package controllers

import (
	"math"
	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)

func ContratosView(c *fiber.Ctx) error {
	return c.Render("renovaciones/contratos", fiber.Map{
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}, "layouts/main")
}

func GetContratos(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	limit, _ := strconv.Atoi(c.Query("limit", "10"))
	if page < 1 {
		page = 1
	}
	offset := (page - 1) * limit
	records, total, err := models.GetAllContratos(limit, offset, c.Query("search"), c.Query("estado"))
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	// Enriquecer con días restantes
	type ContratoDTO struct {
		models.Contrato
		DiasRestantes  int     `json:"dias_restantes"`
		Urgencia       string  `json:"urgencia"` // verde | amarillo | rojo
		PrecioSugerido float64 `json:"precio_sugerido"`
	}
	dtos := make([]ContratoDTO, len(records))
	now := time.Now()
	for i, r := range records {
		dias := int(r.FechaVencimiento.Sub(now).Hours() / 24)
		urgencia := "verde"
		if dias <= 0 {
			urgencia = "rojo"
		} else if dias <= 30 {
			urgencia = "amarillo"
		}
		var sugerido float64
		for _, s := range r.Servicios {
			sugerido += s.Precio
		}
		dtos[i] = ContratoDTO{Contrato: r, DiasRestantes: dias, Urgencia: urgencia, PrecioSugerido: sugerido}
	}
	return c.JSON(fiber.Map{
		"registros":  dtos,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
		"limit":      limit,
	})
}

func CreateContrato(c *fiber.Ctx) error {
	type Input struct {
		ClienteID        uint    `json:"cliente_id"`
		ServicioIDs      []uint  `json:"servicio_ids"`
		FechaInicio      string  `json:"fecha_inicio"`
		FechaVencimiento string  `json:"fecha_vencimiento"`
		PrecioAcordado   float64 `json:"precio_acordado"`
		AutoRenovar      bool    `json:"auto_renovar"`
		Notas            string  `json:"notas"`
	}
	var inp Input
	if err := c.BodyParser(&inp); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	fi, err := time.Parse("2006-01-02", inp.FechaInicio)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Fecha inicio inválida"})
	}
	fv, err := time.Parse("2006-01-02", inp.FechaVencimiento)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Fecha vencimiento inválida"})
	}
	m := models.Contrato{
		ClienteID:        inp.ClienteID,
		FechaInicio:      fi,
		FechaVencimiento: fv,
		PrecioAcordado:   inp.PrecioAcordado,
		AutoRenovar:      inp.AutoRenovar,
		Notas:            inp.Notas,
		Estado:           "activo",
	}
	if err := models.CreateContrato(m, inp.ServicioIDs); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(fiber.Map{"message": "Contrato creado", "ok": true})
}

func UpdateContrato(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
	}
	type Input struct {
		ServicioIDs      []uint  `json:"servicio_ids"`
		Estado           string  `json:"estado"`
		FechaVencimiento string  `json:"fecha_vencimiento"`
		PrecioAcordado   float64 `json:"precio_acordado"`
		AutoRenovar      bool    `json:"auto_renovar"`
		Notas            string  `json:"notas"`
	}
	var inp Input
	if err := c.BodyParser(&inp); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	existing, err := models.GetContratoByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
	}
	if inp.FechaVencimiento != "" {
		fv, err := time.Parse("2006-01-02", inp.FechaVencimiento)
		if err == nil {
			existing.FechaVencimiento = fv
		}
	}
	if inp.Estado != "" {
		existing.Estado = inp.Estado
	}
	existing.PrecioAcordado = inp.PrecioAcordado
	existing.AutoRenovar = inp.AutoRenovar
	existing.Notas = inp.Notas
	if err := models.UpdateContrato(*existing, inp.ServicioIDs); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Actualizado", "ok": true})
}

func RenovarContrato(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
	}
	existing, err := models.GetContratoByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
	}
	// Usar periodicidad del primer servicio asociado
	periodicidad := ""
	if len(existing.Servicios) > 0 {
		periodicidad = existing.Servicios[0].Periodicidad
	}
	nuevaInicio := existing.FechaVencimiento.AddDate(0, 0, 1)
	nuevaVenc := calcularFechaVencimiento(nuevaInicio, periodicidad)

	// Copiar IDs de servicios del contrato anterior
	var servicioIDs []uint
	for _, s := range existing.Servicios {
		servicioIDs = append(servicioIDs, s.ID)
	}
	nuevo := models.Contrato{
		ClienteID:        existing.ClienteID,
		FechaInicio:      nuevaInicio,
		FechaVencimiento: nuevaVenc,
		PrecioAcordado:   existing.PrecioAcordado,
		AutoRenovar:      existing.AutoRenovar,
		Estado:           "activo",
		Notas:            "Renovación automática desde contrato #" + strconv.Itoa(int(existing.ID)),
	}
	if err := models.CreateContrato(nuevo, servicioIDs); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	// Marcar anterior como renovado
	existing.Estado = "renovado"
	models.UpdateContrato(*existing, nil)

	return c.JSON(fiber.Map{"message": "Renovado", "ok": true})
}

func EnviarCorreoContrato(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
	}
	contrato, err := models.GetContratoByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
	}
	if err := services.EnviarCorreoManual(contrato); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Correo enviado", "ok": true})
}

func DeleteContrato(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
	}
	if err := models.DeleteContrato(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"message": "Eliminado", "ok": true})
}

func calcularFechaVencimiento(desde time.Time, periodicidad string) time.Time {
	switch periodicidad {
	case "mensual":
		return desde.AddDate(0, 1, 0)
	case "trimestral":
		return desde.AddDate(0, 3, 0)
	case "semestral":
		return desde.AddDate(0, 6, 0)
	case "anual":
		return desde.AddDate(1, 0, 0)
	default:
		return desde.AddDate(1, 0, 0)
	}
}
