// internal/models/models.go
package models

import (
	"encoding/json"
	"encoding/xml"
)

type FacturaElectronicaCompraVenta struct {
	XMLName                   xml.Name  `xml:"facturaElectronicaCompraVenta"`
	Xsi                       string    `xml:"xmlns:xsi,attr"`
	NoNamespaceSchemaLocation string    `xml:"xsi:noNamespaceSchemaLocation,attr"`
	Cabecera                  Cabecera  `xml:"cabecera"`
	Detalle                   []Detalle `xml:"detalle"`
}

type FacturaEventModel struct {
	TransaccionID string                  `json:"transaccion_id"`
	Estado        string                  `json:"estado"`
	Payload       RequestFacturacionModel `json:"payload"`
}

type RequestFacturacionModel struct {
	Cabecera Cabecera  `json:"cabecera" xml:"cabecera"`
	Detalle  []Detalle `json:"detalle" xml:"detalle"`
}

type Cabecera struct {
	NitEmisor                    int64             `json:"nitEmisor" xml:"nitEmisor"`
	RazonSocialEmisor            string            `json:"razonSocialEmisor" xml:"razonSocialEmisor"`
	Municipio                    string            `json:"municipio" xml:"municipio"`
	Telefono                     Nullable[string]  `json:"telefono" xml:"telefono"`
	NumeroFactura                string            `json:"numeroFactura" xml:"numeroFactura"`
	Cuf                          string            `json:"cuf,omitempty" xml:"cuf,omitempty"`
	Cufd                         string            `json:"cufd,omitempty" xml:"cufd,omitempty"`
	CodigoSucursal               int               `json:"codigoSucursal" xml:"codigoSucursal"`
	Direccion                    string            `json:"direccion" xml:"direccion"`
	CodigoPuntoVenta             Nullable[int]     `json:"codigoPuntoVenta,omitempty" xml:"codigoPuntoVenta,omitempty"`
	FechaEmision                 string            `json:"fechaEmision" xml:"fechaEmision"`
	NombreRazonSocial            Nullable[string]  `json:"nombreRazonSocial" xml:"nombreRazonSocial"`
	CodigoTipoDocumentoIdentidad int               `json:"codigoTipoDocumentoIdentidad" xml:"codigoTipoDocumentoIdentidad"`
	NumeroDocumento              string            `json:"numeroDocumento" xml:"numeroDocumento"`
	Complemento                  Nullable[string]  `json:"complemento,omitempty" xml:"complemento"`
	CodigoCliente                string            `json:"codigoCliente" xml:"codigoCliente"`
	CodigoMetodoPago             int               `json:"codigoMetodoPago" xml:"codigoMetodoPago"`
	NumeroTarjeta                Nullable[string]  `json:"numeroTarjeta,omitempty" xml:"numeroTarjeta"`
	MontoTotal                   float64           `json:"montoTotal" xml:"montoTotal"`
	MontoTotalSujetoIva          float64           `json:"montoTotalSujetoIva" xml:"montoTotalSujetoIva"`
	CodigoMoneda                 int               `json:"codigoMoneda" xml:"codigoMoneda"`
	TipoCambio                   float64           `json:"tipoCambio" xml:"tipoCambio"`
	MontoTotalMoneda             float64           `json:"montoTotalMoneda" xml:"montoTotalMoneda"`
	MontoGiftCard                Nullable[string]  `json:"montoGiftCard" xml:"montoGiftCard"`
	DescuentoAdicional           Nullable[float64] `json:"descuentoAdicional" xml:"descuentoAdicional"`
	CodigoExcepcion              Nullable[int]     `json:"codigoExcepcion,omitempty" xml:"codigoExcepcion,omitempty"`
	Cafc                         Nullable[string]  `json:"cafc,omitempty" xml:"cafc"`
	Leyenda                      string            `json:"leyenda" xml:"leyenda"`
	Usuario                      string            `json:"usuario" xml:"usuario"`
	CodigoDocumentoSector        int               `json:"codigoDocumentoSector" xml:"codigoDocumentoSector"`
	TipoEmision                  int               `json:"tipoEmision" xml:"-"`
	TipoFactura                  int               `json:"tipoFactura" xml:"-"`
}

type Detalle struct {
	ActividadEconomica string            `json:"actividadEconomica" xml:"actividadEconomica"`
	CodigoProductoSin  int               `json:"codigoProductoSin" xml:"codigoProductoSin"`
	CodigoProducto     string            `json:"codigoProducto" xml:"codigoProducto"`
	Descripcion        string            `json:"descripcion" xml:"descripcion"`
	Cantidad           int               `json:"cantidad" xml:"cantidad"`
	UnidadMedida       int               `json:"unidadMedida" xml:"unidadMedida"`
	PrecioUnitario     float64           `json:"precioUnitario" xml:"precioUnitario"`
	MontoDescuento     Nullable[float64] `json:"montoDescuento" xml:"montoDescuento"`
	SubTotal           float64           `json:"subTotal" xml:"subTotal"`
	NumeroSerie        Nullable[string]  `json:"numeroSerie" xml:"numeroSerie"`
	NumeroImei         Nullable[string]  `json:"numeroImei" xml:"numeroImei"`
}

type Nullable[T any] struct {
	Value *T
}

// implementa la interfaz xml.Marshaler para cualquier tipo T
func (n Nullable[T]) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if n.Value == nil {
		// Añadir el atributo xsi:nil="true"
		start.Attr = append(start.Attr, xml.Attr{
			Name:  xml.Name{Local: "xsi:nil"},
			Value: "true",
		})
		e.EncodeElement("", start)
		return nil
	}
	return e.EncodeElement(*n.Value, start)
}

// MarshalJSON implementa la interfaz json.Marshaler
func (n Nullable[T]) MarshalJSON() ([]byte, error) {
	if n.Value == nil {
		return []byte("null"), nil
	}
	return json.Marshal(*n.Value)
}

// UnmarshalJSON implementa la interfaz json.Unmarshaler
func (n *Nullable[T]) UnmarshalJSON(data []byte) error {
	// Si el valor es null, dejamos Value como nil
	if string(data) == "null" {
		n.Value = nil
		return nil
	}

	// Si no es null, intentamos deserializar en un valor de tipo T
	var value T
	if err := json.Unmarshal(data, &value); err != nil {
		return err
	}

	n.Value = &value
	return nil
}
