package services

import (
	"bytes"
	"fmt"
	"github.com/google/uuid"
	"image"
	"image/jpeg"
	"mime/multipart"
	"os"
	"path/filepath"

	"github.com/disintegration/imaging"
)

const (
	MaxAvatarSize = 5 * 1024 * 1024 // 5MB
	AvatarWidth   = 200
	AvatarHeight  = 200
	JPEGQuality   = 90
)

// ImageService handles image processing operations
type ImageService struct {
	uploadDir string
}

// NewImageService creates a new ImageService instance
func NewImageService(uploadDir string) *ImageService {
	if uploadDir == "" {
		uploadDir = "uploads/avatars"
	}
	return &ImageService{
		uploadDir: uploadDir,
	}
}

// ValidateImage validates the image file format and size
// T0223: Validates format (JPEG, PNG, WebP) and size (max 5MB)
func (s *ImageService) ValidateImage(fileHeader *multipart.FileHeader) error {
	// Validate file size
	if fileHeader.Size > MaxAvatarSize {
		return fmt.Errorf("file size exceeds 5MB limit")
	}

	// Validate MIME type
	contentType := fileHeader.Header.Get("Content-Type")
	allowedTypes := []string{"image/jpeg", "image/png", "image/webp"}
	valid := false
	for _, allowedType := range allowedTypes {
		if contentType == allowedType {
			valid = true
			break
		}
	}
	if !valid {
		return fmt.Errorf("unsupported image format. Allowed: JPEG, PNG, WebP")
	}

	return nil
}

// ResizeImage resizes an image to the specified dimensions with crop center
// T0223: Maintains aspect ratio and crops center to fit target dimensions
func (s *ImageService) ResizeImage(img image.Image, width, height int) image.Image {
	// Calculate dimensions for crop center
	bounds := img.Bounds()
	imgWidth := bounds.Dx()
	imgHeight := bounds.Dy()

	// Calculate ratio to maintain aspect ratio
	ratio := float64(imgWidth) / float64(imgHeight)
	targetRatio := float64(width) / float64(height)

	var cropWidth, cropHeight int
	if ratio > targetRatio {
		// Image is wider, crop width
		cropHeight = imgHeight
		cropWidth = int(float64(cropHeight) * targetRatio)
	} else {
		// Image is taller, crop height
		cropWidth = imgWidth
		cropHeight = int(float64(cropWidth) / targetRatio)
	}

	// Crop center
	cropX := (imgWidth - cropWidth) / 2
	cropY := (imgHeight - cropHeight) / 2
	cropped := imaging.Crop(img, image.Rect(cropX, cropY, cropX+cropWidth, cropY+cropHeight))

	// Final resize
	return imaging.Resize(cropped, width, height, imaging.Lanczos)
}

// EncodeJPEG encodes an image as JPEG with the specified quality
// T0223: Encodes image as JPEG with quality 90
func (s *ImageService) EncodeJPEG(img image.Image) ([]byte, error) {
	var buf bytes.Buffer
	if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: JPEGQuality}); err != nil {
		return nil, fmt.Errorf("failed to encode image: %w", err)
	}
	return buf.Bytes(), nil
}

// ProcessAvatar validates and processes an avatar image
// T0221: Validates format (JPEG, PNG, WebP), size (max 5MB), and resizes to 200x200px
// T0223: Refactored to use ValidateImage, ResizeImage, and EncodeJPEG methods
func (s *ImageService) ProcessAvatar(fileHeader *multipart.FileHeader) ([]byte, error) {
	// Validate file
	if err := s.ValidateImage(fileHeader); err != nil {
		return nil, err
	}

	// Open file
	file, err := fileHeader.Open()
	if err != nil {
		return nil, fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// Decode image
	img, format, err := image.Decode(file)
	if err != nil {
		return nil, fmt.Errorf("invalid image format: %w", err)
	}

	// Validate decoded format
	if format != "jpeg" && format != "png" && format != "webp" {
		return nil, fmt.Errorf("unsupported image format: %s", format)
	}

	// Resize with crop center
	resized := s.ResizeImage(img, AvatarWidth, AvatarHeight)

	// Encode as JPEG
	return s.EncodeJPEG(resized)
}

// UploadToS3 uploads image data to S3 (or local storage for now)
// T0221: For now, stores locally. S3 implementation will be added in T0224
func (s *ImageService) UploadToS3(data []byte, key string) (string, error) {
	// Create upload directory if it doesn't exist
	if err := os.MkdirAll(s.uploadDir, 0755); err != nil {
		return "", fmt.Errorf("failed to create upload directory: %w", err)
	}

	// Save file locally (S3 will be implemented in T0224)
	filePath := filepath.Join(s.uploadDir, filepath.Base(key))
	if err := os.WriteFile(filePath, data, 0644); err != nil {
		return "", fmt.Errorf("failed to save file: %w", err)
	}

	// Return local URL (will be S3 URL in T0224)
	avatarURL := fmt.Sprintf("/uploads/avatars/%s", filepath.Base(key))
	return avatarURL, nil
}

// DeleteFromS3 deletes an image from S3 (or local storage for now)
func (s *ImageService) DeleteFromS3(avatarURL string) error {
	// Extract filename from URL
	filename := filepath.Base(avatarURL)
	filePath := filepath.Join(s.uploadDir, filename)

	// Delete file (S3 implementation will be added in T0224)
	if err := os.Remove(filePath); err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("failed to delete file: %w", err)
		}
	}

	return nil
}

// GenerateS3Key generates an S3 key for avatar storage
func (s *ImageService) GenerateS3Key(userID uuid.UUID) string {
	timestamp := uuid.New()
	return fmt.Sprintf("avatars/%d/%d.jpg", userID, timestamp)
}
