from sql import Table
from sql.operators import Exists

from trytond.model import ModelSingleton, ModelSQL, ModelView, fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction


class PurchaseConfiguration(metaclass=PoolMeta):
    __name__ = 'purchase.configuration'

    allow_modification_after_validation = fields.Boolean(
        "Autorise modification after validation")
    auto_hedging = fields.Boolean("Auto hedge")
    auto_hedging_over = fields.Boolean("Over hedge")

    @classmethod
    def default_allow_modification_after_validation(cls):
        return False

    @classmethod
    def default_auto_hedging(cls):
        return False

    @classmethod
    def default_auto_hedging_over(cls):
        return False


class SaleConfiguration(metaclass=PoolMeta):
    __name__ = 'sale.configuration'

    allow_modification_after_validation = fields.Boolean(
        "Autorise modification after validation")
    auto_hedging = fields.Boolean("Auto hedge")
    auto_hedging_over = fields.Boolean("Over hedge")

    @classmethod
    def default_allow_modification_after_validation(cls):
        return False

    @classmethod
    def default_auto_hedging(cls):
        return False

    @classmethod
    def default_auto_hedging_over(cls):
        return False


class AccountConfiguration(metaclass=PoolMeta):
    __name__ = 'account.configuration'

    default_sale_padding_account = fields.MultiValue(fields.Many2One(
            'account.account', "Default Sale Padding",
            domain=[
                ('closed', '!=', True),
                ('type.revenue', '=', True),
                ('company', '=', Eval('context', {}).get('company', -1)),
                ]))
    default_accrual_padding_account = fields.MultiValue(fields.Many2One(
            'account.account', "Default Accrual Padding",
            domain=[
                ('closed', '!=', True),
                ('type.statement', '=', 'balance'),
                ('company', '=', Eval('context', {}).get('company', -1)),
                ]))

    @classmethod
    def multivalue_model(cls, field):
        pool = Pool()
        if field in {
                'default_sale_padding_account',
                'default_accrual_padding_account',
                }:
            return pool.get('account.configuration.default_account')
        return super().multivalue_model(field)


class AccountConfigurationDefaultAccount(metaclass=PoolMeta):
    __name__ = 'account.configuration.default_account'

    default_sale_padding_account = fields.Many2One(
        'account.account', "Default Sale Padding",
        domain=[
            ('closed', '!=', True),
            ('type.revenue', '=', True),
            ('company', '=', Eval('company', -1)),
            ])
    default_accrual_padding_account = fields.Many2One(
        'account.account', "Default Accrual Padding",
        domain=[
            ('closed', '!=', True),
            ('type.statement', '=', 'balance'),
            ('company', '=', Eval('company', -1)),
            ])


