#!/usr/bin/env python3
"""Extrait le tissu des entreprises du numérique du Grand Est depuis l'API
publique recherche-entreprises (Etalab), en respectant le RGPD.

Deux exclusions non négociables :
  * les personnes physiques (nature juridique 1000) — leur raison sociale est
    le nom d'une personne, donc une donnée personnelle ;
  * le bloc `dirigeants`, qui n'est jamais lu ni conservé.

Seules les unités dont l'INSEE autorise la diffusion (statut_diffusion = 'O')
sont retenues.
"""

import csv
import json
import time
import urllib.parse
import urllib.request

API = "https://recherche-entreprises.api.gouv.fr/search"
UA = "cartographie-numerique-grand-est/1.0 (+https://jaikin.eu)"

# Grand Est
DEPARTEMENTS = ["08", "10", "51", "52", "54", "55", "57", "67", "68", "88"]

# Codes NAF du secteur informatique et numérique
NAF = {
    "62.01Z": "Programmation informatique",
    "62.02A": "Conseil en systèmes et logiciels informatiques",
    "62.02B": "Tierce maintenance de systèmes et d'applications",
    "62.03Z": "Gestion d'installations informatiques",
    "62.09Z": "Autres activités informatiques",
    "63.11Z": "Traitement de données, hébergement et activités connexes",
}

DELAI = 0.2          # ~5 requêtes/seconde, sous la limite de l'API
PER_PAGE = 25        # maximum autorisé


def fetch(params, essais=3):
    url = API + "?" + urllib.parse.urlencode(params)
    for tentative in range(essais):
        try:
            req = urllib.request.Request(url, headers={"User-Agent": UA})
            with urllib.request.urlopen(req, timeout=30) as r:
                return json.loads(r.read().decode())
        except Exception as exc:
            if tentative == essais - 1:
                print("  ! échec %s : %s" % (params, exc))
                return None
            time.sleep(1.5 * (tentative + 1))
    return None


def est_diffusable(e):
    """Filtre RGPD : personne morale, diffusion autorisée."""
    if str(e.get("statut_diffusion")) != "O":
        return False
    nj = str(e.get("nature_juridique") or "")
    return bool(nj) and not nj.startswith("1")   # 1000 = entrepreneur individuel


def ligne(e, naf_demande):
    s = e.get("siege") or {}
    return {
        "siren": e.get("siren"),
        "denomination": e.get("nom_raison_sociale") or e.get("nom_complet"),
        "naf_recherche": naf_demande,
        "naf_libelle": NAF[naf_demande],
        "naf_entreprise": e.get("activite_principale"),
        "nature_juridique": e.get("nature_juridique"),
        "categorie_entreprise": e.get("categorie_entreprise"),
        "tranche_effectif": e.get("tranche_effectif_salarie"),
        "date_creation": e.get("date_creation"),
        "etat_administratif": e.get("etat_administratif"),
        "nb_etablissements_ouverts": e.get("nombre_etablissements_ouverts"),
        "siege_departement": s.get("departement"),
        "siege_commune": s.get("libelle_commune"),
        "siege_code_postal": s.get("code_postal"),
        "siege_latitude": s.get("latitude"),
        "siege_longitude": s.get("longitude"),
        "siege_dans_grand_est": "oui" if s.get("departement") in DEPARTEMENTS else "non",
    }


def main():
    vues, lignes, requetes = set(), [], 0
    for naf in NAF:
        for dep in DEPARTEMENTS:
            page, total = 1, None
            while True:
                d = fetch({"activite_principale": naf, "departement": dep,
                           "per_page": PER_PAGE, "page": page})
                requetes += 1
                time.sleep(DELAI)
                if not d or not d.get("results"):
                    break
                if total is None:
                    total = d.get("total_results", 0)
                for e in d["results"]:
                    if not est_diffusable(e):
                        continue
                    if e.get("siren") in vues:
                        continue
                    vues.add(e["siren"])
                    lignes.append(ligne(e, naf))
                if page * PER_PAGE >= min(total or 0, 10000):
                    break
                page += 1
            print("  %s / dép %s : %s trouvés (cumul retenu %d)" % (naf, dep, total, len(lignes)))

    champs = list(lignes[0].keys())
    with open("entreprises-numerique-grand-est.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=champs)
        w.writeheader()
        w.writerows(lignes)
    print("\nTERMINE : %d entreprises uniques, %d requêtes API" % (len(lignes), requetes))


if __name__ == "__main__":
    main()
