package handlers

import (
	"bytes"
	"encoding/base64"
	"image"
	"image/color"
	"image/png"
	"net/http"
	"strings"

	// Register decoders so image.Decode works for common formats
	_ "image/gif"
	_ "image/jpeg"

	"github.com/gin-gonic/gin"

	"chat-switchboard/models"
	"chat-switchboard/store"
)

const avatarSize = 128
const maxUploadBytes = 2 * 1024 * 1024 // 2MB raw input limit

type uploadAvatarRequest struct {
	Image string `json:"image" binding:"required"` // base64 data URI or raw base64
}

// ── Upload Avatar ───────────────────────────
// POST /api/v1/profile/avatar
// Accepts { "image": "data:image/png;base64,..." } or { "image": "<raw base64>" }
// Decodes, resizes to 128×128 PNG, stores as data URI.
func (h *SettingsHandler) UploadAvatar(c *gin.Context) {
	userID := getUserID(c)

	var req uploadAvatarRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing image field"})
		return
	}

	// Strip data URI prefix if present
	b64 := req.Image
	if idx := strings.Index(b64, ","); idx >= 0 {
		b64 = b64[idx+1:]
	}

	// Decode base64
	raw, err := base64.StdEncoding.DecodeString(b64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid base64 encoding"})
		return
	}
	if len(raw) > maxUploadBytes {
		c.JSON(http.StatusBadRequest, gin.H{"error": "image too large (max 2MB)"})
		return
	}

	// Decode image (supports PNG, JPEG, GIF via registered decoders)
	src, _, err := image.Decode(bytes.NewReader(raw))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported image format"})
		return
	}

	// Resize to 128×128
	resized := resizeBilinear(src, avatarSize, avatarSize)

	// Encode to PNG
	var buf bytes.Buffer
	if err := png.Encode(&buf, resized); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to encode avatar"})
		return
	}

	// Build data URI
	dataURI := "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())

	// Store in DB
	err = h.stores.Users.Update(c.Request.Context(), userID, map[string]interface{}{"avatar_url": dataURI})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save avatar"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"avatar": dataURI})
}

// ── Delete Avatar ───────────────────────────
// DELETE /api/v1/profile/avatar
func (h *SettingsHandler) DeleteAvatar(c *gin.Context) {
	userID := getUserID(c)

	err := h.stores.Users.Update(c.Request.Context(), userID, map[string]interface{}{"avatar_url": nil})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove avatar"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "avatar removed"})
}

// ── Bilinear Resize ─────────────────────────
// Pure stdlib bilinear interpolation. Quality is fine for 128×128 avatars.
func resizeBilinear(src image.Image, w, h int) *image.RGBA {
	dst := image.NewRGBA(image.Rect(0, 0, w, h))
	sb := src.Bounds()
	sw := float64(sb.Dx())
	sh := float64(sb.Dy())

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			// Map destination pixel to source coordinates
			sx := (float64(x) + 0.5) * sw / float64(w) - 0.5
			sy := (float64(y) + 0.5) * sh / float64(h) - 0.5

			x0 := int(sx)
			y0 := int(sy)
			xf := sx - float64(x0)
			yf := sy - float64(y0)

			// Clamp
			if x0 < sb.Min.X { x0 = sb.Min.X }
			if y0 < sb.Min.Y { y0 = sb.Min.Y }
			x1 := x0 + 1
			y1 := y0 + 1
			if x1 >= sb.Max.X { x1 = sb.Max.X - 1 }
			if y1 >= sb.Max.Y { y1 = sb.Max.Y - 1 }

			// Sample 4 neighbors
			c00 := src.At(x0, y0)
			c10 := src.At(x1, y0)
			c01 := src.At(x0, y1)
			c11 := src.At(x1, y1)

			dst.Set(x, y, bilinearMix(c00, c10, c01, c11, xf, yf))
		}
	}
	return dst
}

func bilinearMix(c00, c10, c01, c11 color.Color, xf, yf float64) color.Color {
	r00, g00, b00, a00 := c00.RGBA()
	r10, g10, b10, a10 := c10.RGBA()
	r01, g01, b01, a01 := c01.RGBA()
	r11, g11, b11, a11 := c11.RGBA()

	mix := func(v00, v10, v01, v11 uint32) uint8 {
		top := float64(v00)*(1-xf) + float64(v10)*xf
		bot := float64(v01)*(1-xf) + float64(v11)*xf
		return uint8((top*(1-yf) + bot*yf) / 256)
	}

	return color.RGBA{
		R: mix(r00, r10, r01, r11),
		G: mix(g00, g10, g01, g11),
		B: mix(b00, b10, b01, b11),
		A: mix(a00, a10, a01, a11),
	}
}

// ── Persona Avatar Upload ────────────────────
// POST /api/v1/personas/:id/avatar (user) or /api/v1/admin/personas/:id/avatar (admin)
// UploadPersonaAvatar uploads and resizes a persona avatar.
// v0.29.0: accepts PersonaStore instead of using database.DB directly.
func UploadPersonaAvatar(personas store.PersonaStore, c *gin.Context) {
	personaID := c.Param("id")

	var req uploadAvatarRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing image field"})
		return
	}

	b64 := req.Image
	if idx := strings.Index(b64, ","); idx >= 0 {
		b64 = b64[idx+1:]
	}

	raw, err := base64.StdEncoding.DecodeString(b64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid base64 encoding"})
		return
	}
	if len(raw) > maxUploadBytes {
		c.JSON(http.StatusBadRequest, gin.H{"error": "image too large (max 2MB)"})
		return
	}

	src, _, err := image.Decode(bytes.NewReader(raw))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported image format"})
		return
	}

	resized := resizeBilinear(src, avatarSize, avatarSize)

	var buf bytes.Buffer
	if err := png.Encode(&buf, resized); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to encode avatar"})
		return
	}

	dataURI := "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())

	err = personas.Update(c.Request.Context(), personaID, models.PersonaPatch{Avatar: &dataURI})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save avatar"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"avatar": dataURI})
}

// DeletePersonaAvatar clears a persona's avatar.
// v0.29.0: accepts PersonaStore instead of using database.DB directly.
func DeletePersonaAvatar(personas store.PersonaStore, c *gin.Context) {
	personaID := c.Param("id")

	empty := ""
	err := personas.Update(c.Request.Context(), personaID, models.PersonaPatch{Avatar: &empty})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove avatar"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "avatar removed"})
}
