"""Import applications depuis le fichier IODA Sanef.

Lit `deploy/ServeursAssociesAuxServicesIODA_*.xlsx` (sheet "Services Metiers")
et UPSERT dans la table `applications` en utilisant `ioda_libelle` comme cle.

Usage:
    python tools/import_applications_ioda.py [chemin_fichier.xlsx]

Si chemin omis: cherche dans deploy/ le plus recent
ServeursAssoci*AuxServicesIODA_*.xlsx.
"""
import os
import sys
import glob
from pathlib import Path
from datetime import datetime

import openpyxl
from sqlalchemy import create_engine, text

ROOT = Path(__file__).resolve().parent.parent
DATABASE_URL = (os.getenv("DATABASE_URL_DEMO")
                or os.getenv("DATABASE_URL")
                or "postgresql://patchcenter:PatchCenter2026!@localhost:5432/patchcenter_db")


def find_ioda_file():
    """Trouve le fichier IODA le plus recent dans deploy/."""
    pattern = str(ROOT / "deploy" / "ServeursAssoci*AuxServicesIODA_*.xlsx")
    files = sorted(glob.glob(pattern))
    if not files:
        # Fallback: deploy/ sans variantes accent
        files = sorted(glob.glob(str(ROOT / "deploy" / "*IODA*.xlsx")))
    return files[-1] if files else None


def parse_services(xlsx_path):
    """Renvoie liste de dicts {libelle, lib_court, code_pos, ...} depuis sheet 'Services Metiers'."""
    wb = openpyxl.load_workbook(xlsx_path, data_only=True)
    ws_name = next((s for s in wb.sheetnames if "Services" in s and "Metier" in s), None)
    if not ws_name:
        raise SystemExit(f"[ERR] Sheet 'Services Metiers' introuvable. Sheets: {wb.sheetnames}")
    ws = wb[ws_name]

    services = []
    for i, row in enumerate(ws.iter_rows(values_only=True)):
        # Header sur 2 lignes (i=0 mega-header, i=1 vrais en-tetes)
        if i < 2:
            continue
        # Cols: 0 Actions, 1 Type, 2 Statut, 3 Code Zone POS, 4 Lib court, 5 Libelle,
        # 6 Alias, 7 Editeur, 8 Description, 9 Commentaire, 10 Nb composants,
        # 11 Perimetre, 12 Dept/Domaine, 13 Resp Metier, 14 Resp DSI
        libelle = row[5]
        if not libelle or not str(libelle).strip():
            continue
        def s(v):
            if v is None: return None
            t = str(v).strip()
            return t or None

        services.append({
            "libelle":      str(libelle).strip(),
            "type":         s(row[1]),
            "statut":       s(row[2]),
            "code_pos":     s(row[3]),
            "lib_court":    s(row[4]),
            "alias":        s(row[6]),
            "editeur":      s(row[7]),
            "description":  s(row[8]),
            "commentaire":  s(row[9]),
            "nb_components": int(row[10]) if row[10] and str(row[10]).strip().isdigit() else None,
            "perimetre":    s(row[11]),
            "dept_domaine": s(row[12]),
            "resp_metier":  s(row[13]),
            "resp_dsi":     s(row[14]),
        })
    return services


SQL_FIND = text("""
    SELECT id FROM applications
    WHERE ioda_libelle = :libelle
       OR (ioda_libelle IS NULL AND nom_court = :nom_court)
    LIMIT 1
""")

SQL_UPDATE = text("""
    UPDATE applications SET
        nom_complet        = :nom_complet,
        description        = COALESCE(:description, description),
        editeur            = COALESCE(:editeur, editeur),
        ioda_libelle       = :libelle,
        ioda_lib_court     = :lib_court,
        ioda_code_pos      = :code_pos,
        ioda_type          = :type,
        ioda_statut        = :statut,
        ioda_alias         = :alias,
        ioda_perimetre     = :perimetre,
        ioda_dept_domaine  = :dept_domaine,
        ioda_resp_metier   = :resp_metier,
        ioda_resp_dsi      = :resp_dsi,
        ioda_nb_components = :nb_components,
        ioda_commentaire   = :commentaire,
        ioda_imported_at   = now(),
        updated_at         = now()
    WHERE id = :id
""")

SQL_INSERT = text("""
    INSERT INTO applications (
        nom_court, nom_complet, description, editeur, criticite,
        ioda_libelle, ioda_lib_court, ioda_code_pos, ioda_type, ioda_statut,
        ioda_alias, ioda_perimetre, ioda_dept_domaine, ioda_resp_metier,
        ioda_resp_dsi, ioda_nb_components, ioda_commentaire, ioda_imported_at,
        created_at, updated_at
    ) VALUES (
        :nom_court, :nom_complet, :description, :editeur, 'standard',
        :libelle, :lib_court, :code_pos, :type, :statut,
        :alias, :perimetre, :dept_domaine, :resp_metier,
        :resp_dsi, :nb_components, :commentaire, now(),
        now(), now()
    )
""")


def main():
    xlsx = sys.argv[1] if len(sys.argv) > 1 else find_ioda_file()
    if not xlsx or not os.path.exists(xlsx):
        print(f"[ERR] Fichier IODA introuvable. Place-le dans deploy/ (ex: deploy/ServeursAssociesAuxServicesIODA_YYYYMMDD.xlsx)")
        sys.exit(1)

    print(f"[INFO] Fichier: {xlsx}")
    services = parse_services(xlsx)
    print(f"[INFO] Services parses: {len(services)}")

    engine = create_engine(DATABASE_URL)
    print(f"[INFO] DB: {DATABASE_URL.rsplit('@', 1)[-1]}")

    inserted = updated = skipped = 0
    with engine.begin() as conn:
        used_nom_court = set()
        for svc in services:
            base_nc = (svc["lib_court"] or svc["libelle"])[:50]
            params = {
                **svc,
                "nom_court": base_nc,
                "nom_complet": svc["libelle"][:200],
            }
            existing = conn.execute(SQL_FIND, params).fetchone()
            if existing:
                conn.execute(SQL_UPDATE, {**params, "id": existing[0]})
                updated += 1
            else:
                # Eviter collision nom_court avec autre app non-IODA en BDD
                # ou avec un autre service IODA deja traite cette session
                nc = base_nc
                if nc in used_nom_court:
                    # ajout suffixe POS pour unicite
                    suf = svc.get("code_pos") or "X"
                    nc = (base_nc[:46] + "-" + suf)[:50]
                used_nom_court.add(nc)
                # Verif collision DB
                row = conn.execute(text("SELECT 1 FROM applications WHERE nom_court=:nc"),
                                   {"nc": nc}).fetchone()
                if row:
                    nc = (base_nc[:42] + "-IODA-" + (svc.get("code_pos") or "X"))[:50]
                params["nom_court"] = nc
                conn.execute(SQL_INSERT, params)
                inserted += 1

    print(f"[OK] Termine — INSERT: {inserted} | UPDATE: {updated} | SKIP: {skipped}")
    print(f"[INFO] Verif: SELECT COUNT(*) FROM applications WHERE ioda_libelle IS NOT NULL;")


if __name__ == "__main__":
    main()
