package dolibarr

import (
	"encoding/json"
	"fmt"
	"math"
	"time"

	"VerifactuMidAPI/internal/formats"
)

func init() {
	formats.Register(&Transformer{})
}

type Transformer struct{}

func (t *Transformer) Name() string { return "dolibarr" }

type Input struct {
	Invoice InvoiceInput `json:"invoice"`
	Client  *ClientInput `json:"client,omitempty"`
	Emisor  EmisorInput  `json:"emisor"`
	Sistema SistemaInput `json:"sistema"`
}

type InvoiceInput struct {
	Number     string      `json:"number"`
	Date       string      `json:"date"`
	NotePublic string      `json:"notePublic,omitempty"`
	Lines      []LineInput `json:"lines"`
}

type LineInput struct {
	Description string  `json:"description"`
	Quantity    float64 `json:"quantity"`
	UnitPrice   float64 `json:"unitPrice"`
	TaxRate     float64 `json:"taxRate"`
}

type ClientInput struct {
	Name      string `json:"name"`
	VatNumber string `json:"vatNumber"`
}

type EmisorInput struct {
	NIF    string `json:"nif,omitempty"`
	Nombre string `json:"nombre,omitempty"`
}

type SistemaInput struct {
	Nombre       string `json:"nombre,omitempty"`
	NIFProveedor string `json:"nif_proveedor,omitempty"`
	Version      string `json:"version,omitempty"`
}

func (t *Transformer) Transform(raw json.RawMessage) (*formats.TransformResult, error) {
	var in Input
	if err := json.Unmarshal(raw, &in); err != nil {
		return nil, fmt.Errorf("invalid dolibarr format: %w", err)
	}

	if len(in.Invoice.Lines) == 0 {
		return nil, fmt.Errorf("dolibarr format: at least one line is required")
	}

	date, err := parseDate(in.Invoice.Date)
	if err != nil {
		return nil, fmt.Errorf("invalid invoice date: %w", err)
	}

	ivaMap := make(map[float64]*formats.IVAData)
	for _, line := range in.Invoice.Lines {
		rate := line.TaxRate
		lineTotal := line.Quantity * line.UnitPrice
		base := lineTotal / (1 + rate/100)
		cuota := lineTotal - base

		if existing, ok := ivaMap[rate]; ok {
			existing.Base += base
			existing.Cuota += cuota
		} else {
			ivaMap[rate] = &formats.IVAData{
				Base:  base,
				Cuota: cuota,
				Tipo:  rate,
			}
		}
	}

	iva := make([]formats.IVAData, 0, len(ivaMap))
	var importeTotal float64
	for _, v := range ivaMap {
		v.Base = round2(v.Base)
		v.Cuota = round2(v.Cuota)
		importeTotal += v.Base + v.Cuota
		iva = append(iva, *v)
	}

	var dest *formats.DestinatarioData
	if in.Client != nil && in.Client.VatNumber != "" {
		dest = &formats.DestinatarioData{
			Nombre: in.Client.Name,
			NIF:    in.Client.VatNumber,
		}
	}

	desc := in.Invoice.NotePublic
	if desc == "" {
		desc = "Factura"
	}

	return &formats.TransformResult{
		EmisorNIF:       in.Emisor.NIF,
		EmisorNombre:    in.Emisor.Nombre,
		NumSerie:        in.Invoice.Number,
		FechaExpedicion: date,
		TipoFactura:     "F1",
		Descripcion:     desc,
		Destinatario:    dest,
		IVA:             iva,
		ImporteTotal:    round2(importeTotal),
		Sistema: formats.SistemaData{
			Nombre:       in.Sistema.Nombre,
			NIFProveedor: in.Sistema.NIFProveedor,
			Version:      in.Sistema.Version,
		},
	}, nil
}

func parseDate(s string) (string, error) {
	formats := []string{
		"2006-01-02T15:04:05Z07:00",
		"2006-01-02T15:04:05Z",
		"2006-01-02T15:04:05",
		"2006-01-02",
	}
	for _, f := range formats {
		t, err := time.Parse(f, s)
		if err == nil {
			return t.Format("02-01-2006"), nil
		}
	}
	return "", fmt.Errorf("cannot parse date %q", s)
}

func round2(v float64) float64 {
	return math.Round(v*100) / 100
}
