from typing import Optional, Tuple

from faker.utils.checksums import calculate_luhn

from .. import Provider as CompanyProvider


class Provider(CompanyProvider):
    formats = (
        "{{last_name}} {{company_suffix}}",
        "{{last_name}} {{last_name}} {{company_suffix}}",
        "{{last_name}}",
        "{{last_name}}",
    )

    catch_phrase_formats = ("{{catch_phrase_noun}} {{catch_phrase_verb}} {{catch_phrase_attribute}}",)

    nouns = (
        "la sécurité",
        "le plaisir",
        "le confort",
        "la simplicité",
        "l'assurance",
        "l'art",
        "le pouvoir",
        "le droit",
        "la possibilité",
        "l'avantage",
        "la liberté",
    )

    verbs = (
        "de rouler",
        "d'avancer",
        "d'évoluer",
        "de changer",
        "d'innover",
        "de louer",
        "d'atteindre vos buts",
        "de concrétiser vos projets",
    )

    attributes = (
        "de manière efficace",
        "plus rapidement",
        "plus facilement",
        "plus simplement",
        "en toute tranquilité",
        "avant-tout",
        "autrement",
        "naturellement",
        "à la pointe",
        "sans soucis",
        "à l'état pur",
        "à sa source",
        "de manière sûre",
        "en toute sécurité",
    )

    company_suffixes: Tuple[str, ...] = (
        "SA",
        "S.A.",
        "SARL",
        "S.A.R.L.",
        "S.A.S.",
        "et Fils",
    )

    siren_format = "### ### ###"

    # Data from:
    # https://www.insee.fr/fr/information/2120875
    # fmt: off
    ape_codes_naf_2003 = [
        "01.11Z", "01.12Z", "01.13Z", "01.14Z", "01.15Z", "01.16Z", "01.19Z",
        "01.21Z", "01.22Z", "01.23Z", "01.24Z", "01.25Z", "01.26Z", "01.27Z",
        "01.28Z", "01.29Z", "01.30Z", "01.41Z", "01.42Z", "01.43Z", "01.44Z",
        "01.45Z", "01.46Z", "01.47Z", "01.49Z", "01.50Z", "01.61Z", "01.62Z",
        "01.63Z", "01.64Z", "01.70Z", "02.10Z", "02.20Z", "02.30Z", "02.40Z",
        "03.11Z", "03.12Z", "03.21Z", "03.22Z", "05.10Z", "05.20Z", "06.10Z",
        "06.20Z", "07.10Z", "07.21Z", "07.29Z", "08.11Z", "08.12Z", "08.91Z",
        "08.92Z", "08.93Z", "08.99Z", "09.10Z", "09.90Z", "10.11Z", "10.12Z",
        "10.13A", "10.13B", "10.20Z", "10.31Z", "10.32Z", "10.39A", "10.39B",
        "10.41A", "10.41B", "10.42Z", "10.51A", "10.51B", "10.51C", "10.51D",
        "10.52Z", "10.61A", "10.61B", "10.62Z", "10.71A", "10.71B", "10.71C",
        "10.71D", "10.72Z", "10.73Z", "10.81Z", "10.82Z", "10.83Z", "10.84Z",
        "10.85Z", "10.86Z", "10.89Z", "10.91Z", "10.92Z", "11.01Z", "11.02A",
        "11.02B", "11.03Z", "11.04Z", "11.05Z", "11.06Z", "11.07A", "11.07B",
        "12.00Z", "13.10Z", "13.20Z", "13.30Z", "13.91Z", "13.92Z", "13.93Z",
        "13.94Z", "13.95Z", "13.96Z", "13.99Z", "14.11Z", "14.12Z", "14.13Z",
        "14.14Z", "14.19Z", "14.20Z", "14.31Z", "14.39Z", "15.11Z", "15.12Z",
        "15.20Z", "16.10A", "16.10B", "16.21Z", "16.22Z", "16.23Z", "16.24Z",
        "16.29Z", "17.11Z", "17.12Z", "17.21A", "17.21B", "17.21C", "17.22Z",
        "17.23Z", "17.24Z", "17.29Z", "18.11Z", "18.12Z", "18.13Z", "18.14Z",
        "18.20Z", "19.10Z", "19.20Z", "20.11Z", "20.12Z", "20.13A", "20.13B",
        "20.14Z", "20.15Z", "20.16Z", "20.17Z", "20.20Z", "20.30Z", "20.41Z",
        "20.42Z", "20.51Z", "20.52Z", "20.53Z", "20.59Z", "20.60Z", "21.10Z",
        "21.20Z", "22.11Z", "22.19Z", "22.21Z", "22.22Z", "22.23Z", "22.29A",
        "22.29B", "23.11Z", "23.12Z", "23.13Z", "23.14Z", "23.19Z", "23.20Z",
        "23.31Z", "23.32Z", "23.41Z", "23.42Z", "23.43Z", "23.44Z", "23.49Z",
        "23.51Z", "23.52Z", "23.61Z", "23.62Z", "23.63Z", "23.64Z", "23.65Z",
        "23.69Z", "23.70Z", "23.91Z", "23.99Z", "24.10Z", "24.20Z", "24.31Z",
        "24.32Z", "24.33Z", "24.34Z", "24.41Z", "24.42Z", "24.43Z", "24.44Z",
        "24.45Z", "24.46Z", "24.51Z", "24.52Z", "24.53Z", "24.54Z", "25.11Z",
        "25.12Z", "25.21Z", "25.29Z", "25.30Z", "25.40Z", "25.50A", "25.50B",
        "25.61Z", "25.62A", "25.62B", "25.71Z", "25.72Z", "25.73A", "25.73B",
        "25.91Z", "25.92Z", "25.93Z", "25.94Z", "25.99A", "25.99B", "26.11Z",
        "26.12Z", "26.20Z", "26.30Z", "26.40Z", "26.51A", "26.51B", "26.52Z",
        "26.60Z", "26.70Z", "26.80Z", "27.11Z", "27.12Z", "27.20Z", "27.31Z",
        "27.32Z", "27.33Z", "27.40Z", "27.51Z", "27.52Z", "27.90Z", "28.11Z",
        "28.12Z", "28.13Z", "28.14Z", "28.15Z", "28.21Z", "28.22Z", "28.23Z",
        "28.24Z", "28.25Z", "28.29A", "28.29B", "28.30Z", "28.41Z", "28.49Z",
        "28.91Z", "28.92Z", "28.93Z", "28.94Z", "28.95Z", "28.96Z", "28.99A",
        "28.99B", "29.10Z", "29.20Z", "29.31Z", "29.32Z", "30.11Z", "30.12Z",
        "30.20Z", "30.30Z", "30.40Z", "30.91Z", "30.92Z", "30.99Z", "31.01Z",
        "31.02Z", "31.03Z", "31.09A", "31.09B", "32.11Z", "32.12Z", "32.13Z",
        "32.20Z", "32.30Z", "32.40Z", "32.50A", "32.50B", "32.91Z", "32.99Z",
        "33.11Z", "33.12Z", "33.13Z", "33.14Z", "33.15Z", "33.16Z", "33.17Z",
        "33.19Z", "33.20A", "33.20B", "33.20C", "33.20D", "35.11Z", "35.12Z",
        "35.13Z", "35.14Z", "35.21Z", "35.22Z", "35.23Z", "35.30Z", "36.00Z",
        "37.00Z", "38.11Z", "38.12Z", "38.21Z", "38.22Z", "38.31Z", "38.32Z",
        "39.00Z", "41.10A", "41.10B", "41.10C", "41.10D", "41.20A", "41.20B",
        "42.11Z", "42.12Z", "42.13A", "42.13B", "42.21Z", "42.22Z", "42.91Z",
        "42.99Z", "43.11Z", "43.12A", "43.12B", "43.13Z", "43.21A", "43.21B",
        "43.22A", "43.22B", "43.29A", "43.29B", "43.31Z", "43.32A", "43.32B",
        "43.32C", "43.33Z", "43.34Z", "43.39Z", "43.91A", "43.91B", "43.99A",
        "43.99B", "43.99C", "43.99D", "43.99E", "45.11Z", "45.19Z", "45.20A",
        "45.20B", "45.31Z", "45.32Z", "45.40Z", "46.11Z", "46.12A", "46.12B",
        "46.13Z", "46.14Z", "46.15Z", "46.16Z", "46.17A", "46.17B", "46.18Z",
        "46.19A", "46.19B", "46.21Z", "46.22Z", "46.23Z", "46.24Z", "46.31Z",
        "46.32A", "46.32B", "46.32C", "46.33Z", "46.34Z", "46.35Z", "46.36Z",
        "46.37Z", "46.38A", "46.38B", "46.39A", "46.39B", "46.41Z", "46.42Z",
        "46.43Z", "46.44Z", "46.45Z", "46.46Z", "46.47Z", "46.48Z", "46.49Z",
        "46.51Z", "46.52Z", "46.61Z", "46.62Z", "46.63Z", "46.64Z", "46.65Z",
        "46.66Z", "46.69A", "46.69B", "46.69C", "46.71Z", "46.72Z", "46.73A",
        "46.73B", "46.74A", "46.74B", "46.75Z", "46.76Z", "46.77Z", "46.90Z",
        "47.11A", "47.11B", "47.11C", "47.11D", "47.11E", "47.11F", "47.19A",
        "47.19B", "47.21Z", "47.22Z", "47.23Z", "47.24Z", "47.25Z", "47.26Z",
        "47.29Z", "47.30Z", "47.41Z", "47.42Z", "47.43Z", "47.51Z", "47.52A",
        "47.52B", "47.53Z", "47.54Z", "47.59A", "47.59B", "47.61Z", "47.62Z",
        "47.63Z", "47.64Z", "47.65Z", "47.71Z", "47.72A", "47.72B", "47.73Z",
        "47.74Z", "47.75Z", "47.76Z", "47.77Z", "47.78A", "47.78B", "47.78C",
        "47.79Z", "47.81Z", "47.82Z", "47.89Z", "47.91A", "47.91B", "47.99A",
        "47.99B", "49.10Z", "49.20Z", "49.31Z", "49.32Z", "49.39A", "49.39B",
        "49.39C", "49.41A", "49.41B", "49.41C", "49.42Z", "49.50Z", "50.10Z",
        "50.20Z", "50.30Z", "50.40Z", "51.10Z", "51.21Z", "51.22Z", "52.10A",
        "52.10B", "52.21Z", "52.22Z", "52.23Z", "52.24A", "52.24B", "52.29A",
        "52.29B", "53.10Z", "53.20Z", "55.10Z", "55.20Z", "55.30Z", "55.90Z",
        "56.10A", "56.10B", "56.10C", "56.21Z", "56.29A", "56.29B", "56.30Z",
        "58.11Z", "58.12Z", "58.13Z", "58.14Z", "58.19Z", "58.21Z", "58.29A",
        "58.29B", "58.29C", "59.11A", "59.11B", "59.11C", "59.12Z", "59.13A",
        "59.13B", "59.14Z", "59.20Z", "60.10Z", "60.20A", "60.20B", "61.10Z",
        "61.20Z", "61.30Z", "61.90Z", "62.01Z", "62.02A", "62.02B", "62.03Z",
        "62.09Z", "63.11Z", "63.12Z", "63.91Z", "63.99Z", "64.11Z", "64.19Z",
        "64.20Z", "64.30Z", "64.91Z", "64.92Z", "64.99Z", "65.11Z", "65.12Z",
        "65.20Z", "65.30Z", "66.11Z", "66.12Z", "66.19A", "66.19B", "66.21Z",
        "66.22Z", "66.29Z", "66.30Z", "68.10Z", "68.20A", "68.20B", "68.31Z",
        "68.32A", "68.32B", "69.10Z", "69.20Z", "70.10Z", "70.21Z", "70.22Z",
        "71.11Z", "71.12A", "71.12B", "71.20A", "71.20B", "72.11Z", "72.19Z",
        "72.20Z", "73.11Z", "73.12Z", "73.20Z", "74.10Z", "74.20Z", "74.30Z",
        "74.90A", "74.90B", "75.00Z", "77.11A", "77.11B", "77.12Z", "77.21Z",
        "77.22Z", "77.29Z", "77.31Z", "77.32Z", "77.33Z", "77.34Z", "77.35Z",
        "77.39Z", "77.40Z", "78.10Z", "78.20Z", "78.30Z", "79.11Z", "79.12Z",
        "79.90Z", "80.10Z", "80.20Z", "80.30Z", "81.10Z", "81.21Z", "81.22Z",
        "81.29A", "81.29B", "81.30Z", "82.11Z", "82.19Z", "82.20Z", "82.30Z",
        "82.91Z", "82.92Z", "82.99Z", "84.11Z", "84.12Z", "84.13Z", "84.21Z",
        "84.22Z", "84.23Z", "84.24Z", "84.25Z", "84.30A", "84.30B", "84.30C",
        "85.10Z", "85.20Z", "85.31Z", "85.32Z", "85.41Z", "85.42Z", "85.51Z",
        "85.52Z", "85.53Z", "85.59A", "85.59B", "85.60Z", "86.10Z", "86.21Z",
        "86.22A", "86.22B", "86.22C", "86.23Z", "86.90A", "86.90B", "86.90C",
        "86.90D", "86.90E", "86.90F", "87.10A", "87.10B", "87.10C", "87.20A",
        "87.20B", "87.30A", "87.30B", "87.90A", "87.90B", "88.10A", "88.10B",
        "88.10C", "88.91A", "88.91B", "88.99A", "88.99B", "90.01Z", "90.02Z",
        "90.03A", "90.03B", "90.04Z", "91.01Z", "91.02Z", "91.03Z", "91.04Z",
        "92.00Z", "93.11Z", "93.12Z", "93.13Z", "93.19Z", "93.21Z", "93.29Z",
        "94.11Z", "94.12Z", "94.20Z", "94.91Z", "94.92Z", "94.99Z", "95.11Z",
        "95.12Z", "95.21Z", "95.22Z", "95.23Z", "95.24Z", "95.25Z", "95.29Z",
        "96.01A", "96.01B", "96.02A", "96.02B", "96.03Z", "96.04Z", "96.09Z",
        "97.00Z", "98.10Z", "98.20Z", "99.00Z",
    ]
    # fmt: on

    # Data from:
    # https://www.insee.fr/fr/information/8181066
    # fmt: off
    ape_codes_naf_2025 = [
        "01.11Y", "01.12Y", "01.13Y", "01.14Y", "01.15Y", "01.16Y", "01.19Y",
        "01.21Y", "01.22Y", "01.23Y", "01.24Y", "01.25Y", "01.26Y", "01.27Y",
        "01.28Y", "01.29Y", "01.30Y", "01.41Y", "01.42Y", "01.43Y", "01.44Y",
        "01.45Y", "01.46Y", "01.47Y", "01.48G", "01.48H", "01.48J", "01.50Y",
        "01.61Y", "01.62Y", "01.63Y", "01.70Y", "02.10Y", "02.20Y", "02.30Y",
        "02.40Y", "03.11Y", "03.12Y", "03.21Y", "03.22Y", "03.30Y", "05.10Y",
        "05.20Y", "06.10Y", "06.20Y", "07.10Y", "07.21Y", "07.29Y", "08.11Y",
        "08.12Y", "08.91Y", "08.92Y", "08.93Y", "08.99Y", "09.10Y", "09.90Y",
        "10.11Y", "10.12Y", "10.13G", "10.13H", "10.20Y", "10.31Y", "10.32Y",
        "10.39G", "10.39H", "10.41Y", "10.42Y", "10.51G", "10.51H", "10.51J",
        "10.52Y", "10.61G", "10.61H", "10.62Y", "10.71G", "10.71H", "10.71J",
        "10.72Y", "10.73Y", "10.81Y", "10.82Y", "10.83Y", "10.84Y", "10.85Y",
        "10.86Y", "10.89Y", "10.91Y", "10.92Y", "11.01Y", "11.02G", "11.02H",
        "11.03Y", "11.04Y", "11.05Y", "11.06Y", "11.07G", "11.07H", "12.00Y",
        "13.10Y", "13.20Y", "13.30Y", "13.91Y", "13.92Y", "13.93Y", "13.94Y",
        "13.95Y", "13.96Y", "13.99Y", "14.10Y", "14.21Y", "14.22Y", "14.23Y",
        "14.24Y", "14.29Y", "15.11Y", "15.12Y", "15.20Y", "16.11Y", "16.12Y",
        "16.21Y", "16.22Y", "16.23Y", "16.24Y", "16.25Y", "16.26Y", "16.27Y",
        "16.28Y", "17.11Y", "17.12Y", "17.21Y", "17.22Y", "17.23Y", "17.24Y",
        "17.25Y", "18.11Y", "18.12Y", "18.13Y", "18.14Y", "18.20Y", "19.10Y",
        "19.20Y", "20.11Y", "20.12Y", "20.13Y", "20.14Y", "20.15Y", "20.16Y",
        "20.17Y", "20.20Y", "20.30Y", "20.41Y", "20.42Y", "20.51Y", "20.59Y",
        "20.60Y", "21.10Y", "21.20Y", "22.11Y", "22.12Y", "22.21Y", "22.22Y",
        "22.23Y", "22.24Y", "22.25Y", "22.26Y", "23.11Y", "23.12Y", "23.13Y",
        "23.14Y", "23.15Y", "23.20Y", "23.31Y", "23.32Y", "23.41Y", "23.42Y",
        "23.43Y", "23.44Y", "23.45Y", "23.51Y", "23.52Y", "23.61Y", "23.62Y",
        "23.63Y", "23.64Y", "23.65Y", "23.66Y", "23.70Y", "23.91Y", "23.99Y",
        "24.10Y", "24.20Y", "24.31Y", "24.32Y", "24.33Y", "24.34Y", "24.41Y",
        "24.42Y", "24.43Y", "24.44Y", "24.45Y", "24.46Y", "24.51Y", "24.52Y",
        "24.53Y", "24.54Y", "25.11Y", "25.12Y", "25.21Y", "25.22Y", "25.30Y",
        "25.40Y", "25.51Y", "25.52Y", "25.53Y", "25.61Y", "25.62Y", "25.63Y",
        "25.91Y", "25.92Y", "25.93Y", "25.94Y", "25.99Y", "26.11Y", "26.12Y",
        "26.20Y", "26.30Y", "26.40Y", "26.51Y", "26.52Y", "26.60Y", "26.70Y",
        "27.11Y", "27.12Y", "27.20Y", "27.31Y", "27.32Y", "27.33Y", "27.40Y",
        "27.51Y", "27.52Y", "27.90Y", "28.11Y", "28.12Y", "28.13G", "28.13H",
        "28.14Y", "28.15Y", "28.21Y", "28.22Y", "28.23Y", "28.24Y", "28.25Y",
        "28.29Y", "28.30Y", "28.41Y", "28.42Y", "28.91Y", "28.92Y", "28.93Y",
        "28.94Y", "28.95Y", "28.96Y", "28.97Y", "28.99Y", "29.10Y", "29.20Y",
        "29.31Y", "29.32Y", "30.11Y", "30.12Y", "30.13Y", "30.20Y", "30.31Y",
        "30.32Y", "30.40Y", "30.91Y", "30.92Y", "30.99Y", "31.00G", "31.00H",
        "31.00J", "32.11Y", "32.12Y", "32.13Y", "32.20Y", "32.30Y", "32.40Y",
        "32.50Y", "32.91Y", "32.99Y", "33.11Y", "33.12Y", "33.13Y", "33.14Y",
        "33.15Y", "33.16Y", "33.17Y", "33.18G", "33.18H", "33.19Y", "33.20Y",
        "35.11Y", "35.12Y", "35.13Y", "35.14Y", "35.15G", "35.15H", "35.16Y",
        "35.21Y", "35.22Y", "35.23Y", "35.24Y", "35.30Y", "35.40Y", "36.00Y",
        "37.00Y", "38.11Y", "38.12Y", "38.21Y", "38.22Y", "38.23Y", "38.31Y",
        "38.32Y", "38.33Y", "39.00Y", "41.00G", "41.00H", "42.11Y", "42.12Y",
        "42.13G", "42.13H", "42.21Y", "42.22Y", "42.91Y", "42.99Y", "43.11Y",
        "43.12G", "43.12H", "43.13Y", "43.21G", "43.21H", "43.22G", "43.22H",
        "43.23Y", "43.24Y", "43.31Y", "43.32G", "43.32H", "43.33Y", "43.34G",
        "43.34H", "43.35Y", "43.41G", "43.41H", "43.41J", "43.42G", "43.42H",
        "43.42J", "43.50Y", "43.60Y", "43.91Y", "43.99G", "43.99H", "46.11Y",
        "46.12Y", "46.13Y", "46.14Y", "46.15Y", "46.16Y", "46.17G", "46.17H",
        "46.18Y", "46.19G", "46.19H", "46.21Y", "46.22Y", "46.23Y", "46.24Y",
        "46.31Y", "46.32G", "46.32H", "46.33Y", "46.34Y", "46.35Y", "46.36Y",
        "46.37Y", "46.38Y", "46.39Y", "46.41Y", "46.42Y", "46.43G", "46.43H",
        "46.44Y", "46.45Y", "46.46Y", "46.47Y", "46.48Y", "46.49Y", "46.50Y",
        "46.61Y", "46.62Y", "46.63Y", "46.64G", "46.64H", "46.64J", "46.64K",
        "46.71G", "46.71H", "46.72Y", "46.73Y", "46.81Y", "46.82Y", "46.83G",
        "46.83H", "46.83J", "46.84G", "46.84H", "46.85Y", "46.86Y", "46.87Y",
        "46.89Y", "46.90Y", "47.11G", "47.11H", "47.11J", "47.11K", "47.11L",
        "47.12G", "47.12H", "47.21Y", "47.22Y", "47.23Y", "47.24Y", "47.25Y",
        "47.26Y", "47.27G", "47.27H", "47.30Y", "47.40Y", "47.51Y", "47.52G",
        "47.52H", "47.53Y", "47.54Y", "47.55G", "47.55H", "47.61Y", "47.62Y",
        "47.63Y", "47.64Y", "47.69Y", "47.71Y", "47.72G", "47.72H", "47.73Y",
        "47.74G", "47.74H", "47.75Y", "47.76Y", "47.77Y", "47.78G", "47.78H",
        "47.79G", "47.79H", "47.81Y", "47.82Y", "47.83Y", "47.91Y", "47.92G",
        "47.92H", "47.92J", "49.11Y", "49.12Y", "49.20Y", "49.31G", "49.31H",
        "49.32Y", "49.33G", "49.33H", "49.34Y", "49.39Y", "49.41G", "49.41H",
        "49.41J", "49.42Y", "49.50Y", "50.10Y", "50.20Y", "50.30Y", "50.40Y",
        "51.10Y", "51.21Y", "51.22Y", "52.10G", "52.10H", "52.21Y", "52.22Y",
        "52.23Y", "52.24G", "52.24H", "52.25Y", "52.26Y", "52.31Y", "52.32Y",
        "53.10Y", "53.20G", "53.20H", "53.30Y", "55.10Y", "55.20Y", "55.30Y",
        "55.40Y", "55.90Y", "56.11G", "56.11H", "56.11J", "56.12Y", "56.21Y",
        "56.22Y", "56.30Y", "56.40Y", "58.11Y", "58.12Y", "58.13Y", "58.19Y",
        "58.21Y", "58.29Y", "59.11G", "59.11H", "59.11J", "59.11K", "59.12Y",
        "59.13Y", "59.14Y", "59.20Y", "60.10Y", "60.20G", "60.20H", "60.31Y",
        "60.39Y", "61.10Y", "61.20Y", "61.90Y", "62.10Y", "62.20G", "62.20H",
        "62.90Y", "63.10Y", "63.91Y", "63.92Y", "64.11Y", "64.19Y", "64.21Y",
        "64.22Y", "64.31Y", "64.32Y", "64.91Y", "64.92Y", "64.99Y", "65.11Y",
        "65.12Y", "65.20Y", "65.30Y", "66.11Y", "66.12Y", "66.19G", "66.19H",
        "66.21Y", "66.22Y", "66.29Y", "66.30Y", "68.11Y", "68.12Y", "68.20G",
        "68.20H", "68.31Y", "68.32G", "68.32H", "69.10Y", "69.20Y", "70.10Y",
        "70.20Y", "71.11Y", "71.12Y", "71.20G", "71.20H", "72.10G", "72.10H",
        "72.20Y", "73.11Y", "73.12Y", "73.20Y", "73.30Y", "74.11Y", "74.12Y",
        "74.13Y", "74.14Y", "74.20Y", "74.30Y", "74.91Y", "74.99Y", "75.00Y",
        "77.11Y", "77.12Y", "77.21Y", "77.22Y", "77.31Y", "77.32Y", "77.33Y",
        "77.34Y", "77.35Y", "77.39Y", "77.40G", "77.40H", "77.51Y", "77.52Y",
        "78.10Y", "78.20G", "78.20H", "79.11Y", "79.12Y", "79.90Y", "80.01Y",
        "80.09Y", "81.10Y", "81.21Y", "81.22Y", "81.23G", "81.23H", "81.30Y",
        "82.10Y", "82.20Y", "82.30Y", "82.40Y", "82.91Y", "82.92Y", "82.99Y",
        "84.11Y", "84.12Y", "84.13Y", "84.21Y", "84.22Y", "84.23Y", "84.24Y",
        "84.25Y", "84.30G", "84.30H", "84.30J", "85.10Y", "85.20Y", "85.31Y",
        "85.32Y", "85.33Y", "85.40Y", "85.51Y", "85.52Y", "85.53Y", "85.59G",
        "85.59H", "85.61Y", "85.69Y", "86.10Y", "86.21Y", "86.22Y", "86.23Y",
        "86.91Y", "86.92Y", "86.93Y", "86.94G", "86.94H", "86.95Y", "86.96Y",
        "86.97Y", "86.99Y", "87.10G", "87.10H", "87.10J", "87.20G", "87.20H",
        "87.30G", "87.30H", "87.91Y", "87.99G", "87.99H", "88.10G", "88.10H",
        "88.10J", "88.91G", "88.91H", "88.91J", "88.99G", "88.99H", "90.11Y",
        "90.12Y", "90.13Y", "90.20Y", "90.31G", "90.31H", "90.39G", "90.39H",
        "91.11Y", "91.12Y", "91.21Y", "91.22Y", "91.30Y", "91.41Y", "91.42Y",
        "92.00Y", "93.11Y", "93.12Y", "93.13Y", "93.19Y", "93.21Y", "93.29Y",
        "94.11Y", "94.12Y", "94.20Y", "94.91Y", "94.92Y", "94.99Y", "95.10Y",
        "95.21Y", "95.22Y", "95.23Y", "95.24Y", "95.25Y", "95.29G", "95.29H",
        "95.31G", "95.31H", "95.32Y", "95.40Y", "96.10G", "96.10H", "96.21G",
        "96.21H", "96.22Y", "96.23Y", "96.30Y", "96.40Y", "96.91Y", "96.99G",
        "96.99H", "97.00Y", "98.10Y", "98.20Y", "99.00Y",
    ]
    # fmt: on

    def catch_phrase_noun(self) -> str:
        """
        Returns a random catch phrase noun.
        """
        return self.random_element(self.nouns)

    def catch_phrase_attribute(self) -> str:
        """
        Returns a random catch phrase attribute.
        """
        return self.random_element(self.attributes)

    def catch_phrase_verb(self) -> str:
        """
        Returns a random catch phrase verb.
        """
        return self.random_element(self.verbs)

    def catch_phrase(self) -> str:
        """
        :example: 'integrate extensible convergence'
        """
        catch_phrase = ""
        while True:
            pattern: str = self.random_element(self.catch_phrase_formats)
            catch_phrase = self.generator.parse(pattern)
            catch_phrase = catch_phrase[0].upper() + catch_phrase[1:]

            if self._is_catch_phrase_valid(catch_phrase):
                break

        return catch_phrase

    # An array containing string which should not appear twice in a catch phrase
    words_which_should_not_appear_twice = ("sécurité", "simpl")

    def _is_catch_phrase_valid(self, catch_phrase: str) -> bool:
        """
        Validates a french catch phrase.

        :param catch_phrase: The catch phrase to validate.
        """
        for word in self.words_which_should_not_appear_twice:
            # Fastest way to check if a piece of word does not appear twice.
            begin_pos = catch_phrase.find(word)
            end_pos = catch_phrase.find(word, begin_pos + 1)

            if begin_pos != -1 and begin_pos != end_pos:
                return False

        return True

    def siren(self) -> str:
        """
        Generates a siren number (9 digits). Formatted as '### ### ###'.
        """
        code = self.numerify("########")
        luhn_checksum = str(calculate_luhn(float(code)))
        return f"{code[:3]} {code[3:6]} {code[6:]}{luhn_checksum}"

    def siret(self, max_sequential_digits: int = 2) -> str:
        """
        Generates a siret number (14 digits).
        It is in fact the result of the concatenation of a siren number (9 digits),
        a sequential number (4 digits) and a control number (1 digit) concatenation.
        If $max_sequential_digits is invalid, it is set to 2.

        The siret number is formatted as '### ### ### #####'.
        :param max_sequential_digits The maximum number of digits for the sequential number (> 0 && <= 4).
        """
        if max_sequential_digits > 4 or max_sequential_digits <= 0:
            max_sequential_digits = 2

        sequential_number = str(self.random_number(max_sequential_digits)).zfill(4)

        code = self.siren().replace(" ", "") + sequential_number
        luhn_checksum = str(calculate_luhn(float(code)))
        return f"{code[:3]} {code[3:6]} {code[6:9]} {code[9:]}{luhn_checksum}"

    def company_vat(self, siren: str = "") -> str:
        """
        Generate a valid TVA (French VAT) number.
        It is the concatenation of "FR", siren checksum and siren number

        :param siren: Force SIREN number

        :sample:
        :sample: siren="123 456 789"
        """
        siren = siren or self.siren()
        siren_int = int("".join(c for c in siren if c.isdigit()))
        checksum = (12 + 3 * (siren_int % 97)) % 97
        return f"FR {checksum:02} {siren}"

    def ape_code(self, version: Optional[str] = "naf-2003") -> str:
        """
        Generate an APE code (also known as NAF code).
        It identify french company main branch of activity.

        It provide numbers from nomenclature `version` `naf-2003` (default)
        or `naf-2025`.
        To have it generate a truly random (and possibly invalid number) set
        `version` to `None`


        :param version: Set to ``"naf-2003"`` to return a valid NAF 2003 APE code.
        Set to ``"naf-2025"`` to return a valid NAF 2025 APE code.
        Set to ``None`` to return a truly random and possibly invalid number
        Defaults to ``"naf-2003"``
        :param letter: Force letter
        :param siren: Force SIREN

        :sample:
        :sample: version="naf-2003"
        :sample: version="naf-2025"
        :sample: version=None
        """
        if version is None:
            numbers = self.numerify("##.##")
            letter = self.random_uppercase_letter()
            return f"{numbers}{letter}"
        if version == "naf-2003":
            return self.random_element(self.ape_codes_naf_2003)
        if version == "naf-2025":
            return self.random_element(self.ape_codes_naf_2025)
        raise ValueError("Unsupported NAF version. Set version=None to a truly random number.")
