package verifactu

import (
	"encoding/xml"
	"fmt"
	"time"
)

type AltaRequest struct {
	XMLName         xml.Name            `xml:"sum:RegFactuSistemaFacturacion"`
	Cabecera        Cabecera            `xml:"sum:Cabecera"`
	RegistroFactura AltaRegistroFactura `xml:"sum:RegistroFactura"`
}

type AltaRegistroFactura struct {
	RegistroAlta RegistroAlta `xml:"sum1:RegistroAlta"`
}

type Cabecera struct {
	ObligadoEmision ObligadoEmision `xml:"sum1:ObligadoEmision"`
}

type ObligadoEmision struct {
	NombreRazon string `xml:"sum1:NombreRazon"`
	NIF         string `xml:"sum1:NIF"`
}

type RegistroAlta struct {
	IDVersion                string             `xml:"sum1:IDVersion"`
	IDFactura                IDFactura          `xml:"sum1:IDFactura"`
	NombreRazonEmisor        string             `xml:"sum1:NombreRazonEmisor"`
	TipoFactura              string             `xml:"sum1:TipoFactura"`
	DescripcionOperacion     string             `xml:"sum1:DescripcionOperacion"`
	Destinatarios            *Destinatarios     `xml:"sum1:Destinatarios,omitempty"`
	Desglose                 Desglose           `xml:"sum1:Desglose"`
	CuotaTotal               string             `xml:"sum1:CuotaTotal"`
	ImporteTotal             string             `xml:"sum1:ImporteTotal"`
	Encadenamiento           Encadenamiento     `xml:"sum1:Encadenamiento"`
	SistemaInformatico       SistemaInformatico `xml:"sum1:SistemaInformatico"`
	FechaHoraHusoGenRegistro string             `xml:"sum1:FechaHoraHusoGenRegistro"`
	TipoHuella               string             `xml:"sum1:TipoHuella"`
	Huella                   string             `xml:"sum1:Huella"`
}

type IDFactura struct {
	IDEmisorFactura        string `xml:"sum1:IDEmisorFactura"`
	NumSerieFactura        string `xml:"sum1:NumSerieFactura"`
	FechaExpedicionFactura string `xml:"sum1:FechaExpedicionFactura"`
}

type Destinatarios struct {
	IDDestinatario []IDDestinatario `xml:"sum1:IDDestinatario"`
}

type IDDestinatario struct {
	NombreRazon string `xml:"sum1:NombreRazon"`
	NIF         string `xml:"sum1:NIF"`
}

type Desglose struct {
	DetalleDesglose []DetalleDesglose `xml:"sum1:DetalleDesglose"`
}

type DetalleDesglose struct {
	ClaveRegimen                  string `xml:"sum1:ClaveRegimen"`
	CalificacionOperacion         string `xml:"sum1:CalificacionOperacion"`
	TipoImpositivo                string `xml:"sum1:TipoImpositivo"`
	BaseImponibleOimporteNoSujeto string `xml:"sum1:BaseImponibleOimporteNoSujeto"`
	CuotaRepercutida              string `xml:"sum1:CuotaRepercutida"`
}

type Encadenamiento struct {
	PrimerRegistro   string            `xml:"sum1:PrimerRegistro,omitempty"`
	RegistroAnterior *RegistroAnterior `xml:"sum1:RegistroAnterior,omitempty"`
}

type RegistroAnterior struct {
	IDEmisorFactura        string `xml:"sum1:IDEmisorFactura"`
	NumSerieFactura        string `xml:"sum1:NumSerieFactura"`
	FechaExpedicionFactura string `xml:"sum1:FechaExpedicionFactura"`
	Huella                 string `xml:"sum1:Huella"`
}

type SistemaInformatico struct {
	NombreRazon                 string `xml:"sum1:NombreRazon"`
	NIF                         string `xml:"sum1:NIF"`
	NombreSistemaInformatico    string `xml:"sum1:NombreSistemaInformatico"`
	IdSistemaInformatico        string `xml:"sum1:IdSistemaInformatico"`
	Version                     string `xml:"sum1:Version"`
	NumeroInstalacion           string `xml:"sum1:NumeroInstalacion"`
	TipoUsoPosibleSoloVerifactu string `xml:"sum1:TipoUsoPosibleSoloVerifactu"`
	TipoUsoPosibleMultiOT       string `xml:"sum1:TipoUsoPosibleMultiOT"`
	IndicadorMultiplesOT        string `xml:"sum1:IndicadorMultiplesOT"`
}

