"""Construction du bundle de config app mobile depuis les sources committées.

Entrées :
  - contract      : rbac_50_roles.json (portails_business + rôles · source de vérité
                    des ONGLETS et de leurs rôles autorisés).
  - spec          : mobile_spec.json (choix de config : nom/slug/scheme, profils EAS,
                    ordre des onglets, liste des champs `a_confirmer`).
  - portails_spec : labels + icônes des 5 portails (mise en page · même source que les
                    Workspaces).
  - langs         : langues FR/EN/ES + défaut (seo_spec · source unique).

Sortie : bundle déterministe (tri stable, aucun horodatage) :
  - app_config      -> objet Expo `app.config` (thème dark+doré #4, locales seo,
                       identifiants de store `null` · a_confirmer).
  - eas_build       -> objet `eas.json` (profils de build + submit, credentials `null`).
  - role_navigation -> 1 onglet par portail métier, gated par la surface RBAC exacte.
  - manifest        -> traçabilité (comptes, résumé par portail, marque, a_confirmer).

Anti-invention (#6) : aucun identifiant de store/build fabriqué (tous `null`) ; rôles =
surface RBAC exacte du portail (mêmes valeurs que les Has Role des Workspaces) ; tokens
de marque + devises + langues repris verbatim de leurs sources uniques.
"""

from __future__ import annotations

from typing import Any

from . import deps


def _labels_icons(portails_spec: dict) -> tuple[dict, dict]:
    labels = {p["key"]: p["label"] for p in portails_spec["portails"]}
    icons = {p["key"]: p["icon"] for p in portails_spec["portails"]}
    return labels, icons


def build_app_config(spec: dict, langs: list[str], default_lang: str) -> dict[str, Any]:
    """Objet Expo `app.config`. Thème #4 ; identifiants de store `null` (#6/#8)."""
    app = spec["app"]
    b = deps.branding
    return {
        "expo": {
            "name": app["name"],
            "slug": app["slug"],
            "scheme": app["scheme"],
            "version": None,
            "orientation": app["orientation"],
            "userInterfaceStyle": app["user_interface_style"],
            "backgroundColor": b.COLOR_BG,
            "primaryColor": b.COLOR_ACCENT,
            "icon": None,
            "splash": {
                "backgroundColor": b.COLOR_BG,
                "resizeMode": "contain",
                "image": None,
            },
            "locales": {lang: None for lang in langs},
            "ios": {
                "bundleIdentifier": None,
                "buildNumber": None,
                "supportsTablet": True,
            },
            "android": {
                "package": None,
                "versionCode": None,
            },
            "extra": {
                "expoSdkMajor": app["expo_sdk_major"],
                "defaultLocale": default_lang,
                "devises": [b.DEVISE_PRIMAIRE, b.DEVISE_SECONDAIRE],
                "eas": {"projectId": None},
            },
            "plugins": [],
        }
    }


def build_eas(spec: dict) -> dict[str, Any]:
    """Objet `eas.json` : profils de build + submit ; credentials `null` (#6/#8)."""
    return {
        "cli": {"version": None, "appVersionSource": spec["eas"]["app_version_source"]},
        "build": {
            "development": {
                "developmentClient": True,
                "distribution": "internal",
                "channel": "development",
            },
            "preview": {"distribution": "internal", "channel": "preview"},
            "production": {"channel": "production", "autoIncrement": True},
        },
        "submit": {
            "production": {
                "ios": {"appleId": None, "ascAppId": None, "appleTeamId": None},
                "android": {"serviceAccountKeyPath": None, "track": "internal"},
            }
        },
    }


def build_navigation(contract: dict, spec: dict, portails_spec: dict) -> list[dict[str, Any]]:
    """1 onglet par portail métier, dans l'ordre de la spec ; rôles = surface RBAC exacte."""
    roles_map = deps.roles_by_portail(contract)
    labels, icons = _labels_icons(portails_spec)
    nav: list[dict[str, Any]] = []
    for key in spec["portail_order"]:
        roles = sorted(roles_map.get(key, []))
        nav.append(
            {
                "portail": key,
                "label": labels[key],
                "icon": icons[key],
                "route": f"/{key}",
                "roles_allowed": roles,
                "n_roles": len(roles),
            }
        )
    return nav


