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"
)

// ─── Render de vistas ────────────────────────────────────────────────────────

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

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

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

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

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

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

// ─── Dashboard ───────────────────────────────────────────────────────────────

func ContabilidadDashboard(c *fiber.Ctx) error {
	now := time.Now()
	mes, _ := strconv.Atoi(c.Query("mes", strconv.Itoa(int(now.Month()))))
	anio, _ := strconv.Atoi(c.Query("anio", strconv.Itoa(now.Year())))
	if mes < 1 || mes > 12 {
		mes = int(now.Month())
	}
	if anio < 2000 {
		anio = now.Year()
	}
	data, err := models.GetDashboardData(mes, anio)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(data)
}

func ContabilidadConsolidado(ctx *fiber.Ctx) error {
	now := time.Now()
	mes, _ := strconv.Atoi(ctx.Query("mes", strconv.Itoa(int(now.Month()))))
	anio, _ := strconv.Atoi(ctx.Query("anio", strconv.Itoa(now.Year())))
	if mes < 1 || mes > 12 {
		mes = int(now.Month())
	}
	if anio < 2000 {
		anio = now.Year()
	}
	data, err := models.CalcularYGuardarConsolidado(mes, anio)
	if err != nil {
		return ctx.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return ctx.JSON(data)
}

func ContabilidadListConsolidados(c *fiber.Ctx) error {
	anio, _ := strconv.Atoi(c.Query("anio", "0"))
	items, err := models.ListConsolidados(anio)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(items)
}

// =============================================================================
// ─── CRUD: Cuentas ──────────────────────────────────────────────────────────
// =============================================================================

func GetCuentas(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	search := c.Query("search", "")
	if page < 1 {
		page = 1
	}
	limit := 50
	offset := (page - 1) * limit
	items, total, err := models.GetAllCuentas(limit, offset, search)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

func GetCuentasSelect(c *fiber.Ctx) error {
	items, err := models.GetAllCuentasSelect()
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(items)
}

func CreateCuenta(c *fiber.Ctx) error {
	var req models.Cuenta
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if req.Nombre == "" {
		return c.Status(400).JSON(fiber.Map{"error": "nombre es requerido"})
	}
	if req.Tipo == "" {
		req.Tipo = "egreso"
	}
	req.Activo = true
	if err := models.CreateCuenta(&req); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(req)
}

func UpdateCuenta(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"})
	}
	var req models.Cuenta
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	req.ID = uint(id)
	if err := models.UpdateCuenta(&req); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteCuenta(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.DeleteCuenta(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// =============================================================================
// ─── CRUD: Entidades ────────────────────────────────────────────────────────
// =============================================================================

func GetEntidades(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	search := c.Query("search", "")
	if page < 1 {
		page = 1
	}
	limit := 20
	offset := (page - 1) * limit
	items, total, err := models.GetAllEntidades(limit, offset, search)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

func GetEntidadesSelect(c *fiber.Ctx) error {
	items, err := models.GetAllEntidadesSelect()
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(items)
}

func CreateEntidad(c *fiber.Ctx) error {
	var req models.Entidad
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if req.Nombre == "" {
		return c.Status(400).JSON(fiber.Map{"error": "nombre es requerido"})
	}
	req.Activo = true
	if err := models.CreateEntidad(&req); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(req)
}

func UpdateEntidad(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"})
	}
	var req models.Entidad
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	req.ID = uint(id)
	if err := models.UpdateEntidad(&req); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteEntidad(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.DeleteEntidad(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// =============================================================================
// ─── CRUD: Transacciones ────────────────────────────────────────────────────
// =============================================================================

func GetTransacciones(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	search := c.Query("search", "")
	filtroTipo := c.Query("tipo", "")
	mes, _ := strconv.Atoi(c.Query("mes", "0"))
	anio, _ := strconv.Atoi(c.Query("anio", "0"))
	if page < 1 {
		page = 1
	}
	limit := 30
	offset := (page - 1) * limit
	items, total, err := models.GetAllTransacciones(limit, offset, search, filtroTipo, mes, anio)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

func CreateTransaccion(c *fiber.Ctx) error {
	type Req struct {
		Fecha       string  `json:"fecha"`
		Tipo        string  `json:"tipo"`
		Descripcion string  `json:"descripcion"`
		Valor       float64 `json:"valor"`
		CuentaID    *uint   `json:"cuenta_id"`
		EntidadID   *uint   `json:"entidad_id"`
		FormaPago   string  `json:"forma_pago"`
		Estado      string  `json:"estado"`
		Notas       string  `json:"notas"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if req.Descripcion == "" {
		return c.Status(400).JSON(fiber.Map{"error": "descripcion es requerida"})
	}
	if req.Valor <= 0 {
		return c.Status(400).JSON(fiber.Map{"error": "valor debe ser mayor a 0"})
	}
	t := &models.Transaccion{
		Tipo:        req.Tipo,
		Descripcion: req.Descripcion,
		Valor:       req.Valor,
		CuentaID:    req.CuentaID,
		EntidadID:   req.EntidadID,
		FormaPago:   req.FormaPago,
		Estado:      req.Estado,
		Notas:       req.Notas,
		Fecha:       time.Now(),
	}
	if t.Tipo == "" {
		t.Tipo = "ingreso"
	}
	if t.Estado == "" {
		t.Estado = "registrada"
	}
	if req.Fecha != "" {
		if parsed, err := time.Parse("2006-01-02", req.Fecha); err == nil {
			t.Fecha = parsed
		}
	}
	if err := models.CreateTransaccion(t); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(t)
}

func UpdateTransaccion(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 Req struct {
		Fecha       string  `json:"fecha"`
		Tipo        string  `json:"tipo"`
		Descripcion string  `json:"descripcion"`
		Valor       float64 `json:"valor"`
		CuentaID    *uint   `json:"cuenta_id"`
		EntidadID   *uint   `json:"entidad_id"`
		FormaPago   string  `json:"forma_pago"`
		Estado      string  `json:"estado"`
		Notas       string  `json:"notas"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	t := &models.Transaccion{
		Tipo:        req.Tipo,
		Descripcion: req.Descripcion,
		Valor:       req.Valor,
		CuentaID:    req.CuentaID,
		EntidadID:   req.EntidadID,
		FormaPago:   req.FormaPago,
		Estado:      req.Estado,
		Notas:       req.Notas,
		Fecha:       time.Now(),
	}
	t.ID = uint(id)
	if req.Fecha != "" {
		if parsed, err := time.Parse("2006-01-02", req.Fecha); err == nil {
			t.Fecha = parsed
		}
	}

	if err := models.UpdateTransaccion(t); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteTransaccion(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.DeleteTransaccion(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// =============================================================================
// ─── CRUD: Cuentas por Cobrar ──────────────────────────────────────────────
// =============================================================================

func GetCuentasCobro(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	search := c.Query("search", "")
	estado := c.Query("estado", "")
	if page < 1 {
		page = 1
	}
	limit := 20
	offset := (page - 1) * limit
	items, total, err := models.GetAllCuentasCobro(limit, offset, search, estado)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

func CreateCuentaCobro(c *fiber.Ctx) error {
	type Req struct {
		ClienteID        uint    `json:"cliente_id"`
		Fecha            string  `json:"fecha"`
		Descripcion      string  `json:"descripcion"`
		Valor            float64 `json:"valor"`
		FechaVencimiento string  `json:"fecha_vencimiento"`
		Notas            string  `json:"notas"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if req.ClienteID == 0 {
		return c.Status(400).JSON(fiber.Map{"error": "cliente_id es requerido"})
	}
	cc := &models.CuentaCobro{
		ClienteID:   req.ClienteID,
		Descripcion: req.Descripcion,
		Valor:       req.Valor,
		Estado:      "pendiente",
		Notas:       req.Notas,
		Fecha:       time.Now(),
	}
	if req.Fecha != "" {
		if parsed, err := time.Parse("2006-01-02", req.Fecha); err == nil {
			cc.Fecha = parsed
		}
	}
	if req.FechaVencimiento != "" {
		if parsed, err := time.Parse("2006-01-02", req.FechaVencimiento); err == nil {
			cc.FechaVencimiento = &parsed
		}
	}
	if err := models.CreateCuentaCobro(cc); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(cc)
}

func UpdateCuentaCobro(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 Req struct {
		Estado           string  `json:"estado"`
		FechaPago        string  `json:"fecha_pago"`
		TransaccionID    *uint   `json:"transaccion_id"`
		Notas            string  `json:"notas"`
		FechaVencimiento string  `json:"fecha_vencimiento"`
		Descripcion      string  `json:"descripcion"`
		Valor            float64 `json:"valor"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	cc := &models.CuentaCobro{}
	cc.ID = uint(id)
	if req.Estado != "" {
		cc.Estado = req.Estado
	}
	if req.FechaPago != "" {
		if parsed, err := time.Parse("2006-01-02", req.FechaPago); err == nil {
			cc.FechaPago = &parsed
		}
	}
	cc.TransaccionID = req.TransaccionID
	cc.Notas = req.Notas
	if err := models.UpdateCuentaCobro(cc); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteCuentaCobro(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.DeleteCuentaCobro(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// =============================================================================
// ─── CRUD: Cuentas por Pagar ────────────────────────────────────────────────
// =============================================================================

func GetCuentasPagar(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	search := c.Query("search", "")
	estado := c.Query("estado", "")
	if page < 1 {
		page = 1
	}
	limit := 20
	offset := (page - 1) * limit
	items, total, err := models.GetAllCuentasPagar(limit, offset, search, estado)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

func CreateCuentaPagar(c *fiber.Ctx) error {
	type Req struct {
		EntidadID   uint    `json:"entidad_id"`
		Fecha       string  `json:"fecha"`
		Descripcion string  `json:"descripcion"`
		Valor       float64 `json:"valor"`
		Vencimiento string  `json:"vencimiento"`
		Notas       string  `json:"notas"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if req.EntidadID == 0 {
		return c.Status(400).JSON(fiber.Map{"error": "entidad_id es requerido"})
	}
	cp := &models.CuentaPagar{
		EntidadID:   req.EntidadID,
		Descripcion: req.Descripcion,
		Valor:       req.Valor,
		Estado:      "pendiente",
		Notas:       req.Notas,
		Fecha:       time.Now(),
	}
	if req.Fecha != "" {
		if parsed, err := time.Parse("2006-01-02", req.Fecha); err == nil {
			cp.Fecha = parsed
		}
	}
	if req.Vencimiento != "" {
		if parsed, err := time.Parse("2006-01-02", req.Vencimiento); err == nil {
			cp.Vencimiento = &parsed
		}
	}
	if err := models.CreateCuentaPagar(cp); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(cp)
}

func UpdateCuentaPagar(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 Req struct {
		Estado        string `json:"estado"`
		FechaPago     string `json:"fecha_pago"`
		TransaccionID *uint  `json:"transaccion_id"`
		Notas         string `json:"notas"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	cp := &models.CuentaPagar{}
	cp.ID = uint(id)
	if req.Estado != "" {
		cp.Estado = req.Estado
	}
	if req.FechaPago != "" {
		if parsed, err := time.Parse("2006-01-02", req.FechaPago); err == nil {
			cp.FechaPago = &parsed
		}
	}
	cp.TransaccionID = req.TransaccionID
	cp.Notas = req.Notas
	if err := models.UpdateCuentaPagar(cp); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func MarcarCuentaPagarPagada(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"})
	}
	var req struct {
		FechaPago string `json:"fecha_pago"`
	}
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	fecha := time.Now()
	if req.FechaPago != "" {
		if parsed, err := time.Parse("2006-01-02", req.FechaPago); err == nil {
			fecha = parsed
		}
	}
	if err := models.MarcarCuentaPagarPagada(uint(id), fecha); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// GenerarDocumentoCuentaCobro produce el PDF de solicitud de cuenta de cobro a
// partir de la plantilla activa tipo 'cuenta_cobro'.
// POST /api/v2/contabilidad/cuentas-cobro/:id/generar-documento
func GenerarDocumentoCuentaCobro(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"})
	}
	doc, err := services.GenerarDocumentoCuentaCobro(uint(id), generadoPorFromContext(c))
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(fiber.Map{"ok": true, "documento": doc})
}

func DeleteCuentaPagar(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.DeleteCuentaPagar(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}