type AnulacionRequest struct {
	XMLName         xml.Name                `xml:"sum:RegFactuSistemaFacturacion"`
	Cabecera        Cabecera                `xml:"sum:Cabecera"`
	RegistroFactura AnulacionRegistroFactura `xml:"sum:RegistroFactura"`
}

type AnulacionRegistroFactura struct {
	RegistroAnulacion RegistroAnulacion `xml:"sum1:RegistroAnulacion"`
}

type RegistroAnulacion struct {
	IDVersion                string             `xml:"sum1:IDVersion"`
	IDFacturaAnulada         IDFacturaAnulada   `xml:"sum1:IDFacturaAnulada"`
	Encadenamiento           Encadenamiento     `xml:"sum1:Encadenamiento"`
	SistemaInformatico       SistemaInformatico `xml:"sum1:SistemaInformatico"`
	FechaHoraHusoGenRegistro string             `xml:"sum1:FechaHoraHusoGenRegistro"`
	TipoHuella               string             `xml:"sum1:TipoHuella"`
	Huella                   string             `xml:"sum1:Huella"`
}

type IDFacturaAnulada struct {
	IDEmisorFacturaAnulada        string `xml:"sum1:IDEmisorFacturaAnulada"`
	NumSerieFacturaAnulada        string `xml:"sum1:NumSerieFacturaAnulada"`
	FechaExpedicionFacturaAnulada string `xml:"sum1:FechaExpedicionFacturaAnulada"`
}

type Response struct {
	XMLName xml.Name `xml:"Envelope"`
	Body    ResponseBody
}

type ResponseBody struct {
	XMLName   xml.Name           `xml:"Body"`
	Fault     *Fault             `xml:"Fault"`
	Respuesta *RespuestaRegFactu `xml:"RespuestaRegFactuSistemaFacturacion"`
}

type Fault struct {
	FaultCode   string `xml:"faultcode"`
	FaultString string `xml:"faultstring"`
}

type RespuestaRegFactu struct {
	CSV             string           `xml:"CSV"`
	EstadoEnvio     string           `xml:"EstadoEnvio"`
	RespuestaLineas []RespuestaLinea `xml:"RespuestaLinea"`
}

type RespuestaLinea struct {
	EstadoRegistro   string `xml:"EstadoRegistro"`
	CodigoError      string `xml:"CodigoErrorRegistro"`
	DescripcionError string `xml:"DescripcionErrorRegistro"`
}

type AltaData struct {
	EmisorNombre       string
	EmisorNIF          string
	NumSerie           string
	FechaExpedicion    time.Time
	TipoFactura        string
	Descripcion        string
	DestinatarioNombre string
	DestinatarioNIF    string
	IVA                []IVARegularizacionData
	CuotaTotal         float64
	ImporteTotal       float64
	Sistema            SistemaData
	Huella             string
	FechaGen           time.Time
	PrevHash           string
	PrevNumSerie       string
	PrevFecha          time.Time
}

type IVARegularizacionData struct {
	Base         float64
	Cuota        float64
	Tipo         float64
	ClaveRegimen string
	Calificacion string
}

type SistemaData struct {
	Nombre            string
	NIFProveedor      string
	NombreSistema     string
	Version           string
	NumeroInstalacion string
	TipoUsoVerifactu  string
}