class Configuration(ModelSingleton, ModelSQL, ModelView):
    "Purchase Trade Configuration"
    __name__ = 'purchase_trade.configuration'

    _REPORT_LABELS = (
        ('sale_report_label', 'sale', 'report_sale', 'Proforma'),
        ('sale_commission_report_label', 'account_invoice',
            'report_sale_commission_ict', 'Commission invoice Sale'),
        ('sale_bill_report_label', 'sale', 'report_bill', 'Draft'),
        ('invoice_report_label', 'account_invoice', 'report_invoice',
            'Invoice'),
        ('invoice_cndn_report_label', 'account_invoice',
            'report_invoice_ict_final', 'CN/DN'),
        ('invoice_commission_cndn_report_label', 'account_invoice',
            'report_invoice_ict_commission_final', 'CN/DN Commission'),
        ('invoice_prepayment_report_label', 'account_invoice',
            'report_prepayment', 'Prepayment'),
        ('invoice_packing_list_report_label', 'purchase_trade',
            'report_invoice_packing_list', 'Packing List'),
        ('invoice_payment_order_report_label', 'purchase_trade',
            'report_payment_order', 'Payment Order'),
        ('purchase_report_label', 'purchase', 'report_purchase', 'Purchase'),
        ('purchase_commission_report_label', 'account_invoice',
            'report_purchase_commission_ict', 'Commission invoice Purchase'),
        ('shipment_shipping_report_label', 'stock',
            'report_shipment_in_shipping', 'Shipping instructions'),
        ('shipment_insurance_report_label', 'purchase_trade',
            'report_shipment_in_insurance', 'Insurance'),
        ('shipment_coo_report_label', 'purchase_trade',
            'report_shipment_in_coo', 'COO'),
        ('shipment_packing_list_report_label', 'purchase_trade',
            'report_shipment_in_packing_list', 'Packing List'),
        ('shipment_linkage_report_label', 'purchase_trade',
            'report_shipment_in_linkage', 'Linkage'),
    )
    _COFFEE_MENU_XML_IDS = (
        'menu_coffee_purchase',
        'menu_coffee_sample_purchase',
        'menu_coffee_lab_analysis_purchase',
        'menu_coffee_cupping_session_purchase',
        'menu_coffee_cupping_criterion_purchase',
        'menu_coffee_sale',
        'menu_coffee_sample_sale',
        'menu_coffee_lab_analysis_sale',
        'menu_coffee_cupping_session_sale',
        'menu_coffee_cupping_criterion_sale',
    )
    _ORPHAN_ACT_WINDOW_XML_IDS = (
        'act_itsa_operations_workflow',
        'act_tradon_processes',
    )

    pricing_rule = fields.Text("Pricing Rule")
    active_coffee_compatibility = fields.Boolean(
        "Active coffee compatibility")
    sale_report_template = fields.Char("Sale Template")
    sale_report_label = fields.Char("Sale Menu Label")
    sale_commission_report_template = fields.Char(
        "Sale Commission Template")
    sale_commission_report_label = fields.Char(
        "Sale Commission Menu Label")
    sale_bill_report_template = fields.Char("Sale Bill Template")
    sale_bill_report_label = fields.Char("Sale Bill Menu Label")
    sale_final_report_template = fields.Char("Sale Final Template")
    invoice_report_template = fields.Char("Invoice Template")
    invoice_report_label = fields.Char("Invoice Menu Label")
    invoice_cndn_report_template = fields.Char("CN/DN Template")
    invoice_cndn_report_label = fields.Char("CN/DN Menu Label")
    invoice_commission_cndn_report_template = fields.Char(
        "CN/DN Commission Template")
    invoice_commission_cndn_report_label = fields.Char(
        "CN/DN Commission Menu Label")
    invoice_prepayment_report_template = fields.Char("Prepayment Template")
    invoice_prepayment_report_label = fields.Char("Prepayment Menu Label")
    invoice_packing_list_report_template = fields.Char("Packing List Template")
    invoice_packing_list_report_label = fields.Char("Packing List Menu Label")
    invoice_payment_order_report_template = fields.Char("Payment Order Template")
    invoice_payment_order_report_label = fields.Char(
        "Payment Order Menu Label")
    purchase_report_template = fields.Char("Purchase Template")
    purchase_report_label = fields.Char("Purchase Menu Label")
    purchase_commission_report_template = fields.Char(
        "Purchase Commission Template")
    purchase_commission_report_label = fields.Char(
        "Purchase Commission Menu Label")
    shipment_shipping_report_template = fields.Char("Shipping Template")
    shipment_shipping_report_label = fields.Char("Shipping Menu Label")
    shipment_insurance_report_template = fields.Char("Insurance Template")
    shipment_insurance_report_label = fields.Char("Insurance Menu Label")
    shipment_coo_report_template = fields.Char("COO Template")
    shipment_coo_report_label = fields.Char("COO Menu Label")
    shipment_packing_list_report_template = fields.Char("Packing List Template")
    shipment_packing_list_report_label = fields.Char(
        "Packing List Menu Label")
    shipment_linkage_report_template = fields.Char("Linkage Template")
    shipment_linkage_report_label = fields.Char("Linkage Menu Label")

    @classmethod
    def __register__(cls, module_name):
        super().__register__(module_name)

        if module_name == 'purchase_trade':
            cls._cleanup_orphan_act_window_xml_ids()

    @classmethod
    def _cleanup_orphan_act_window_xml_ids(cls):
        cursor = Transaction().connection.cursor()
        model_data = Table('ir_model_data')
        act_window = Table('ir_action_act_window')

        cursor.execute(*model_data.delete(
                where=(model_data.module == 'purchase_trade')
                & (model_data.model == 'ir.action.act_window')
                & model_data.fs_id.in_(cls._ORPHAN_ACT_WINDOW_XML_IDS)
                & ~Exists(act_window.select(
                        act_window.id,
                        where=act_window.id == model_data.db_id))))

    @classmethod
    def default_active_coffee_compatibility(cls):
        return False

    @classmethod
    def create(cls, vlist):
        records = super().create(vlist)
        cls._sync_report_labels(records)
        active = any(
            bool(values.get(
                'active_coffee_compatibility',
                cls.default_active_coffee_compatibility()))
            for values in vlist)
        cls._sync_coffee_menu_visibility(records, active=active)
        cls._clear_coffee_view_cache()
        return records

    @classmethod
    def write(cls, *args):
        actions = list(zip(args[::2], args[1::2]))
        active = None
        for records, values in actions:
            if 'active_coffee_compatibility' in values:
                active = bool(values['active_coffee_compatibility'])
        super().write(*args)
        records = sum(args[::2], [])
        cls._sync_report_labels(records)
        cls._sync_coffee_menu_visibility(records, active=active)
        if active is not None:
            cls._clear_coffee_view_cache()

    @classmethod
    def _sync_report_labels(cls, records):
        if not records:
            return
        pool = Pool()
        ModelData = pool.get('ir.model.data')
        ActionReport = pool.get('ir.action.report')
        to_write = []
        for record in records:
            for field_name, module, xml_id, default_label in cls._REPORT_LABELS:
                label = (getattr(record, field_name, '') or '').strip()
                action_id = ModelData.get_id(module, xml_id)
                action = ActionReport(action_id)
                target_label = label or default_label
                if getattr(action, 'name', '') != target_label:
                    to_write.extend(([action], {'name': target_label}))
        if to_write:
            with Transaction().set_user(0):
                ActionReport.write(*to_write)

    @classmethod
    def _sync_coffee_menu_visibility(cls, records=None, active=None):
        if active is None:
            configurations = cls.search([], limit=1)
            active = bool(
                configurations
                and configurations[0].active_coffee_compatibility)
        pool = Pool()
        ModelData = pool.get('ir.model.data')
        Menu = pool.get('ir.ui.menu')
        menus = []
        for xml_id in cls._COFFEE_MENU_XML_IDS:
            try:
                menu_id = ModelData.get_id('purchase_trade', xml_id)
            except Exception:
                continue
            menus.append(Menu(menu_id))
        if menus:
            with Transaction().set_user(0):
                Menu.write(menus, {'active': active})

    @classmethod
    def _clear_coffee_view_cache(cls):
        ModelView._fields_view_get_cache.clear()
