import re
import cxmlinvbot.mapping.action

from   cxmlinvbot.config.base import BaseConfig
from   cxmlinvbot.mapping.headermapping import HeaderMapping
from   cxmlinvbot.mapping.mapping import Mapping
from   local.config.env import EnvConfig

class InvoiceDetailRequestServiceMapping(Mapping):
  
  VAT_RE = re.compile('(?P<rate>[0-9]*)%.*')

  DTD_URL = BaseConfig.INVOICE_DETAIL_DTD_URL

  STRUCTURE = HeaderMapping.STRUCTURE + [
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoiceDetailHeaderIndicator',
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoiceDetailLineIndicator',
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner',
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/Name',
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/PostalAddress',
    'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/PaymentTerm',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailOrderInfo/OrderReference/DocumentReference',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/InvoiceDetailServiceItemReference',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/SubtotalAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Description',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxableAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/GrossAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/SubtotalAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Description',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxableAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/GrossAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/NetAmount/Money',
    'Request/InvoiceDetailRequest/InvoiceDetailSummary/DueAmount/Money',
  ]

  FIELD_TO_ACTION_MAPS = HeaderMapping.FIELD_TO_ACTION_MAPS + [dict(
    BillToAddressID=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact@addressID', '437805'),
    ContactName=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/Name', 'CBRE MANAGED SERVICES LIMITED'),
    POAddressLine1=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/PostalAddress/Street', '61 SOUTHWARK STREET'),
    POCity=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/PostalAddress/City', 'London'),
    POPostalCode=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/PostalAddress/PostalCode', 'SE1 0HL'),
    POCountry=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/PostalAddress/Country@isoCountryCode', 'GB'),
    # This first set of fields come directly from the CSV and in the same order
    #SAVATRegNO=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailItem/Tax/Extrinsic',
    #                                'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Extrinsic'),
    #                                '775685765'),
    InvoiceNumber=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader@invoiceID'),
    Reference=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailOrderInfo/OrderReference/DocumentReference@payloadID'),      
    InvoiceDate=cxmlinvbot.mapping.action.Lambda('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader@invoiceDate', lambda d : d.isoformat(), ('InvoiceDate',)),
    #DueDate=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/PaymentTerm/Extrinsic'),
    PlannedDate=cxmlinvbot.mapping.action.Ignore(),
    TaxableAmount=cxmlinvbot.mapping.action.Required(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/SubtotalAmount/Money',
                                          'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxableAmount/Money',
                                          'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxableAmount/Money',
                                          'Request/InvoiceDetailRequest/InvoiceDetailSummary/NetAmount/Money',
                                          'Request/InvoiceDetailRequest/InvoiceDetailSummary/SubtotalAmount/Money')),
    Total=cxmlinvbot.mapping.action.Required(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/GrossAmount/Money',
                                              'Request/InvoiceDetailRequest/InvoiceDetailSummary/GrossAmount/Money')),
    TaxTotal=cxmlinvbot.mapping.action.Ignore(),
    InvoiceAmountPaid=cxmlinvbot.mapping.action.Ignore(),
    InvoiceAmountDue=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailSummary/DueAmount/Money'),
    InventoryItemCode=cxmlinvbot.mapping.action.Ignore(),
    Description=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/InvoiceDetailServiceItemReference/Description'),      
    Quantity=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem@quantity', ''),
    #UnitAmount=cxmlinvbot.mapping.action.Required('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/UnitPrice/Money'),
    #UoM=cxmlinvbot.mapping.action.Lambda('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/UnitOfMeasure', lambda m : {'Each':'EA'}.get(m, 'M4'), ('UoM',)),      
    Discount=cxmlinvbot.mapping.action.Ignore(),
    LineAmount=cxmlinvbot.mapping.action.Ignore(),
    TaxType=cxmlinvbot.mapping.action.Required(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Description',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Description')),
    #TaxCode=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxRegime',
    #                                'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxRegime'),
    #                                'Standard'),
    TaxAmount=cxmlinvbot.mapping.action.Required(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Money',
                                        'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxAmount/Money',
                                        'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Money',
                                        'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxAmount/Money')),
    TrackingName1=cxmlinvbot.mapping.action.Ignore(),
    TrackingOption1=cxmlinvbot.mapping.action.Ignore(),
    TrackingName2=cxmlinvbot.mapping.action.Ignore(),
    TrackingOption2=cxmlinvbot.mapping.action.Ignore(),
    Currency=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxableAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxAmount/Money@currency',
                                    #'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/UnitPrice/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/SubtotalAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/GrossAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxableAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/SubtotalAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/GrossAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/NetAmount/Money@currency',
                                    'Request/InvoiceDetailRequest/InvoiceDetailSummary/DueAmount/Money@currency'),
                                    'GBP'),
    Type=cxmlinvbot.mapping.action.Ignore(),
    Sent=cxmlinvbot.mapping.action.Ignore(),
    Status=cxmlinvbot.mapping.action.Ignore(),

    # The following set of fields are additional requirements of the CXML document, they are not present in the CSV
    DeploymentMode=cxmlinvbot.mapping.action.Default('Request@deploymentMode', EnvConfig.DEPLOYMENT_MODE), # 'production' or 'test'
    #ExtrinsicDueDate=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/PaymentTerm/Extrinsic@name', 'DueDate'),
    #ExtrinsicVatNo=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailItem/Tax/Extrinsic@name',
    #                                      'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Extrinsic@name'),
    #                                      'ProdoorVatRegNO'),
    InvoiceLineNum=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem@invoiceLineNumber',
                                          'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/InvoiceDetailServiceItemReference@lineNumber'),
                                          '1'), # There is only ever 1 PO per Invoice hence defaulting to 1
    InvoiceOp=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader@operation', 'new'),
    InvoiceOrigin=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader@invoiceOrigin', 'supplier'),
    IPRole=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact@role', 'remitTo'),
    Language=cxmlinvbot.mapping.action.Default(( 'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/InvoiceDetailServiceItemReference/Description@xml:lang',
                                      'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/Description@xml:lang',
                                      'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/Description@xml:lang',
                                      'Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoicePartner/Contact/Name@xml:lang'), 
                                      'en'), # Standard xmlLangCode
    PayInNumDays=cxmlinvbot.mapping.action.Lambda('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/PaymentTerm@payInNumberOfDays', lambda d1, d2 : str((d1-d2).days), ('DueDate', 'InvoiceDate')),
    #SubTotalAmount=cxmlinvbot.mapping.action.Lambda(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/SubtotalAmount/Money',
    #                                      'Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail/TaxableAmount/Money',
    #                                      'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail/TaxableAmount/Money',
    #                                      'Request/InvoiceDetailRequest/InvoiceDetailSummary/NetAmount/Money',
    #                                      'Request/InvoiceDetailRequest/InvoiceDetailSummary/SubtotalAmount/Money'),
    #                                        lambda x, y : str(x * y), ('UnitAmount', 'Quantity')), # we never expect a Quantity > 1 currently            
    TaxCat=cxmlinvbot.mapping.action.Default(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail@category',
                                              'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail@category'),
                                              'vat'),
    TaxInline=cxmlinvbot.mapping.action.Default('Request/InvoiceDetailRequest/InvoiceDetailRequestHeader/InvoiceDetailLineIndicator@isTaxInLine', 'yes'),
    TaxRate=cxmlinvbot.mapping.action.Lambda(('Request/InvoiceDetailRequest/InvoiceDetailOrder/InvoiceDetailServiceItem/Tax/TaxDetail@percentageRate',
                                              'Request/InvoiceDetailRequest/InvoiceDetailSummary/Tax/TaxDetail@percentageRate'),
                                              lambda s : InvoiceDetailRequestServiceMapping.VAT_RE.fullmatch(s).group('rate'), ('TaxType',)),
)]