func BuildAltaRequest(data AltaData) *AltaRequest {
	fechaExp := data.FechaExpedicion.Format("02-01-2006")
	fechaGen := data.FechaGen.Format("2006-01-02T15:04:05-07:00")

	ivaList := make([]DetalleDesglose, len(data.IVA))
	for i, iva := range data.IVA {
		ivaList[i] = DetalleDesglose{
			ClaveRegimen:                  iva.ClaveRegimen,
			CalificacionOperacion:         iva.Calificacion,
			TipoImpositivo:                fmt.Sprintf("%.2f", iva.Tipo),
			BaseImponibleOimporteNoSujeto: fmt.Sprintf("%.2f", iva.Base),
			CuotaRepercutida:              fmt.Sprintf("%.2f", iva.Cuota),
		}
	}

	var enc Encadenamiento
	if data.PrevHash == "" {
		enc = Encadenamiento{PrimerRegistro: "S"}
	} else {
		enc = Encadenamiento{
			RegistroAnterior: &RegistroAnterior{
				IDEmisorFactura:        data.EmisorNIF,
				NumSerieFactura:        data.PrevNumSerie,
				FechaExpedicionFactura: data.PrevFecha.Format("02-01-2006"),
				Huella:                 data.PrevHash,
			},
		}
	}

	var destinatarios *Destinatarios
	if data.DestinatarioNIF != "" {
		destinatarios = &Destinatarios{
			IDDestinatario: []IDDestinatario{
				{NombreRazon: data.DestinatarioNombre, NIF: data.DestinatarioNIF},
			},
		}
	}

	req := &AltaRequest{
		Cabecera: Cabecera{
			ObligadoEmision: ObligadoEmision{
				NombreRazon: data.EmisorNombre,
				NIF:         data.EmisorNIF,
			},
		},
		RegistroFactura: AltaRegistroFactura{
			RegistroAlta: RegistroAlta{
				IDVersion: "1.0",
				IDFactura: IDFactura{
					IDEmisorFactura:        data.EmisorNIF,
					NumSerieFactura:        data.NumSerie,
					FechaExpedicionFactura: fechaExp,
				},
				NombreRazonEmisor:    data.EmisorNombre,
				TipoFactura:          data.TipoFactura,
				DescripcionOperacion: data.Descripcion,
				Destinatarios:        destinatarios,
				Desglose: Desglose{
					DetalleDesglose: ivaList,
				},
				CuotaTotal:     fmt.Sprintf("%.2f", data.CuotaTotal),
				ImporteTotal:   fmt.Sprintf("%.2f", data.ImporteTotal),
				Encadenamiento: enc,
				SistemaInformatico: SistemaInformatico{
					NombreRazon:                 data.Sistema.Nombre,
					NIF:                         data.Sistema.NIFProveedor,
					NombreSistemaInformatico:    data.Sistema.NombreSistema,
					IdSistemaInformatico:        "01",
					Version:                     data.Sistema.Version,
					NumeroInstalacion:           data.Sistema.NumeroInstalacion,
					TipoUsoPosibleSoloVerifactu: data.Sistema.TipoUsoVerifactu,
					TipoUsoPosibleMultiOT:       "N",
					IndicadorMultiplesOT:        "N",
				},
				FechaHoraHusoGenRegistro: fechaGen,
				TipoHuella:               "01",
				Huella:                   data.Huella,
			},
		},
	}

	return req
}

func BuildAnulacionRequest(data AltaData) *AnulacionRequest {
	fechaExp := data.FechaExpedicion.Format("02-01-2006")
	fechaGen := data.FechaGen.Format("2006-01-02T15:04:05-07:00")

	var enc Encadenamiento
	if data.PrevHash == "" {
		enc = Encadenamiento{PrimerRegistro: "S"}
	} else {
		enc = Encadenamiento{
			RegistroAnterior: &RegistroAnterior{
				IDEmisorFactura:        data.EmisorNIF,
				NumSerieFactura:        data.PrevNumSerie,
				FechaExpedicionFactura: data.PrevFecha.Format("02-01-2006"),
				Huella:                 data.PrevHash,
			},
		}
	}

	req := &AnulacionRequest{
		Cabecera: Cabecera{
			ObligadoEmision: ObligadoEmision{
				NombreRazon: data.EmisorNombre,
				NIF:         data.EmisorNIF,
			},
		},
		RegistroFactura: AnulacionRegistroFactura{RegistroAnulacion: RegistroAnulacion{
			IDVersion: "1.0",
			IDFacturaAnulada: IDFacturaAnulada{
				IDEmisorFacturaAnulada:        data.EmisorNIF,
				NumSerieFacturaAnulada:        data.NumSerie,
				FechaExpedicionFacturaAnulada: fechaExp,
			},
			Encadenamiento: enc,
			SistemaInformatico: SistemaInformatico{
				NombreRazon:                 data.Sistema.Nombre,
				NIF:                         data.Sistema.NIFProveedor,
				NombreSistemaInformatico:    data.Sistema.NombreSistema,
				IdSistemaInformatico:        "01",
				Version:                     data.Sistema.Version,
				NumeroInstalacion:           data.Sistema.NumeroInstalacion,
				TipoUsoPosibleSoloVerifactu: data.Sistema.TipoUsoVerifactu,
				TipoUsoPosibleMultiOT:       "N",
				IndicadorMultiplesOT:        "N",
			},
			FechaHoraHusoGenRegistro: fechaGen,
			TipoHuella:               "01",
			Huella:                   data.Huella,
		}},
	}

	return req
}
