import random

from .. import Provider as BaseProvider


class Provider(BaseProvider):
    """
    A Faker provider for the Spanish VAT IDs and DOIs
    """

    vat_id_formats = (
        "ES?########",
        "ES########?",
        "ES?#######?",
    )

    def vat_id(self) -> str:
        """
        http://ec.europa.eu/taxation_customs/vies/faq.html#item_11
        :return: a random Spanish VAT ID

        :sample:
        """

        return self.bothify(self.random_element(self.vat_id_formats))

    def nie(self) -> str:
        """
        https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
        :return: a random Spanish NIE

        :sample:
        """

        first_chr = random.randrange(0, 3)
        doi_body = str(random.randrange(0, 10000000)).zfill(7)
        control = self._calculate_control_doi(str(first_chr) + doi_body)
        return "XYZ"[first_chr] + doi_body + control

    def nif(self) -> str:
        """
        https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
        :return: NIF

        :sample:
        """

        nie_body = str(random.randrange(0, 100000000))  # generate a number of a maximum of 8 characters long
        return nie_body.zfill(8) + self._calculate_control_doi(nie_body)

    def cif(self) -> str:
        """
        https://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
        :return: a random Spanish CIF

        :sample:
        """

        first_chr = random.choice("ABCDEFGHJNPQRSUVW")
        doi_body = str(random.randrange(0, 10000000)).zfill(7)
        cif = first_chr + doi_body
        return cif + self._calculate_control_cif(cif)

    def nuss(self, company: bool = False) -> str:
        """
        :param company: flag to indicate if we should generate a company NUSS
        :return: a random Spanish Social Security Number (Número de la Seguridad Social)

        :sample:
        :sample: company=True
        """
        nuss_body_length = 8
        if company:
            nuss_body_length = 7
        province_digits = f"{random.choice(list(range(1, 54)) + [66]):02d}"
        nuss_body = "".join(str(random.randint(0, 9)) for _ in range(nuss_body_length))
        control_digits = f"{int(province_digits+nuss_body) % 97:02d}"
        nuss = f"{province_digits}{nuss_body}{control_digits}"
        return nuss

    @staticmethod
    def _calculate_control_doi(doi: str) -> str:
        """
        Calculate the letter that corresponds to the end of a DOI
        :param doi: calculated value so far needing a control character
        :return: DOI control character
        """

        lookup = "TRWAGMYFPDXBNJZSQVHLCKE"
        return lookup[int(doi) % 23]

    @classmethod
    def _calculate_control_cif(cls, cif: str) -> str:
        """
        Calculate the letter that corresponds to the end of a CIF
        :param cif: calculated value so far needing a control character
        :return: CIF control character

        Code was converted from the minified js of: https://generadordni.es/
        """

        sum_ = 0
        first_chr, cif_value = cif[0], cif[1:]
        for index, char in enumerate(cif_value):
            if index % 2:
                sum_ += int(char)
            else:
                sum_ += sum(map(int, str(int(char) * 2)))
        if sum_ > 10:
            sum_ = int(str(sum_)[-1])
        else:
            sum_ = sum_
        sum_ = 10 - (sum_ % 10)

        if first_chr in ["F", "J", "K", "N", "P", "Q", "R", "S", "U", "V", "W"]:
            return chr(64 + sum_)
        elif first_chr in ["A", "B", "C", "D", "E", "F", "G", "H", "L", "M"]:
            if sum_ == 10:
                sum_ = 0
            return str(sum_)
        else:  # K, L, M  # pragma: no cover
            # Old format that is no longer used, here for full compatability
            return cls._calculate_control_doi(cif)  # pragma: no cover
