package controllers

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"image/png"
	"log"
	"os"
	"strings"

	"github.com/chai2010/webp"
	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/helpers"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
	"gorm.io/gorm"
	// Importa el repositorio
)

func CreateQr(c *fiber.Ctx) error {
	var data services.ContactData
	if err := json.Unmarshal(c.Body(), &data); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Datos inválidos",
		})
	}

	// 1. Generar el QR en formato PNG
	qrBytes, err := services.GenerateVCardQR(data)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo generar el QR",
		})
	}

	// 2. Decodificar PNG
	img, err := png.Decode(bytes.NewReader(qrBytes))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al decodificar QR PNG",
		})
	}

	// 3. Convertir a WebP
	var webpBuf bytes.Buffer
	if err := webp.Encode(&webpBuf, img, nil); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al convertir QR a WebP",
		})
	}

	random := helpers.RandomString(4)
	fileName := fmt.Sprintf("qrs/qr-%s-%s.webp", strings.ReplaceAll(data.FirstName, " ", "_"), random)
	tmpPath := fmt.Sprintf("/tmp/%s.webp", data.FirstName)

	// 4. Guardar archivo temporal
	if err := os.WriteFile(tmpPath, webpBuf.Bytes(), 0644); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo guardar el QR temporalmente",
		})
	}
	defer os.Remove(tmpPath)

	// 5. Subir a OSS
	ossProvider, err := getOSSFromBody(c)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error de conexión con OSS",
		})
	}

	if err := ossProvider.UploadFile(fileName, tmpPath); err != nil {
		log.Printf("Error subiendo archivo a OSS: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error subiendo archivo a OSS",
		})
	}

	// 6. URL del archivo
	url := ossProvider.PublicURL(fileName)

	// 7. Buscar si ya existe un registro con ese vcard_id
	var existing models.QrVcard
	db := app.Http.Database.DB

	err = db.Where("vcard_id = ?", data.VcardID).First(&existing).Error

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// No existe, crear nuevo
			newQr := models.QrVcard{
				Qr:        url,
				UsuarioID: data.FirstName,
				Email:     data.Email,
				VcardID:   data.VcardID,
			}
			if err := models.CreateQrVcard(&newQr); err != nil {
				return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
					"error": "Error al guardar nuevo QR en la base de datos",
				})
			}
		} else {
			// Otro error
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"error": "Error al consultar la base de datos",
			})
		}
	} else {
		// Ya existe, eliminar el QR anterior en OSS
		oldFilePath := strings.Replace(existing.Qr, ossProvider.PublicURL(""), "", 1)

		if err := ossProvider.DeleteFile(oldFilePath); err != nil {
			log.Printf("Error al eliminar QR anterior en OSS: %v", err)
			// No retornamos error, porque queremos continuar de todas formas
		}

		// Actualizar el registro
		existing.Qr = url
		existing.UsuarioID = data.FirstName
		existing.Email = data.Email

		if err := models.UpdateQrVcard(&existing); err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"error": "Error al actualizar el QR en la base de datos",
			})
		}
	}

	// 8. Retornar la URL
	return c.JSON(fiber.Map{
		"url": url,
	})
}

func CreateQrTmp(c *fiber.Ctx) error {
	var data services.ContactData
	if err := json.Unmarshal(c.Body(), &data); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Datos inválidos",
		})
	}

	qrBytes, err := services.GenerateVCardQR(data)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo generar el QR",
		})
	}

	// Decodificar PNG
	img, err := png.Decode(bytes.NewReader(qrBytes))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo procesar la imagen",
		})
	}

	// Codificar a WebP
	var webpBuffer bytes.Buffer
	if err := webp.Encode(&webpBuffer, img, &webp.Options{Lossless: true}); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo convertir a WebP",
		})
	}

	c.Type("webp")
	return c.Send(webpBuffer.Bytes())
}

func CreateUrlQr(c *fiber.Ctx) error {
	var data struct {
		URL string `json:"url"`
		Nombre string `json:"unico"`
	}
	if err := json.Unmarshal(c.Body(), &data); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Datos inválidos",
		})
	}

	qrBytes, err := services.GenerateURlQR(data.URL)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo generar el QR",
		})
	}
	
	img, err := png.Decode(bytes.NewReader(qrBytes))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo procesar la imagen",
		})
	}
	
	// Codificar a WebP
	var webpBuffer bytes.Buffer
	if err := webp.Encode(&webpBuffer, img, &webp.Options{Lossless: true}); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo convertir a WebP",
		})
	}
	
	// 3. Convertir a WebP
	var webpBuf bytes.Buffer
	if err := webp.Encode(&webpBuf, img, nil); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error al convertir QR a WebP",
		})
	}


	fileName := fmt.Sprintf("qrs/url/qr-%s.webp", strings.ReplaceAll(data.Nombre, " ", "_") )	
	tmpPath := fmt.Sprintf("/tmp/%s.webp", data.Nombre)

	// 4. Guardar archivo temporal
	if err := os.WriteFile(tmpPath, webpBuf.Bytes(), 0644); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "No se pudo guardar el QR temporalmente",
		})
	}
	defer os.Remove(tmpPath)

	// 5. Subir a OSS
	ossProvider, err := getOSSFromBody(c)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error de conexión con OSS",
		})
	}

	if err := ossProvider.UploadFile(fileName, tmpPath); err != nil {
		log.Printf("Error subiendo archivo a OSS: %v", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Error subiendo archivo a OSS",
		})
	}

	// 6. URL del archivo
	url := ossProvider.PublicURL(fileName)


	// 8. Retornar la URL
	return c.JSON(fiber.Map{
		"url": url,
	})
}