def build_store_listing(spec: dict, nav: list[dict], langs: list[str]) -> dict[str, Any]:
    """Fiche store (App Store / Play) DÉRIVÉE des sources · #6.

    Le worker ne SOUMET pas aux stores (agent Mobile · #8) : il produit la fiche
    versionnable (sous-titre, descriptions, mots-clés FR/EN/ES). Les faits produit
    prêts à dériver — nombre de portails, énumération des portails, langues — sont
    INJECTÉS depuis la surface réelle (`role_navigation` = labels portails_spec,
    langues seo), jamais recopiés à la main : la copie marketing ne peut donc pas
    mentir sur les portails/langues. Seuls le style rédactionnel et les mots-clés
    sont des choix authoriaux (mêmes que `app.name`), portés par la spec.
    """
    sl_spec = spec["store_listing"]
    portails_str = " · ".join(n["label"] for n in nav)
    langues_str = "/".join(lang.upper() for lang in langs)
    n_portails = str(len(nav))

    def _render(text: str) -> str:
        return (
            text.replace("{n_portails}", n_portails)
            .replace("{portails}", portails_str)
            .replace("{langues}", langues_str)
        )

    content = {
        lang: {champ: _render(text) for champ, text in sl_spec["content"][lang].items()}
        for lang in langs
    }
    return {
        "generated_from": "mobile_spec.json · store_listing (gabarits rédactionnels) + role_navigation (portails) + seo_spec (langues)",
        "note": (
            "Fiche store DÉRIVÉE : nombre/énumération des portails et langues injectés "
            "depuis les sources (jamais recopiés · #6). Le worker ne build ni ne submit "
            "aux stores — hand-off agent Mobile (#8)."
        ),
        "limits": sl_spec["limits"],
        "limits_source": sl_spec["limits_source"],
        "langues": list(langs),
        "content": content,
    }


def build_bundle(contract: dict, spec: dict, portails_spec: dict, langs: list[str], default_lang: str) -> dict[str, Any]:
    app_config = build_app_config(spec, langs, default_lang)
    eas_build = build_eas(spec)
    nav = build_navigation(contract, spec, portails_spec)
    store_listing = build_store_listing(spec, nav, langs)
    b = deps.branding

    portails_meta = [
        {
            "portail": n["portail"],
            "label": n["label"],
            "icon": n["icon"],
            "nb_roles": n["n_roles"],
        }
        for n in nav
    ]

    manifest = {
        "generated_from": spec["generated_from"],
        "spec_version": spec["version"],
        "source_version": contract["version"],
        "roadmap_ref": spec["roadmap_ref"],
        "app": {
            "name": spec["app"]["name"],
            "slug": spec["app"]["slug"],
            "scheme": spec["app"]["scheme"],
            "expo_sdk_major": spec["app"]["expo_sdk_major"],
        },
        "counts": {
            "onglets": len(nav),
            "portails": len(nav),
            "roles_couverts": sum(n["n_roles"] for n in nav),
            "langues": len(langs),
            "identifiants_a_confirmer": len(spec["a_confirmer"]),
        },
        "brand": {
            "fond": {"valeur": b.COLOR_BG, "source": "CLAUDE.md #4"},
            "accent": {"valeur": b.COLOR_ACCENT, "source": "CLAUDE.md #4"},
        },
        "devises": {
            "primaire": b.DEVISE_PRIMAIRE,
            "secondaire": b.DEVISE_SECONDAIRE,
            "source": "CLAUDE.md #10",
        },
        "langues": {
            "liste": list(langs),
            "defaut": default_lang,
            "source": "seo/seo_spec.json · site.langs",
        },
        "a_confirmer": spec["a_confirmer"],
        "identifiants_statut": "a_confirmer",
        "portails": portails_meta,
        "hand_off": (
            "Build Expo 54 + submit App Store #32 / Play Store exécutés hors de ce "
            "worker (agent Mobile · #8) : ce livrable ne produit que la config "
            "versionnable. Les identifiants/credentials `null` sont renseignés au "
            "build sur le poste/CI Mobile, jamais fabriqués ici (#6)."
        ),
    }

    return {
        "app_config": app_config,
        "eas_build": eas_build,
        "role_navigation": nav,
        "store_listing": store_listing,
        "manifest": manifest,
    }
