#
# Para esta implementacion se usa BDD
# ver **test_nomina.py**.
#
# La idea en general es validar comportamiento desde el XML,
# creando las estructuras minimas necesaras.


from .. import fe
from .. import form

from dataclasses import dataclass

class Amount(form.Amount):
    pass


class Devengado:
    pass

@dataclass
class DevengadoBasico(Devengado):
    dias_trabajados: int
    sueldo_trabajado: Amount

    def apply(self, fragment):
        fragment.find_or_create_element('./Basico')
        
        fragment.set_attributes('/Basico',
                                # NIE069
                                DiasTrabajados = str(self.dias_trabajados),
                                # NIE070
                                SueldoTrabajado = str(self.sueldo_trabajado)
                                )

@dataclass
class DevengadoTransporte(Devengado):
    auxilio_transporte: Amount = None
    viatico_manutencion: Amount = None
    viatico_manutencion_no_salarial: Amount = None

    def apply(self, fragment):
        fragment.set_element('./Transporte', None,
                             append_ = True,
                             # NIE071
                             AuxilioTransporte = self.auxilio_transporte,
                             # NIE072
                             ViaticoManuAlojS = self.viatico_manutencion,
                             # NIE073
                             ViaticoManuAlojNS = self.viatico_manutencion_no_salarial
                             )

class Deduccion:
    pass

@dataclass
class DeduccionSalud(Deduccion):
    porcentaje: Amount
    deduccion: Amount

    def apply(self, fragment):
        fragment.set_element('./Salud', None,
                             append_ = True,
                             # NIE161
                             Porcentaje = self.porcentaje,
                             #  NIE163
                             Deduccion = self.deduccion
                             )

@dataclass
class DeduccionFondoPension(Deduccion):
    porcentaje: Amount
    deduccion: Amount

    def apply(self, fragment):
        fragment.set_element('./FondoPension', None,
                             append_ = True,
                             # NIE164
                             Porcentaje = self.porcentaje,
                             #  NIE166
                             Deduccion = self.deduccion
                             )

class DIANNominaIndividualError(Exception):
    pass

class DIANNominaIndividual:
    def __init__(self):
        self.fexml = fe.FeXML('NominaIndividual', 'http://www.dian.gov.co/contratos/facturaelectronica/v1')

        # layout, la dian requiere que los elementos
        # esten ordenados segun el anexo tecnico
        self.fexml.placeholder_for('./Devengados/Basico')
        self.fexml.placeholder_for('./Devengados/Transporte', optional=True)

        self.devengados = self.fexml.fragment('./Devengados')
        self.deducciones = self.fexml.fragment('./Deducciones')

    def adicionar_devengado(self, devengado):
        if not isinstance(devengado, Devengado):
            raise ValueError('se espera tipo Devengado')

        devengado.apply(self.devengados)

    def adicionar_deduccion(self, deduccion):
        if not isinstance(deduccion, Deduccion):
            raise ValueError('se espera tipo Devengado')

        deduccion.apply(self.deducciones)

    def validate(self):
        """
        Valida requisitos segun anexo tecnico
        """
        errors = []

        def check_element(xpath, msg):
            if not self.fexml.exist_element(xpath):
                errors.append(DIANNominaIndividualError(msg))

        def check_attribute(xpath, key, msg):
            err = DIANNominaIndividualError(msg)
            elem = self.fexml.get_element(xpath)

            if elem is None:
                return errors.append(err)

            if elem.get(key, None) is None:
                return errors.append(err)

        check_attribute('/fe:NominaIndividual/Periodo', 'FechaIngreso', 'se requiere Periodo')
        
        check_element(
            '/fe:NominaIndividual/Devengados/Basico',
            'se requiere DevengadoBasico'
        )
        
        check_element(
            '/fe:NominaIndividual/Deducciones/Salud',
            'se requiere DeduccionSalud'
        )

        check_element(
            '/fe:NominaIndividual/Deducciones/FondoPension',
            'se requiere DeduccionFondoPension'
        )

        return errors

    def toFachoXML(self):
        return self.fexml