# -*- coding: utf-8 -*-
import hashlib
import base64

from lxml import etree
import requests


class SolicitaDescarga():
    SOAP_URL = 'https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/SolicitaDescargaService.svc'
    SOAP_ACTION = 'http://DescargaMasivaTerceros.sat.gob.mx/ISolicitaDescargaService/SolicitaDescarga'
    NSMAP = {
        's': 'http://schemas.xmlsoap.org/soap/envelope/',
        'des': 'http://DescargaMasivaTerceros.sat.gob.mx',
        'xd': 'http://www.w3.org/2000/09/xmldsig#'
    }

    def __init__(self, fiel):
        self.fiel = fiel
    
    def __generar_soapreq__(self, rfc_solicitante, fecha_inicial, fecha_final, rfc_emisor, rfc_receptor, tipo_solicitud):
        soap_req = etree.Element('{{{}}}{}'.format(self.NSMAP['s'], 'Envelope'), nsmap=self.NSMAP)
        
        etree.SubElement(soap_req, '{{{}}}{}'.format(self.NSMAP['s'], 'Header'))

        body = etree.SubElement(soap_req, '{{{}}}{}'.format(self.NSMAP['s'], 'Body'))

        solicitadescarga = etree.SubElement(body, '{{{}}}{}'.format(self.NSMAP['des'], 'SolicitaDescarga'))

        solicitud = etree.SubElement(solicitadescarga, '{{{}}}{}'.format(self.NSMAP['des'], 'solicitud'))
        solicitud.set('RfcSolicitante', rfc_solicitante)
        solicitud.set('FechaFinal', fecha_final.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
        solicitud.set('FechaInicial', fecha_inicial.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
        solicitud.set('TipoSolicitud', tipo_solicitud)
        if rfc_emisor is not None:
            solicitud.set('RfcEmisor', rfc_emisor)
        
        if rfc_receptor is not None:
            solicitud.set('RfcReceptor', rfc_receptor)
        
        signature = etree.SubElement(solicitud, 'Signature', nsmap={None: 'http://www.w3.org/2000/09/xmldsig#'})

        signedinfo = etree.SubElement(signature, 'SignedInfo', nsmap={None: 'http://www.w3.org/2000/09/xmldsig#'})

        canonicalizationmethod = etree.SubElement(signedinfo, 'CanonicalizationMethod')
        canonicalizationmethod.set('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#')

        signaturemethod = etree.SubElement(signedinfo, 'SignatureMethod')
        signaturemethod.set('Algorithm', 'http://www.w3.org/2000/09/xmldsig#rsa-sha1')

        reference = etree.SubElement(signedinfo, 'Reference')
        reference.set('URI', '#_0')

        transforms = etree.SubElement(reference, 'Transforms')

        transform = etree.SubElement(transforms, 'Transform')
        transform.set('Algorithm', 'http://www.w3.org/2001/10/xml-exc-c14n#')

        digestmethod = etree.SubElement(reference, 'DigestMethod')
        digestmethod.set('Algorithm', 'http://www.w3.org/2000/09/xmldsig#sha1')

        digestvalue = etree.SubElement(reference, 'DigestValue')

        signaturevalue = etree.SubElement(signature, 'SignatureValue')

        keyinfo = etree.SubElement(signature, 'KeyInfo')
        
        x509data = etree.SubElement(keyinfo, 'X509Data')

        x509issuerserial = etree.SubElement(x509data, 'X509IssuerSerial')

        x509issuername = etree.SubElement(x509issuerserial, 'X509IssuerName')
        
        x509serialnumber = etree.SubElement(x509issuerserial, 'X509SerialNumber')
        
        x509certificate = etree.SubElement(x509data, 'X509Certificate')

        to_digest = etree.tostring(solicitadescarga, method='c14n', exclusive=1)

        digest = base64.b64encode(hashlib.new('sha1', to_digest).digest())
        
        digestvalue.text = digest

        to_sign = etree.tostring(signedinfo, method='c14n', exclusive=1)

        firma = self.fiel.firmar_sha1(to_sign)

        signaturevalue.text = firma

        x509certificate.text = self.fiel.cer_to_base64()

        x509issuername.text = self.fiel.cer_issuer()

        x509serialnumber.text = self.fiel.cer_serial_number()
        
        return etree.tostring(soap_req)
    
    def solicitar_descarga(
            self, token, rfc_solicitante, fecha_inicial, fecha_final,
            rfc_emisor=None, rfc_receptor=None, tipo_solicitud='CFDI'
        ):
        
        soapreq = self.__generar_soapreq__(
            rfc_solicitante, fecha_inicial, fecha_final, rfc_emisor, rfc_receptor, tipo_solicitud
        )

        headers = {
            'Content-type': 'text/xml;charset="utf-8"',
            'Accept': 'text/xml',
            'Cache-Control': 'no-cache',
            'SOAPAction': self.SOAP_ACTION,
            'Authorization': 'WRAP access_token="{}"'.format(token)
        }

        response = requests.post(self.SOAP_URL, data=soapreq, headers=headers, verify=True)

        if response.status_code != requests.codes['ok']:
            if not response.text.startswith('<s:Envelope'):
                ex = 'El webservice Autenticacion responde: {}'.format(response.text)
            else:
                resp_xml = etree.fromstring(response.text)
                ex = resp_xml.find('s:Body/s:Fault/faultstring', namespaces=self.NSMAP).text
            raise Exception(ex)

        if not response.text.startswith('<s:Envelope'):
            ex = 'El webservice Autenticacion responde: {}'.format(response.text)
            raise Exception(ex)

        nsmap= {
            's': 'http://schemas.xmlsoap.org/soap/envelope/',
            None: 'http://DescargaMasivaTerceros.sat.gob.mx'
        }

        resp_xml = etree.fromstring(response.text)

        f_val = 's:Body/SolicitaDescargaResponse/SolicitaDescargaResult'

        s_d_r = resp_xml.find(f_val, namespaces=nsmap)

        ret_val = {
            'id_solicitud': s_d_r.get('IdSolicitud'),
            'cod_estatus': s_d_r.get('CodEstatus'),
            'mensaje': s_d_r.get('Mensaje')
        }

        return ret_val
