# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval


class TaxRule(metaclass=PoolMeta):
    __name__ = 'account.tax.rule'

    def apply(self, tax, pattern):
        pool = Pool()
        Subdivision = pool.get('country.subdivision')

        def parents(subdivision):
            while subdivision:
                yield subdivision.id
                subdivision = subdivision.parent

        pattern = pattern.copy()
        pattern.setdefault('from_country')
        pattern.setdefault('to_country')
        for name in ['from_subdivision', 'to_subdivision']:
            subdivision = pattern.pop(name, None)
            if not subdivision:
                continue
            subdivision = Subdivision(subdivision)
            pattern[name] = list(parents(subdivision))
        return super().apply(tax, pattern)


class _TaxRuleLineMixin:
    __slots__ = ()
    from_country = fields.Many2One(
        'country.country', 'From Country', ondelete='RESTRICT',
        help="Apply only to addresses of this country.")
    from_subdivision = fields.Many2One(
        'country.subdivision', "From Subdivision", ondelete='RESTRICT',
        domain=[
            ('country', '=', Eval('from_country', -1)),
            ],
        states={
            'invisible': ~Eval('from_country'),
            },
        help="Apply only to addresses in this subdivision.")
    to_country = fields.Many2One(
        'country.country', 'To Country', ondelete='RESTRICT',
        help="Apply only to addresses of this country.")
    to_subdivision = fields.Many2One(
        'country.subdivision', "To Subdivision", ondelete='RESTRICT',
        domain=[
            ('country', '=', Eval('to_country', -1)),
            ],
        states={
            'invisible': ~Eval('to_country'),
            },
        help="Apply only to addresses in this subdivision.")


class TaxRuleLineTemplate(_TaxRuleLineMixin, metaclass=PoolMeta):
    __name__ = 'account.tax.rule.line.template'

    def _get_tax_rule_line_value(self, rule_line=None):
        value = super()._get_tax_rule_line_value(
            rule_line=rule_line)
        if not rule_line or rule_line.from_country != self.from_country:
            value['from_country'] = (
                self.from_country.id if self.from_country else None)
        if (not rule_line
                or rule_line.from_subdivision != self.from_subdivision):
            value['from_subdivision'] = (
                self.from_subdivision.id if self.from_subdivision else None)
        if not rule_line or rule_line.to_country != self.to_country:
            value['to_country'] = (
                self.to_country.id if self.to_country else None)
        if (not rule_line
                or rule_line.to_subdivision != self.to_subdivision):
            value['to_subdivision'] = (
                self.to_subdivision.id if self.to_subdivision else None)
        return value


class TaxRuleLine(_TaxRuleLineMixin, metaclass=PoolMeta):
    __name__ = 'account.tax.rule.line'

    def match(self, pattern):
        for name in ['from_subdivision', 'to_subdivision']:
            subdivision = getattr(self, name)
            if name not in pattern:
                if subdivision:
                    return False
                else:
                    continue
            pattern = pattern.copy()
            subdivisions = pattern.pop(name)
            if (subdivision is not None
                    and subdivision.id not in subdivisions):
                return False
        return super().match(pattern)


class InvoiceLine(metaclass=PoolMeta):
    __name__ = 'account.invoice.line'

    @fields.depends('origin')
    def _get_tax_rule_pattern(self):
        pool = Pool()
        try:
            SaleLine = pool.get('sale.line')
        except KeyError:
            SaleLine = None
        try:
            PurchaseLine = pool.get('purchase.line')
        except KeyError:
            PurchaseLine = None
        try:
            Move = pool.get('stock.move')
            ShipmentOut = pool.get('stock.shipment.out')
            ShipmentOutReturn = pool.get('stock.shipment.out.return')
        except KeyError:
            Move = ShipmentOut = ShipmentOutReturn = None

        pattern = super()._get_tax_rule_pattern()

        from_country = from_subdivision = to_country = to_subdivision = None
        origin = self.origin
        # find the first origin which is not an invoice line
        while isinstance(origin, self.__class__) and origin.id >= 0:
            origin = origin.origin
        if (SaleLine
                and isinstance(origin, SaleLine)
                and origin.id >= 0):
            warehouse = origin.warehouse
            if warehouse and warehouse.address:
                from_country = warehouse.address.country
                from_subdivision = warehouse.address.subdivision
            shipment_address = origin.sale.shipment_address
            to_country = shipment_address.country
            to_subdivision = shipment_address.subdivision
        elif (PurchaseLine
                and isinstance(origin, PurchaseLine)
                and origin.id >= 0):
            invoice_address = origin.purchase.invoice_address
            from_country = invoice_address.country
            from_subdivision = invoice_address.subdivision
            warehouse = origin.warehouse
            if warehouse and warehouse.address:
                to_country = warehouse.address.country
                to_subdivision = warehouse.address.subdivision
        elif (Move
                and isinstance(origin, Move)
                and origin.id >= 0):
            move = origin
            if move.from_location.warehouse:
                warehouse_address = move.from_location.warehouse.address
                if warehouse_address:
                    from_country = warehouse_address.country
                    from_subdivision = warehouse_address.subdivision
            elif isinstance(move.origin, ShipmentOutReturn):
                delivery_address = move.origin.delivery_address
                from_country = delivery_address.country
                from_subdivision = delivery_address.subdivision
            if move.to_location.warehouse:
                warehouse_address = move.to_location.warehouse.address
                if warehouse_address:
                    to_country = warehouse_address.country
                    to_subdivision = warehouse_address.subdivision
            elif isinstance(move.origin, ShipmentOut):
                delivery_address = move.origin.delivery_address
                to_country = delivery_address.country
                to_subdivision = delivery_address.subdivision

        pattern['from_country'] = from_country.id if from_country else None
        pattern['from_subdivision'] = (
            from_subdivision.id if from_subdivision else None)
        pattern['to_country'] = to_country.id if to_country else None
        pattern['to_subdivision'] = (
            to_subdivision.id if to_subdivision else None)
        return pattern
