# Part of Odoo. See LICENSE file for full copyright and licensing details.

from collections import defaultdict

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.addons.l10n_vn_edi_viettel.models.account_move import _l10n_vn_edi_send_request, SINVOICE_API_URL


class L10n_Vn_Edi_ViettelSinvoiceSymbol(models.Model):
    _name = 'l10n_vn_edi_viettel.sinvoice.symbol'
    _description = 'SInvoice symbol'
    """
    The invoice symbols are made of multiple parts.
    The symbols characters have meanings that will influence the resulting invoices:

    The first character is either C (invoice with code from tax department) or K (without)
    It is then followed by two number which represent the current year (24 in 2024)
    Next, a single character representing additional invoice information:

        T: E-invoices registered with tax authorities by enterprises, organizations or household businesses;
        D: E-invoices used for sale of public property or national reserve goods or special e-invoices that do not have some mandatory contents of the “T” invoices;
        L: E-invoices issued separately by tax authorities;
        M: E-invoices generated by cash registers;
        N: Electronic delivery and internal consignment notes;
        B: Electronic delivery notes for goods sent to sales agents;
        G: VAT invoices in the form of electronic stamps, tickets or cards;
        H: Sales invoices in the form of electronic stamps, tickets or cards.

    Finally, it ends with two characters that shall be decided by the seller for their management requirements.
    """

    name = fields.Char(
        string='Symbol',
        required=True,
        readonly=True,
    )
    invoice_template_code = fields.Char(
        string='Template Code',
        required=True,
        readonly=True,
    )
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        required=True,
        default=lambda self: self.env.company,
        readonly=True,
    )
    active = fields.Boolean(default=True)

    _name_template_uniq = models.Constraint(
        'unique (name, invoice_template_code, company_id)',
        'The combination symbol/template must be unique per company!',
    )

    @api.constrains('name', 'invoice_template_code')
    def _constrains_changes(self):
        """
        Multiple API endpoints will use these data, we should thus not allow changing them if they have been used
        for any invoices sent to sinvoice.
        """
        invoice_counts = self.env['account.move']._read_group(
            domain=[
                ('l10n_vn_edi_invoice_symbol', 'in', self.ids),
                ('l10n_vn_edi_invoice_state', 'not in', ('ready_to_send', False)),  # Only matches sent invoices.
            ],
            groupby=['l10n_vn_edi_invoice_symbol'],
            aggregates=['__count'],
        )
        invoices_per_symbol = defaultdict(int)
        for symbol, count in invoice_counts:
            invoices_per_symbol[symbol.id] = count

        for record in self:
            if invoices_per_symbol[record.id] > 0:
                raise UserError(_('You cannot change the symbol value or template of the symbol %s because it has '
                                  'already been used to send invoices.', record.name))

    @api.depends('name', 'invoice_template_code')
    def _compute_display_name(self):
        """ As we allow multiple of the same symbol name, we need to also display the template to differentiate. """
        for symbol in self:
            symbol.display_name = f'{symbol.name} ({symbol.invoice_template_code})'

    @api.model
    def _l10n_vn_edi_lookup_symbols(self, company_id):
        """Lookup available invoice symbols based on the company's tax code."""

        # Get access token from the company
        access_token, error = company_id._l10n_vn_edi_get_access_token()
        if error:
            return {}, error

        symbols_data, error_message = _l10n_vn_edi_send_request(
            method='POST',
            url=f'{SINVOICE_API_URL}InvoiceAPI/InvoiceUtilsWS/getAllInvoiceTemplates',
            json_data={
                'taxCode': company_id.vat,
                'invoiceType': "all",
            },
            headers={
                'Content-Type': 'application/json',
            },
            cookies={'access_token': access_token},
        )
        return symbols_data, error_message

    def action_fetch_symbols(self):
        """Fetch symbols from the API and populate the list."""
        errors = []
        vn_companies = self.env.companies.filtered(lambda c: c.country_id.code == 'VN')

        if not vn_companies:
            raise UserError(_('Please select a Vietnamese company to fetch SInvoice symbol!'))

        existing_symbols = {
            (name, invoice_template_code, company_id): symbol
            for name, invoice_template_code, company_id, symbol in self.with_context(active_test=False)._read_group(
                domain=[('company_id', 'in', vn_companies.ids)],
                groupby=['name', 'invoice_template_code', 'company_id'],
                aggregates=['id:recordset'],
            )
        }

        seen_keys = set()
        symbols_to_create = []

        for company in vn_companies:
            if not company.vat:
                errors.append(_('VAT number is missing on company %s.', company.display_name))
                continue

            symbols_data, error = self._l10n_vn_edi_lookup_symbols(company)

            if error:
                errors.append(_('%(company)s: %(error)s', company=company.display_name, error=error))
                continue

            if not symbols_data:
                errors.append(_('No symbols found for company %s. Please check your configuration and try again.', company.display_name))
                continue

            for symbol_data in symbols_data.get('template', []):
                symbol_code = symbol_data.get('invoiceSeri')
                template_name = symbol_data.get('templateCode')
                key = (symbol_code, template_name, company)

                if key not in existing_symbols:
                    symbols_to_create.append({
                        'name': symbol_code,
                        'invoice_template_code': template_name,
                        'company_id': company.id,
                    })

                seen_keys.add(key)

        if symbols_to_create:
            self.create(symbols_to_create)

        symbols_to_archive = [symbol for key, symbol in existing_symbols.items() if key not in seen_keys]
        if symbols_to_archive:
            self.browse([s.id for s in symbols_to_archive]).write({'active': False})

        if errors:
            if len(vn_companies) == 1:
                raise UserError('\n'.join(msg.split(': ', 1)[-1] for msg in errors))
            else:
                raise UserError(_('Some companies encountered issues:\n\n%s', '\n'.join(errors)))

        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }
