package service

import (
	"context"
	"crypto/md5"
	"fmt"
	"mime/multipart"
	"path/filepath"
	"strings"
	"time"

	"github.com/minio/minio-go/v7"
)

type fileUploadService struct {
	minioClient *minio.Client
	bucketName  string
}

type FileUploadService interface {
	UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error)
	DeleteFile(filePath string) error
	GetFileURL(filePath string) (string, error)
	ValidateFile(file *multipart.FileHeader) error
}

func NewFileUploadService(minioClient *minio.Client, bucketName string) FileUploadService {
	return &fileUploadService{
		minioClient: minioClient,
		bucketName:  bucketName,
	}
}

// UploadFile - Upload file to MinIO
func (f *fileUploadService) UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error) {
	// Validate file
	if err := f.ValidateFile(file); err != nil {
		return "", 0, err
	}

	// Open file
	src, err := file.Open()
	if err != nil {
		return "", 0, err
	}
	defer src.Close()

	// Generate unique filename
	ext := filepath.Ext(file.Filename)
	fileName := strings.TrimSuffix(file.Filename, ext)
	hasher := md5.New()
	hasher.Write([]byte(fmt.Sprintf("%s-%d", fileName, time.Now().UnixNano())))
	uniqueFileName := fmt.Sprintf("%x%s", hasher.Sum(nil), ext)

	// Create file path
	filePath = fmt.Sprintf("%s/%s", folder, uniqueFileName)

	// Upload file to MinIO
	ctx := context.Background()
	_, err = f.minioClient.PutObject(ctx, f.bucketName, filePath, src, file.Size, minio.PutObjectOptions{
		ContentType: file.Header.Get("Content-Type"),
	})
	if err != nil {
		return "", 0, err
	}

	return filePath, file.Size, nil
}

// DeleteFile - Delete file from MinIO
func (f *fileUploadService) DeleteFile(filePath string) error {
	ctx := context.Background()
	return f.minioClient.RemoveObject(ctx, f.bucketName, filePath, minio.RemoveObjectOptions{})
}

// GetFileURL - Get file URL from MinIO
func (f *fileUploadService) GetFileURL(filePath string) (string, error) {
	ctx := context.Background()

	// Generate presigned URL (valid for 7 days)
	url, err := f.minioClient.PresignedGetObject(ctx, f.bucketName, filePath, 7*24*time.Hour, nil)
	if err != nil {
		return "", err
	}

	return url.String(), nil
}

// ValidateFile - Validate uploaded file
func (f *fileUploadService) ValidateFile(file *multipart.FileHeader) error {
	// Check file size (max 50MB)
	const maxFileSize = 50 * 1024 * 1024 // 50MB
	if file.Size > maxFileSize {
		return fmt.Errorf("file size exceeds maximum limit of 50MB")
	}

	// Check file extension
	ext := strings.ToLower(filepath.Ext(file.Filename))
	allowedExts := []string{".pdf", ".doc", ".docx", ".txt", ".mp4", ".avi", ".mov", ".mp3", ".wav", ".jpg", ".jpeg", ".png", ".gif"}

	isAllowed := false
	for _, allowedExt := range allowedExts {
		if ext == allowedExt {
			isAllowed = true
			break
		}
	}

	if !isAllowed {
		return fmt.Errorf("file type not allowed. Allowed types: %s", strings.Join(allowedExts, ", "))
	}

	// Check MIME type
	contentType := file.Header.Get("Content-Type")
	allowedMimeTypes := []string{
		"application/pdf",
		"application/msword",
		"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		"text/plain",
		"video/mp4",
		"video/avi",
		"video/quicktime",
		"audio/mpeg",
		"audio/wav",
		"image/jpeg",
		"image/png",
		"image/gif",
	}

	isValidMimeType := false
	for _, allowedMimeType := range allowedMimeTypes {
		if contentType == allowedMimeType {
			isValidMimeType = true
			break
		}
	}

	if !isValidMimeType {
		return fmt.Errorf("invalid file type. Content-Type: %s", contentType)
	}

	return nil
}
