package infra

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

// ThumbnailGenerate lee una imagen JPEG o PNG desde srcPath, la redimensiona
// manteniendo aspect ratio para que quepa dentro de (maxWidth, maxHeight) y guarda
// el resultado en dstPath con el formato inferido por la extension de dstPath.
//
// Solo soporta entrada/salida JPEG y PNG (image stdlib). Formatos modernos como
// WebP, AVIF o HEIC no estan soportados — retornan error explicito.
//
// El algoritmo de resize es nearest-neighbor (sin filtro) para mantener cero
// dependencias externas. Para apps que necesiten calidad alta usar una libreria
// como `golang.org/x/image/draw` con BiLinear o CatmullRom.
func ThumbnailGenerate(srcPath string, dstPath string, maxWidth int, maxHeight int) error {
	if maxWidth <= 0 || maxHeight <= 0 {
		return fmt.Errorf("thumbnail_generate: maxWidth y maxHeight deben ser > 0")
	}

	srcF, err := os.Open(srcPath)
	if err != nil {
		return fmt.Errorf("thumbnail_generate: open src %s: %w", srcPath, err)
	}
	defer srcF.Close()

	srcImg, _, err := image.Decode(srcF)
	if err != nil {
		return fmt.Errorf("thumbnail_generate: decode %s: %w", srcPath, err)
	}

	thumb := resizeNearest(srcImg, maxWidth, maxHeight)

	if err := os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
		return fmt.Errorf("thumbnail_generate: mkdir dst: %w", err)
	}

	dstF, err := os.Create(dstPath)
	if err != nil {
		return fmt.Errorf("thumbnail_generate: create dst %s: %w", dstPath, err)
	}
	defer dstF.Close()

	ext := strings.ToLower(filepath.Ext(dstPath))
	switch ext {
	case ".jpg", ".jpeg":
		return jpeg.Encode(dstF, thumb, &jpeg.Options{Quality: 85})
	case ".png":
		return png.Encode(dstF, thumb)
	default:
		return fmt.Errorf("thumbnail_generate: extension %q no soportada (solo jpg/jpeg/png)", ext)
	}
}

// resizeNearest redimensiona la imagen al maximo (maxW, maxH) manteniendo aspect
// ratio. Usa interpolacion nearest-neighbor (rapida pero baja calidad).
func resizeNearest(src image.Image, maxW, maxH int) image.Image {
	srcBounds := src.Bounds()
	srcW := srcBounds.Dx()
	srcH := srcBounds.Dy()

	if srcW == 0 || srcH == 0 {
		return src
	}

	// Calcular escala manteniendo aspect ratio
	scaleW := float64(maxW) / float64(srcW)
	scaleH := float64(maxH) / float64(srcH)
	scale := scaleW
	if scaleH < scaleW {
		scale = scaleH
	}
	if scale >= 1.0 {
		// Imagen ya cabe, no agrandar
		return src
	}

	dstW := int(float64(srcW) * scale)
	dstH := int(float64(srcH) * scale)
	if dstW < 1 {
		dstW = 1
	}
	if dstH < 1 {
		dstH = 1
	}

	dst := image.NewRGBA(image.Rect(0, 0, dstW, dstH))
	for y := 0; y < dstH; y++ {
		srcY := int(float64(y) / scale)
		for x := 0; x < dstW; x++ {
			srcX := int(float64(x) / scale)
			dst.Set(x, y, src.At(srcBounds.Min.X+srcX, srcBounds.Min.Y+srcY))
		}
	}
	return dst
}
