package services

import (
	"bytes"
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// Note: To use this image optimizer, install the required package:
// go get golang.org/x/image/draw
//
// For now, we'll use basic resizing without the external library
// Uncomment the import above and the advanced resize function when ready

// ImageSize defines thumbnail dimensions
type ImageSize struct {
	Width  int
	Height int
	Name   string
}

var (
	// StandardSizes for responsive images
	StandardSizes = []ImageSize{
		{Width: 150, Height: 150, Name: "thumb"},
		{Width: 400, Height: 400, Name: "small"},
		{Width: 800, Height: 800, Name: "medium"},
		{Width: 1200, Height: 1200, Name: "large"},
	}
)

// OptimizedImage holds paths to all generated sizes
type OptimizedImage struct {
	Original string
	Thumb    string
	Small    string
	Medium   string
	Large    string
}

// OptimizeAndResize processes an uploaded image
func OptimizeAndResize(sourcePath string) (*OptimizedImage, error) {
	// Open source image
	file, err := os.Open(sourcePath)
	if err != nil {
		return nil, fmt.Errorf("failed to open image: %w", err)
	}
	defer file.Close()

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

	result := &OptimizedImage{
		Original: sourcePath,
	}

	// Generate thumbnails
	dir := filepath.Dir(sourcePath)
	base := strings.TrimSuffix(filepath.Base(sourcePath), filepath.Ext(sourcePath))

	for _, size := range StandardSizes {
		resized := resizeImage(img, size.Width, size.Height)
		outputPath := filepath.Join(dir, fmt.Sprintf("%s_%s.jpg", base, size.Name))

		if err := saveJPEG(resized, outputPath, 85); err != nil {
			continue
		}

		// Store path in result
		switch size.Name {
		case "thumb":
			result.Thumb = outputPath
		case "small":
			result.Small = outputPath
		case "medium":
			result.Medium = outputPath
		case "large":
			result.Large = outputPath
		}
	}

	// Optimize original if it's too large
	if format == "jpeg" || format == "jpg" {
		optimizeJPEG(sourcePath)
	} else if format == "png" {
		convertPNGToJPEG(sourcePath)
	}

	return result, nil
}

// resizeImage resizes image maintaining aspect ratio
// Using simple nearest-neighbor scaling (for production, consider golang.org/x/image/draw)
func resizeImage(src image.Image, maxWidth, maxHeight int) image.Image {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()

	// Calculate new dimensions maintaining aspect ratio
	ratio := float64(srcW) / float64(srcH)
	var newW, newH int

	if srcW > srcH {
		newW = maxWidth
		newH = int(float64(maxWidth) / ratio)
	} else {
		newH = maxHeight
		newW = int(float64(maxHeight) * ratio)
	}

	// Don't upscale
	if newW > srcW || newH > srcH {
		return src
	}

	// Simple nearest-neighbor resize
	// For production quality, use: golang.org/x/image/draw with CatmullRom
	dst := image.NewRGBA(image.Rect(0, 0, newW, newH))
	xRatio := float64(srcW) / float64(newW)
	yRatio := float64(srcH) / float64(newH)

	for y := 0; y < newH; y++ {
		for x := 0; x < newW; x++ {
			srcX := int(float64(x) * xRatio)
			srcY := int(float64(y) * yRatio)
			dst.Set(x, y, src.At(srcX, srcY))
		}
	}

	return dst
}

// saveJPEG saves image as JPEG with specified quality
func saveJPEG(img image.Image, path string, quality int) error {
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()

	return jpeg.Encode(out, img, &jpeg.Options{Quality: quality})
}

// optimizeJPEG re-encodes JPEG with optimal quality
func optimizeJPEG(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	img, err := jpeg.Decode(file)
	if err != nil {
		return err
	}

	// Create temporary file
	tmpPath := path + ".tmp"
	out, err := os.Create(tmpPath)
	if err != nil {
		return err
	}

	// Encode with 85% quality
	err = jpeg.Encode(out, img, &jpeg.Options{Quality: 85})
	out.Close()

	if err != nil {
		os.Remove(tmpPath)
		return err
	}

	// Check if new file is smaller
	origInfo, _ := os.Stat(path)
	newInfo, _ := os.Stat(tmpPath)

	if newInfo.Size() < origInfo.Size() {
		// Replace original
		os.Remove(path)
		os.Rename(tmpPath, path)
	} else {
		// Keep original
		os.Remove(tmpPath)
	}

	return nil
}

// convertPNGToJPEG converts PNG to JPEG for better compression
func convertPNGToJPEG(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	img, err := png.Decode(file)
	if err != nil {
		return err
	}

	// Create JPEG version
	jpegPath := strings.TrimSuffix(path, ".png") + ".jpg"
	out, err := os.Create(jpegPath)
	if err != nil {
		return err
	}
	defer out.Close()

	return jpeg.Encode(out, img, &jpeg.Options{Quality: 90})
}

// GetImageDimensions returns width and height of an image
func GetImageDimensions(path string) (int, int, error) {
	file, err := os.Open(path)
	if err != nil {
		return 0, 0, err
	}
	defer file.Close()

	config, _, err := image.DecodeConfig(file)
	if err != nil {
		return 0, 0, err
	}

	return config.Width, config.Height, nil
}

// ValidateImageFile checks if file is a valid image
func ValidateImageFile(reader io.Reader) (string, error) {
	// Read first 512 bytes for MIME detection
	buf := make([]byte, 512)
	n, err := io.ReadFull(reader, buf)
	if err != nil && err != io.ErrUnexpectedEOF {
		return "", err
	}

	// Try to decode as image
	_, format, err := image.Decode(bytes.NewReader(buf[:n]))
	if err != nil {
		return "", fmt.Errorf("not a valid image: %w", err)
	}

	// Validate format
	validFormats := map[string]bool{
		"jpeg": true,
		"jpg":  true,
		"png":  true,
		"gif":  true,
		"webp": true,
	}

	if !validFormats[format] {
		return "", fmt.Errorf("unsupported image format: %s", format)
	}

	return format, nil
}
