from odoo import api, fields, models

from odoo.addons.l10n_gr_edi.models.preferred_classification import (
    CLASSIFICATION_CATEGORY_SELECTION,
    CLASSIFICATION_MAP,
    CLASSIFICATION_TYPE_SELECTION,
    CLASSIFICATION_VAT_SELECTION,
    TAX_EXEMPTION_CATEGORY_SELECTION,
    TYPES_WITH_SEND_EXPENSE,
)
from odoo.tools.sql import column_exists, create_column


class AccountMoveLine(models.Model):
    _inherit = 'account.move.line'

    l10n_gr_edi_available_cls_category = fields.Char(compute='_compute_l10n_gr_edi_available_cls_category')
    l10n_gr_edi_available_cls_type = fields.Char(compute='_compute_l10n_gr_edi_available_cls_type')
    l10n_gr_edi_available_cls_vat = fields.Char(compute='_compute_l10n_gr_edi_available_cls_type')
    l10n_gr_edi_need_exemption_category = fields.Boolean(compute='_compute_l10n_gr_edi_need_exemption_category', default=False)

    l10n_gr_edi_detail_type = fields.Selection(
        selection=[('1', '1'), ('2', '2')],
        string='Detail Type',
        compute='_compute_l10n_gr_edi_detail_type',
        store=True,
        readonly=False,
    )
    l10n_gr_edi_cls_category = fields.Selection(
        selection=CLASSIFICATION_CATEGORY_SELECTION,
        string='myDATA Category',
        compute='_compute_l10n_gr_edi_cls_category',
        store=True,
        readonly=False,
    )
    l10n_gr_edi_cls_type = fields.Selection(
        selection=CLASSIFICATION_TYPE_SELECTION,
        string='myDATA Type',
        compute='_compute_l10n_gr_edi_cls_type',
        store=True,
        readonly=False,
    )
    l10n_gr_edi_cls_vat = fields.Selection(
        selection=CLASSIFICATION_VAT_SELECTION,
        string='VAT Classification',
        compute='_compute_l10n_gr_edi_cls_vat',
        store=True,
        readonly=False,
    )
    l10n_gr_edi_tax_exemption_category = fields.Selection(
        selection=TAX_EXEMPTION_CATEGORY_SELECTION,
        string='Tax Exemption Category',
        compute='_compute_l10n_gr_edi_tax_exemption_category',
        store=True,
        readonly=False,
    )

    def _auto_init(self):
        """
        Create all compute-stored fields here to avoid MemoryError when initializing on large databases.
        """
        for column_name, column_type in (
            ('l10n_gr_edi_detail_type', 'varchar'),
            ('l10n_gr_edi_cls_category', 'varchar'),
            ('l10n_gr_edi_cls_type', 'varchar'),
            ('l10n_gr_edi_cls_vat', 'varchar'),
            ('l10n_gr_edi_tax_exemption_category', 'varchar'),
        ):
            if not column_exists(self.env.cr, 'account_move_line', column_name):
                create_column(self.env.cr, 'account_move_line', column_name, column_type)

        return super()._auto_init()

    @api.depends('move_id.l10n_gr_edi_inv_type')
    def _compute_l10n_gr_edi_detail_type(self):
        self.l10n_gr_edi_detail_type = False

    @api.depends(
        'move_id.l10n_gr_edi_inv_type',
        'move_id.l10n_gr_edi_correlation_id',
        'l10n_gr_edi_detail_type',
    )
    def _compute_l10n_gr_edi_available_cls_category(self):
        for line in self:
            inv_type = line.move_id.l10n_gr_edi_inv_type

            # we need inv_type to calculate available_cls_category
            if not inv_type or (
                    inv_type
                    and CLASSIFICATION_MAP[inv_type] == 'associate'
                    and not line.move_id.l10n_gr_edi_correlation_id
            ):  # associate inv_type must have a correlation_id, otherwise inv_type is considered empty
                line.l10n_gr_edi_available_cls_category = False
                continue

            if CLASSIFICATION_MAP[inv_type] == 'associate':
                inv_type = line.move_id.l10n_gr_edi_correlation_id.l10n_gr_edi_inv_type

            is_income = (
                line.move_type in ('out_invoice', 'out_refund')
                and inv_type not in TYPES_WITH_SEND_EXPENSE
                and (not line.l10n_gr_edi_detail_type or line.l10n_gr_edi_detail_type == '2')
            )

            line.l10n_gr_edi_available_cls_category = self.env['l10n_gr_edi.preferred_classification']._get_l10n_gr_edi_available_cls_category(
                inv_type=inv_type, category_type='1' if is_income else '2')

    @api.depends('l10n_gr_edi_cls_category', 'move_id.l10n_gr_edi_correlation_id')
    def _compute_l10n_gr_edi_available_cls_type(self):
        for line in self:
            inv_type = line.move_id.l10n_gr_edi_inv_type
            cls_category = line.l10n_gr_edi_cls_category

            if inv_type and CLASSIFICATION_MAP[inv_type] == 'associate' and line.move_id.l10n_gr_edi_correlation_id:
                inv_type = line.move_id.l10n_gr_edi_correlation_id.l10n_gr_edi_inv_type

            if cls_category:
                line.l10n_gr_edi_available_cls_type = self.env['l10n_gr_edi.preferred_classification']._get_l10n_gr_edi_available_cls_type(inv_type, cls_category)
                line.l10n_gr_edi_available_cls_vat = self.env['l10n_gr_edi.preferred_classification']._get_l10n_gr_edi_available_cls_vat(inv_type, cls_category)
            else:
                line.l10n_gr_edi_available_cls_type = False
                line.l10n_gr_edi_available_cls_vat = False

    def _l10n_gr_edi_get_preferred_classification_id(self, with_category=False):
        self.ensure_one()
        if with_category:  # for _compute_l10n_gr_edi_cls_type
            domain = [
                ('l10n_gr_edi_inv_type', '=', self.move_id.l10n_gr_edi_inv_type),
                ('l10n_gr_edi_cls_category', '=', self.l10n_gr_edi_cls_category),
                ('l10n_gr_edi_cls_type', 'in', self.l10n_gr_edi_available_cls_type.split(',')),
            ]
        else:  # for _compute_l10n_gr_edi_cls_category
            domain = [
                ('l10n_gr_edi_inv_type', '=', self.move_id.l10n_gr_edi_inv_type),
                ('l10n_gr_edi_cls_category', 'in', self.l10n_gr_edi_available_cls_category.split(',')),
            ]

        preferred_id = self.env['l10n_gr_edi.preferred_classification']
        # Try to get from the move's fiscal position first
        if self.move_id.fiscal_position_id:
            preferred_id = self.move_id.fiscal_position_id.l10n_gr_edi_preferred_classification_ids.filtered_domain(domain)[:1]
        # If nothing is found, try to get preferred classification from the line's product
        if not preferred_id and self.product_id:
            preferred_id = self.product_id.product_tmpl_id.l10n_gr_edi_preferred_classification_ids.filtered_domain(domain)[:1]

        # If by the end nothing is still found, set the default preferred classification as [ 1.1 inv-type | 1.2 category | E3_561_007 type ]
        if not preferred_id:
            preferred_id = self.env.ref('l10n_gr_edi.default_preferred_classification', raise_if_not_found=False)
        if not preferred_id:
            preferred_id = self.env['l10n_gr_edi.preferred_classification'].create([{
                'l10n_gr_edi_inv_type': '1.1',
                'l10n_gr_edi_cls_category': 'category1_2',
                'l10n_gr_edi_cls_type': 'E3_561_007',
            }])
            # Save the newly made preferred cls so that we don't have to create a new one everytime.
            self.env['ir.model.data'].create([{
                'name': 'default_preferred_classification',
                'module': 'l10n_gr_edi',
                'model': 'l10n_gr_edi.preferred_classification',
                'res_id': preferred_id.id,
                'noupdate': True,
            }])

        return preferred_id

    @api.depends(
        'move_id.l10n_gr_edi_inv_type',
        'l10n_gr_edi_available_cls_category',
        'product_id',
    )
    def _compute_l10n_gr_edi_cls_category(self):
        for line in self:
            if not line.l10n_gr_edi_available_cls_category:
                line.l10n_gr_edi_cls_category = False
            elif preferred_id := line._l10n_gr_edi_get_preferred_classification_id():
                line.l10n_gr_edi_cls_category = preferred_id.l10n_gr_edi_cls_category
            elif line.l10n_gr_edi_cls_category and line.l10n_gr_edi_cls_category in line.l10n_gr_edi_available_cls_category:
                line.l10n_gr_edi_cls_category = line.l10n_gr_edi_cls_category
            else:
                line.l10n_gr_edi_cls_category = False

    @api.depends(
        'move_id.l10n_gr_edi_inv_type',
        'l10n_gr_edi_available_cls_type',
        'product_id',
    )
    def _compute_l10n_gr_edi_cls_type(self):
        for line in self:
            if not line.l10n_gr_edi_available_cls_type:
                line.l10n_gr_edi_cls_type = False
            elif preferred_id := line._l10n_gr_edi_get_preferred_classification_id(with_category=True):
                line.l10n_gr_edi_cls_type = preferred_id.l10n_gr_edi_cls_type
            elif line.l10n_gr_edi_cls_type and line.l10n_gr_edi_cls_type in line.l10n_gr_edi_available_cls_type:
                line.l10n_gr_edi_cls_type = line.l10n_gr_edi_cls_type
            else:
                line.l10n_gr_edi_cls_type = False

    @api.depends('l10n_gr_edi_available_cls_vat')
    def _compute_l10n_gr_edi_cls_vat(self):
        self.l10n_gr_edi_cls_vat = False

    @api.depends('tax_ids')
    def _compute_l10n_gr_edi_need_exemption_category(self):
        for line in self:
            taxes = line.tax_ids.flatten_taxes_hierarchy()
            line.l10n_gr_edi_need_exemption_category = len(taxes) == 1 and taxes.amount == 0

    @api.depends('tax_ids')
    def _compute_l10n_gr_edi_tax_exemption_category(self):
        for line in self:
            taxes = line.tax_ids.flatten_taxes_hierarchy()
            if line.move_id.country_code == 'GR' and len(taxes) == 1 and taxes.amount == 0:
                if line.l10n_gr_edi_tax_exemption_category:
                    line.l10n_gr_edi_tax_exemption_category = line.l10n_gr_edi_tax_exemption_category
                else:
                    line.l10n_gr_edi_tax_exemption_category = taxes.l10n_gr_edi_default_tax_exemption_category or '1'
            else:
                line.l10n_gr_edi_tax_exemption_category = False
