package controllers

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/rest/middlewares"
)

// ─── Auth ─────────────────────────────────────────────────────────────────────

func PortalLoginPage(c *fiber.Ctx) error {
	if auth.IsPortalLoggedIn(c) {
		return c.Redirect("/portal/dashboard")
	}
	return c.Render("portal/login", fiber.Map{
		"error": c.Query("error"),
	}, "layouts/portal_public")
}

func PortalLoginPost(c *fiber.Ctx) error {
	email := strings.TrimSpace(c.FormValue("email"))
	password := c.FormValue("password")
	u, err := models.CheckPortalLogin(email, password)
	if err != nil {
		return c.Redirect("/portal/login?error=" + url.QueryEscape(err.Error()))
	}
	if err := auth.SetPortalSession(c, u.ID); err != nil {
		return c.Redirect("/portal/login?error=Error+interno")
	}
	return c.Redirect("/portal/dashboard")
}

func PortalLogout(c *fiber.Ctx) error {
	_ = auth.DestroyPortalSession(c)
	return c.Redirect("/portal/login")
}

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

func PortalDashboard(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Redirect("/portal/login")
	}

	// Recargar con accesos
	fullUser, err := models.GetPortalUserByID(u.ID)
	if err != nil {
		return c.Redirect("/portal/login")
	}

	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	proyectos, _ := models.GetProyectosByClienteIDs(clienteIDs)

	// Si partner: agrupar proyectos por cliente
	type ClienteProyectos struct {
		Cliente   models.Cliente
		Proyectos []models.Proyecto
	}
	var grupos []ClienteProyectos
	clienteMap := map[uint]*ClienteProyectos{}
	for _, p := range proyectos {
		if _, ok := clienteMap[p.ClienteID]; !ok {
			clienteMap[p.ClienteID] = &ClienteProyectos{Cliente: p.Cliente}
		}
		clienteMap[p.ClienteID].Proyectos = append(clienteMap[p.ClienteID].Proyectos, p)
	}
	for _, g := range clienteMap {
		grupos = append(grupos, *g)
	}

	return c.Render("portal/dashboard", fiber.Map{
		"portalUser": fullUser,
		"proyectos":  proyectos,
		"grupos":     grupos,
		"isPartner":  fullUser.Rol == "partner" || (fullUser.Role != nil && fullUser.Role.EsPortalPartner),
	}, "layouts/portal")
}

// ─── Detalle de proyecto ──────────────────────────────────────────────────────

func PortalProyecto(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Redirect("/portal/login")
	}

	slug := c.Params("slug")
	proy, err := models.GetProyectoBySlug(slug)
	if err != nil {
		return c.Status(404).Render("portal/404", fiber.Map{}, "layouts/portal")
	}

	// Verificar acceso
	fullUser, _ := models.GetPortalUserByID(u.ID)
	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	hasAccess := false
	for _, cid := range clienteIDs {
		if cid == proy.ClienteID {
			hasAccess = true
			break
		}
	}
	if !hasAccess {
		return c.Status(403).Redirect("/portal/dashboard")
	}

	fases, _ := models.GetFasesByProyecto(proy.ID)
	avances, _ := models.GetAvancesByProyecto(proy.ID, true)
	entregables, _ := models.GetEntregablesByProyecto(proy.ID, true)
	tickets, _ := models.GetTicketsByPortalUser(u.ID)

	return c.Render("portal/proyecto", fiber.Map{
		"portalUser":  fullUser,
		"proyecto":    proy,
		"fases":       fases,
		"avances":     avances,
		"entregables": entregables,
		"tickets":     tickets,
	}, "layouts/portal")
}

// ─── API del portal ───────────────────────────────────────────────────────────

// PortalGetProyectos devuelve los proyectos del usuario logueado (JSON para Alpine).
func PortalGetProyectos(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	fullUser, _ := models.GetPortalUserByID(u.ID)
	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	proyectos, _ := models.GetProyectosByClienteIDs(clienteIDs)
	return c.JSON(proyectos)
}

