import logging
import re

from stdnum.eu.vat import check_vies

from odoo import api, fields, models, _

_logger = logging.getLogger(__name__)


class ResPartner(models.Model):
    _name = 'res.partner'
    _inherit = 'res.partner'

    partner_gid = fields.Integer('Company database ID')
    additional_info = fields.Char('Additional info')

    @api.model
    def _iap_replace_location_codes(self, iap_data):
        country_code, country_name = iap_data.pop('country_code', False), iap_data.pop('country_name', False)
        state_code, state_name = iap_data.pop('state_code', False), iap_data.pop('state_name', False)

        country, state = None, None
        if country_code:
            country = self.env['res.country'].search([['code', '=ilike', country_code]])
        if not country and country_name:
            country = self.env['res.country'].search([['name', '=ilike', country_name]])

        if country:
            if state_code:
                state = self.env['res.country.state'].search([
                    ('country_id', '=', country.id), ('code', '=ilike', state_code)
                ], limit=1)
            if not state and state_name:
                state = self.env['res.country.state'].search([
                    ('country_id', '=', country.id), ('name', '=ilike', state_name)
                ], limit=1)

        if country:
            iap_data['country_id'] = {'id': country.id, 'display_name': country.display_name}
        if state:
            iap_data['state_id'] = {'id': state.id, 'display_name': state.display_name}

        return iap_data

    @api.model
    def _iap_replace_language_codes(self, iap_data):
        if lang := iap_data.pop('preferred_language', False):
            if installed_lang := (
                self.env['res.lang'].search([('code', '=', lang), ('iso_code', '=', lang)])  # specific lang (e.g.: fr_BE)
                or
                self.env['res.lang'].search([('code', 'ilike', lang[:2]), ('iso_code', 'ilike', lang[:2])], limit=1)  # fallback to generic lang (e.g. fr)
            ):
                iap_data['lang'] = installed_lang.code
        return iap_data

    @api.model
    def _format_data_company(self, iap_data):
        self._iap_replace_location_codes(iap_data)
        self._iap_replace_language_codes(iap_data)
        return iap_data

    # Deprecated since DnB
    @api.model
    def autocomplete(self, query, timeout=15):
        return []

    # Deprecated since DnB
    @api.model
    def enrich_company(self, company_domain, partner_gid, vat, timeout=15):
        return {}

    # Deprecated since DnB
    @api.model
    def read_by_vat(self, vat, timeout=15):
        return []

    # Deprecated since DnB
    def check_gst_in(self, vat):
        return False

    @api.model
    def autocomplete_by_name(self, query, query_country_id, timeout=15):
        if query_country_id is False:  # If it's 0, we purposely do not want to filter on the country
            query_country_id = self.env.company.country_id.id
        query_country_code = self.env['res.country'].browse(query_country_id).code
        response, _ = self.env['iap.autocomplete.api']._request_partner_autocomplete('search_by_name', {
            'query': query,
            'query_country_code': query_country_code,
        }, timeout=timeout)
        if response and not response.get("error"):
            results = []
            for suggestion in response.get("data"):
                results.append(self._format_data_company(suggestion))
            return results
        else:
            return []

    @api.model
    def autocomplete_by_vat(self, vat, query_country_id, timeout=15):
        query_country_id = query_country_id or self.env.company.country_id.id
        query_country_code = self.env['res.country'].browse(query_country_id).code
        response, _ = self.env['iap.autocomplete.api']._request_partner_autocomplete('search_by_vat', {
            'query': vat,
            'query_country_code': query_country_code,
        }, timeout=timeout)
        if response and not response.get("error"):
            results = []
            for suggestion in response.get("data"):
                results.append(self._format_data_company(suggestion))
            return results
        else:
            vies_result = None
            try:
                _logger.info('Calling VIES service to check VAT for autocomplete: %s', vat)
                vies_result = check_vies(vat, timeout=timeout)
            except Exception:
                _logger.warning("Failed VIES VAT check.", exc_info=True)
            if vies_result:
                name = vies_result['name']
                if vies_result['valid'] and name != '---':
                    address = list(filter(bool, vies_result['address'].split('\n')))
                    street = address[0]
                    zip_city_record = next(filter(lambda addr: re.match(r'^\d.*', addr), address[1:]), None)
                    zip_city = zip_city_record.split(' ', 1) if zip_city_record else [None, None]
                    street2 = next((addr for addr in filter(lambda addr: addr != zip_city_record, address[1:])), None)
                    return [self._iap_replace_location_codes({
                        'name': name,
                        'vat': vat,
                        'street': street,
                        'street2': street2,
                        'city': zip_city[1],
                        'zip': zip_city[0],
                        'country_code': vies_result['countryCode'],
                    })]
            return []

    @api.model
    def _process_enriched_response(self, response, error):
        if response and response.get('data'):
            result = self._format_data_company(response.get('data'))
        else:
            result = {}

        if response and response.get('credit_error'):
            result.update({
                'error': True,
                'error_message': 'Insufficient Credit'
            })
        elif response and response.get('error'):
            result.update({
                'error': True,
                'error_message': _('Unable to enrich company (no credit was consumed).'),
            })
        elif error:
            result.update({
                'error': True,
                'error_message': error
            })
        return result

    @api.model
    def enrich_by_duns(self, duns, timeout=15):
        response, error = self.env['iap.autocomplete.api']._request_partner_autocomplete('enrich_by_duns', {
            'duns': duns,
        }, timeout=timeout)
        return self._process_enriched_response(response, error)

    @api.model
    def enrich_by_gst(self, gst, timeout=15):
        response, error = self.env['iap.autocomplete.api']._request_partner_autocomplete('enrich_by_gst', {
            'gst': gst,
        }, timeout=timeout)
        return self._process_enriched_response(response, error)

    @api.model
    def enrich_by_domain(self, domain, timeout=15):
        response, error = self.env['iap.autocomplete.api']._request_partner_autocomplete('enrich_by_domain', {
            'domain': domain,
        }, timeout=timeout)
        return self._process_enriched_response(response, error)

    def iap_partner_autocomplete_add_tags(self, unspsc_codes):
        """Called by JS to create the activity tags from the UNSPSC codes"""
        self.ensure_one()

        # If the UNSPSC module is installed, we might have a translation, so let's use it
        if self.env['ir.module.module']._get('product_unspsc').state == 'installed':
            tag_names = self.env['product.unspsc.code']\
                            .with_context(active_test=False)\
                            .search([('code', 'in', [unspsc_code for unspsc_code, __ in unspsc_codes])])\
                            .mapped('name')
        # If it's not, then we use the default English name provided by DnB
        else:
            tag_names = [unspsc_name for __, unspsc_name in unspsc_codes]

        tag_ids = self.env['res.partner.category']
        for tag_name in tag_names:
            if existing_tag := self.env['res.partner.category'].search([('name', '=', tag_name)]):
                tag_ids |= existing_tag
            else:
                tag_ids |= self.env['res.partner.category'].create({'name': tag_name})
        self.category_id = tag_ids

    @api.model
    def _get_view(self, view_id=None, view_type='form', **options):
        arch, view = super()._get_view(view_id, view_type, **options)

        if view_type == 'form':
            for node in arch.xpath("//field[@name='name' or @name='vat']"):
                node.set('widget', 'field_partner_autocomplete')

        return arch, view
