# 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.i18n import gettext
from trytond.model import (
    MatchMixin, ModelSQL, ModelView, fields, sequence_ordered)
from trytond.model.exceptions import RecursionError
from trytond.pool import PoolMeta
from trytond.pyson import Bool, Eval, Get, If, TimeDelta


class Template(metaclass=PoolMeta):
    __name__ = 'product.template'
    producible = fields.Boolean("Producible")

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls.producible.states = {
            'invisible': ~Eval('type').in_(cls.get_producible_types()),
            }

    @classmethod
    def get_producible_types(cls):
        return ['goods', 'assets']

    @classmethod
    def view_attributes(cls):
        return super().view_attributes() + [
            ('//page[@id="production"]', 'states', {
                    'invisible': ~Eval('producible'),
                    })]


class Product(metaclass=PoolMeta):
    __name__ = 'product.product'

    boms = fields.One2Many('product.product-production.bom', 'product',
        'BOMs', order=[('sequence', 'ASC'), ('id', 'ASC')],
        states={
            'invisible': ~Eval('producible')
            })
    production_lead_times = fields.One2Many('production.lead_time',
        'product', 'Lead Times', order=[('sequence', 'ASC'), ('id', 'ASC')],
        states={
            'invisible': ~Eval('producible'),
            })

    @classmethod
    def validate(cls, products):
        super().validate(products)
        for product in products:
            product.check_bom_recursion()

    def check_bom_recursion(self, product=None):
        '''
        Check BOM recursion
        '''
        if product is None:
            product = self
        for product_bom in self.boms:
            for input_ in product_bom.bom.inputs:
                if input_.phantom_bom:
                    for i in input_.phantom_bom.inputs:
                        i.check_bom_recursion()
                if input_.product and (input_.product == product
                        or input_.product.check_bom_recursion(
                            product=product)):
                    raise RecursionError(gettext(
                            'production.msg_recursive_bom_product',
                            product=product.rec_name))

    @classmethod
    def copy(cls, products, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('boms', None)
        default.setdefault('production_lead_times', None)
        return super().copy(products, default=default)

    def get_bom(self, pattern=None):
        if pattern is None:
            pattern = {}
        for bom in self.boms:
            if bom.match(pattern):
                return bom


class ProductBom(sequence_ordered(), MatchMixin, ModelSQL, ModelView):
    __name__ = 'product.product-production.bom'

    product = fields.Many2One(
        'product.product', "Product", ondelete='CASCADE', required=True,
        domain=[
            ('producible', '=', True),
            ])
    bom = fields.Many2One(
        'production.bom', "BOM", ondelete='CASCADE', required=True,
        domain=[
            ('output_products', '=', If(Bool(Eval('product')),
                    Eval('product', 0),
                    Get(Eval('_parent_product', {}), 'id', 0))),
            ])

    def get_rec_name(self, name):
        return self.bom.rec_name

    @classmethod
    def search_rec_name(cls, name, clause):
        return [('bom.rec_name',) + tuple(clause[1:])]


class ProductionLeadTime(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
    __name__ = 'production.lead_time'

    product = fields.Many2One(
        'product.product', "Product", ondelete='CASCADE', required=True,
        domain=[
            ('producible', '=', True),
            ])
    bom = fields.Many2One('production.bom', 'BOM', ondelete='CASCADE',
        domain=[
            ('output_products', '=', If(Bool(Eval('product')),
                    Eval('product', -1),
                    Get(Eval('_parent_product', {}), 'id', 0))),
            ])
    lead_time = fields.TimeDelta(
        "Lead Time",
        domain=['OR',
            ('lead_time', '=', None),
            ('lead_time', '>=', TimeDelta()),
            ])

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._order.insert(0, ('product', 'ASC'))
