"""Données de démonstration de la base DATA.

`run(org_id)` crée le site de démonstration du module Sites pour l'organisation « Dev »
(id imprimé par `bun run db:seed` côté web/ — rien n'est partagé entre les bases).
Idempotent : relançable à volonté. Le TLD `.example` (RFC 2606) ne résout jamais :
domaine fictif garanti, et le seed n'appelle pas le preflight — il écrit en base,
sous le garde-fou `assert_local`.

Usage : uv run python -m app.seed --org-id <id>
"""

import argparse
import asyncio
import sys

from sqlalchemy import select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine

from app.core.config import get_settings
from app.db.guard import assert_local
from app.sites.models import Site, SiteTarget

DEMO_DOMAIN = "demo.seoinsight.example"
DEMO_NAME = "Site de démonstration"


async def run(org_id: str) -> None:
    # Moteur dédié, jamais celui de `app.db.engine` : `db_reset` enchaîne plusieurs
    # `asyncio.run()`, et un moteur global resterait attaché à une boucle déjà fermée.
    engine = create_async_engine(str(get_settings().database_url))
    try:
        async with async_sessionmaker(engine, expire_on_commit=False)() as session:
            # ON CONFLICT DO NOTHING plutôt que SELECT puis INSERT : c'est la contrainte
            # UNIQUE (org_id, domain) qui arbitre, donc deux seeds concurrents n'échouent
            # plus sur une IntegrityError brute. Pas de ligne rendue = site déjà présent.
            site_id = await session.scalar(
                pg_insert(Site)
                .values(org_id=org_id, domain=DEMO_DOMAIN, name=DEMO_NAME)
                .on_conflict_do_nothing(index_elements=["org_id", "domain"])
                .returning(Site.id)
            )
            if site_id is None:
                existing = await session.scalar(
                    select(Site.id).where(Site.org_id == org_id, Site.domain == DEMO_DOMAIN)
                )
                if existing is not None:
                    # Auto-réparation : un site de démo privé de sa cible par défaut (état
                    # qu'aucun appel d'api ne produit — seulement un DELETE à la main en
                    # psql) la retrouve. ON CONFLICT DO NOTHING sans inférence d'index : les
                    # deux contraintes de `site_targets` (couple unique, unique partielle
                    # « une seule défaut ») absorbent le cas nominal où la cible est là.
                    await session.execute(
                        pg_insert(SiteTarget)
                        .values(
                            site_id=existing,
                            org_id=org_id,
                            country="FR",
                            language="fr",
                            is_default=True,
                        )
                        .on_conflict_do_nothing()
                    )
                    await session.commit()
                print(
                    f"seed DATA : site de démo déjà présent pour l'organisation {org_id} — "
                    f"SITE_ID={existing}"
                )
                return
            await session.execute(
                pg_insert(SiteTarget).values(
                    site_id=site_id, org_id=org_id, country="FR", language="fr", is_default=True
                )
            )
            await session.commit()
            print(f"seed DATA : site de démo créé pour l'organisation {org_id} — SITE_ID={site_id}")
    finally:
        await engine.dispose()


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        prog="python -m app.seed", description="Données de démonstration de la base DATA."
    )
    parser.add_argument(
        "--org-id",
        required=True,
        help="identifiant de l'organisation « Dev » (ligne ORG_ID=… de `bun run db:seed`)",
    )
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> None:
    args = parse_args(argv)
    # Garde-fou avant toute connexion : le seed n'écrit que dans une base de dev locale.
    try:
        assert_local(str(get_settings().database_url))
    except RuntimeError as exc:
        print(exc, file=sys.stderr)
        raise SystemExit(1) from exc
    asyncio.run(run(args.org_id))


if __name__ == "__main__":
    main()