// PortalGetProyectoData devuelve todo el contenido de un proyecto (JSON).
func PortalGetProyectoData(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	slug := c.Params("slug")
	proy, err := models.GetProyectoBySlug(slug)
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "no encontrado"})
	}
	// Verificar acceso
	fullUser, _ := models.GetPortalUserByID(u.ID)
	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	hasAccess := false
	for _, cid := range clienteIDs {
		if cid == proy.ClienteID {
			hasAccess = true
			break
		}
	}
	if !hasAccess {
		return c.Status(403).JSON(fiber.Map{"error": "sin acceso"})
	}

	fases, _ := models.GetFasesByProyecto(proy.ID)
	avances, _ := models.GetAvancesByProyecto(proy.ID, true)
	entregables, _ := models.GetEntregablesByProyecto(proy.ID, true)
	tickets, _ := models.GetTicketsByPortalUser(u.ID)
	facturas, _ := models.GetFacturasByCliente(proy.ClienteID, true)

	return c.JSON(fiber.Map{
		"proyecto":    proy,
		"fases":       fases,
		"avances":     avances,
		"entregables": entregables,
		"tickets":     tickets,
		"facturas":    facturas,
	})
}

// PortalCrearTicket crea un nuevo ticket de soporte.
func PortalCrearTicket(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	type Req struct {
		ProyectoSlug string `json:"proyecto_slug"`
		Titulo       string `json:"titulo"`
		Descripcion  string `json:"descripcion"`
		Prioridad    string `json:"prioridad"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if strings.TrimSpace(req.Titulo) == "" {
		return c.Status(400).JSON(fiber.Map{"error": "El título es requerido"})
	}

	proy, err := models.GetProyectoBySlug(req.ProyectoSlug)
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Proyecto no encontrado"})
	}

	t := &models.ProyectoTicket{
		ProyectoID:   proy.ID,
		PortalUserID: u.ID,
		AutorNombre:  u.Nombre,
		Titulo:       req.Titulo,
		Descripcion:  req.Descripcion,
		Prioridad:    req.Prioridad,
		Estado:       "abierto",
	}
	if t.Prioridad == "" {
		t.Prioridad = "media"
	}
	if err := models.CreateProyectoTicket(t); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(t)
}

// PortalResponderTicket agrega un mensaje a un ticket.
func PortalResponderTicket(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	ticketID, _ := c.ParamsInt("id")
	type Req struct{ Contenido string `json:"contenido"` }
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if strings.TrimSpace(req.Contenido) == "" {
		return c.Status(400).JSON(fiber.Map{"error": "El mensaje no puede estar vacío"})
	}
	ticket, err := models.GetTicketByID(uint(ticketID))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Ticket no encontrado"})
	}
	if ticket.PortalUserID != u.ID {
		return c.Status(403).JSON(fiber.Map{"error": "Sin acceso"})
	}
	msg := &models.TicketMensaje{
		TicketID:    uint(ticketID),
		Contenido:   req.Contenido,
		EsAdmin:     false,
		AutorNombre: u.Nombre,
	}
	if err := models.CreateTicketMensaje(msg); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(msg)
}

// PortalDownloadEntregable descarga un entregable del portal.
func PortalDownloadEntregable(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	entID, _ := c.ParamsInt("id")
	item, err := models.GetProyectoEntregableByID(uint(entID))
	if err != nil || !item.Visible {
		return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
	}
	// Verificar que el proyecto pertenece al cliente del portal user
	proy, err := models.GetProyectoByID(item.ProyectoID)
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Proyecto no encontrado"})
	}
	fullUser, _ := models.GetPortalUserByID(u.ID)
	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	hasAccess := false
	for _, cid := range clienteIDs {
		if cid == proy.ClienteID {
			hasAccess = true
			break
		}
	}
	if !hasAccess {
		return c.Status(403).JSON(fiber.Map{"error": "Sin acceso"})
	}
	clean := item.Archivo
	if !strings.HasPrefix(clean, "uploads/") {
		return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
	}
	c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.OriginalName))
	return c.SendFile(clean)
}

// PortalDownloadFactura descarga el PDF de una factura del portal.
func PortalDownloadFactura(c *fiber.Ctx) error {
	u := middlewares.PortalUserFromLocals(c)
	if u == nil {
		return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
	}
	factID, _ := c.ParamsInt("id")
	f, err := models.GetFacturaByID(uint(factID))
	if err != nil || !f.Visible || f.Archivo == "" {
		return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
	}
	// Verificar acceso
	fullUser, _ := models.GetPortalUserByID(u.ID)
	clienteIDs := models.GetClienteIDsForPortalUser(fullUser)
	hasAccess := false
	for _, cid := range clienteIDs {
		if cid == f.ClienteID {
			hasAccess = true
			break
		}
	}
	if !hasAccess {
		return c.Status(403).JSON(fiber.Map{"error": "Sin acceso"})
	}
	clean := f.Archivo
	if !strings.HasPrefix(clean, "uploads/") {
		return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"})
	}
	c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, f.OriginalName))
	return c.SendFile(clean)
}
