package services

import (
	"context"
	"encoding/base64"
	"fmt"
	"os"
	"time"

	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/chromedp"
)

// RenderHTMLToPDF convierte un documento HTML (ya renderizado, con JS/CSS inline si aplica)
// a PDF usando Chrome headless. Es el motor de "impresión" que comparten cotizaciones,
// contratos, actas de proyecto y cuentas de cobro: la plantilla se renderiza a HTML con
// text/template y este servicio la convierte al PDF final.
//
// Requiere Chrome/Chromium instalado en el host (ver Dockerfile). La ruta del binario se
// puede forzar con la variable de entorno CHROME_EXEC_PATH; si no está definida, chromedp
// intenta ubicar un Chrome instalado en el sistema.
func RenderHTMLToPDF(html string) ([]byte, error) {
	opts := append(chromedp.DefaultExecAllocatorOptions[:],
		chromedp.Flag("headless", true),
		chromedp.Flag("no-sandbox", true),
		chromedp.Flag("disable-gpu", true),
		chromedp.Flag("disable-dev-shm-usage", true),
	)
	if execPath := os.Getenv("CHROME_EXEC_PATH"); execPath != "" {
		opts = append(opts, chromedp.ExecPath(execPath))
	}

	allocCtx, cancelAlloc := chromedp.NewExecAllocator(context.Background(), opts...)
	defer cancelAlloc()

	ctx, cancel := chromedp.NewContext(allocCtx)
	defer cancel()

	ctx, cancelTimeout := context.WithTimeout(ctx, 30*time.Second)
	defer cancelTimeout()

	dataURI := "data:text/html;base64," + base64.StdEncoding.EncodeToString([]byte(html))

	var pdfBuf []byte
	err := chromedp.Run(ctx,
		chromedp.Navigate(dataURI),
		chromedp.WaitReady("body", chromedp.ByQuery),
		chromedp.ActionFunc(func(ctx context.Context) error {
			buf, _, err := page.PrintToPDF().
				WithPrintBackground(true).
				WithPreferCSSPageSize(true).
				WithMarginTop(0.4).
				WithMarginBottom(0.4).
				WithMarginLeft(0.4).
				WithMarginRight(0.4).
				Do(ctx)
			if err != nil {
				return err
			}
			pdfBuf = buf
			return nil
		}),
	)
	if err != nil {
		return nil, fmt.Errorf("error generando PDF: %w", err)
	}
	return pdfBuf, nil
}

// SaveGeneratedFile persiste bytes generados (ej. un PDF) en disco bajo uploads/documentos/{subdir}
// y retorna la ruta relativa guardada en el modelo (DocumentoGenerado.Archivo, ProyectoDocumento.Archivo, etc).
func SaveGeneratedFile(subdir, filename string, data []byte) (string, error) {
	dir := fmt.Sprintf("uploads/documentos/%s", subdir)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", fmt.Errorf("no se pudo crear el directorio %s: %w", dir, err)
	}
	path := dir + "/" + filename
	if err := os.WriteFile(path, data, 0644); err != nil {
		return "", fmt.Errorf("no se pudo escribir %s: %w", path, err)
	}
	return path, nil
}
