package services

import (
	"bytes"
	"encoding/json"
	"fmt"
	"text/template"
	"time"

	"github.com/google/uuid"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)

// GenerarDocumento es el punto único que usan cotizaciones, contratos, actas de
// proyecto y cuentas de cobro para producir su PDF: toma la plantilla activa del
// tipo pedido (Capa 1, fuente de verdad editable en /plantillas-documento), la
// renderiza con los datos que arma cada endpoint, la convierte a PDF y la deja
// guardada en el historial (DocumentoGenerado) asociada al cliente/proyecto.
func GenerarDocumento(tipo string, datos map[string]interface{}, clienteID, proyectoID *uint, generadoPor string) (*models.DocumentoGenerado, []byte, error) {
	plantilla, err := models.GetPlantillaDocumentoActiva(tipo)
	if err != nil {
		return nil, nil, fmt.Errorf("no hay una plantilla activa para el tipo '%s': %w", tipo, err)
	}

	tmpl, err := template.New("documento").Parse(plantilla.ContenidoHTML)
	if err != nil {
		return nil, nil, fmt.Errorf("plantilla HTML inválida: %w", err)
	}
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, datos); err != nil {
		return nil, nil, fmt.Errorf("error al renderizar plantilla: %w", err)
	}

	pdfBytes, err := RenderHTMLToPDF(buf.String())
	if err != nil {
		return nil, nil, err
	}

	filename := fmt.Sprintf("%s-%s.pdf", tipo, uuid.NewString())
	subdir := tipo
	if clienteID != nil {
		subdir = fmt.Sprintf("%s/%d", tipo, *clienteID)
	}
	path, err := SaveGeneratedFile(subdir, filename, pdfBytes)
	if err != nil {
		return nil, nil, err
	}

	doc := &models.DocumentoGenerado{
		Tipo:        tipo,
		ClienteID:   clienteID,
		ProyectoID:  proyectoID,
		PlantillaID: &plantilla.ID,
		Nombre:      filename,
		Archivo:     path,
		TipoMime:    "application/pdf",
		Tamanio:     int64(len(pdfBytes)),
		DatosJSON:   marshalDatos(datos),
		GeneradoPor: generadoPor,
	}
	if err := models.CreateDocumentoGenerado(doc); err != nil {
		return nil, nil, fmt.Errorf("documento generado pero no se pudo guardar en el historial: %w", err)
	}

	return doc, pdfBytes, nil
}

// ItemCotizacion es un renglón de una cotización con el precio ya calculado
// por quien llama (Claude, tras consultar /api/v2/tarifas). El backend solo suma.
type ItemCotizacion struct {
	Descripcion   string  `json:"descripcion"`
	Cantidad      float64 `json:"cantidad"`
	ValorUnitario float64 `json:"valor_unitario"`
	Unidad        string  `json:"unidad"`
}

// CrearCotizacion es la única implementación de "generar cotización": la usan por
// igual el endpoint POST /api/v2/cotizaciones y la tool crear_cotizacion del
// agente de Telegram/Claude, para que el comportamiento sea idéntico sin importar el canal.
func CrearCotizacion(clienteID uint, alcance, tipoProyecto string, items []ItemCotizacion, generadoPor string) (*models.DocumentoGenerado, float64, error) {
	if clienteID == 0 {
		return nil, 0, fmt.Errorf("cliente_id requerido")
	}
	if alcance == "" {
		return nil, 0, fmt.Errorf("alcance requerido")
	}
	if len(items) == 0 {
		return nil, 0, fmt.Errorf("items requerido (al menos un ítem con descripcion, cantidad y valor_unitario)")
	}

	cliente, err := models.GetClienteByID(clienteID)
	if err != nil {
		return nil, 0, fmt.Errorf("cliente no encontrado: %w", err)
	}

	var total float64
	for _, it := range items {
		total += it.Cantidad * it.ValorUnitario
	}

	datos := DatosBaseDocumento(map[string]interface{}{
		"Cliente":      cliente,
		"Alcance":      alcance,
		"TipoProyecto": tipoProyecto,
		"Items":        items,
		"Total":        total,
	})

	doc, _, err := GenerarDocumento("cotizacion", datos, &clienteID, nil, generadoPor)
	if err != nil {
		return nil, 0, err
	}
	return doc, total, nil
}

// DatosBaseDocumento agrega los campos comunes a cualquier plantilla (fecha,
// datos fiscales de U-SITE) al mapa de datos específicos de cada tipo de documento.
func DatosBaseDocumento(extra map[string]interface{}) map[string]interface{} {
	base := map[string]interface{}{
		"Fecha":         time.Now().Format("02/01/2006"),
		"EmpresaNombre": "U-SITE S.A.S BIC",
		"EmpresaWeb":    "https://u-site.app",
	}
	for k, v := range extra {
		base[k] = v
	}
	return base
}

func marshalDatos(datos map[string]interface{}) string {
	b, err := json.Marshal(datos)
	if err != nil {
		return "{}"
	}
	return string(b)
}
