"""
Provision a new tenant end-to-end: create tenant + schema, seed master data,
seed default roles, and create the first admin login. General-purpose —
works for any slug, not hardcoded to the two demo tenants (unlike seed.py /
create_users.py, which predate real client onboarding and only know about
demo-enterprise/demo-government).

Safe to re-run (idempotent at every step). Usage:

    # Step 1 — create the tenant + empty schema:
    python provision_tenant.py --slug government2 --plan government

    # Step 2 — run migrations for ALL tenants (loops automatically, safe):
    docker compose exec api alembic upgrade head

    # Step 3 — re-run this script with the same slug to finish provisioning
    # (seed masters, seed roles, create the first admin login):
    python provision_tenant.py --slug government2 --plan government \\
        --admin-email admin@government2.in

If --admin-password is omitted, a random one is generated and printed once —
save it, it is not stored anywhere in plaintext.
"""
import argparse
import asyncio
import secrets

from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from ams.core.database import AsyncSessionLocal, set_tenant_search_path
from ams.core.security import hash_password
from ams.services.tenant import TenantAlreadyExists, create_tenant, get_tenant_by_slug


async def _schema_has_users_table(session: AsyncSession, schema: str) -> bool:
    result = await session.execute(
        text("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = :s AND table_name = 'users')"),
        {"s": schema},
    )
    return bool(result.scalar())


async def provision(slug: str, plan: str, admin_email: str | None, admin_password: str | None, admin_name: str) -> None:
    async with AsyncSessionLocal() as session:
        async with session.begin():
            try:
                tenant = await create_tenant(slug=slug, plan=plan)
                print(f"Tenant '{slug}' created (id={tenant['id']}).")
            except TenantAlreadyExists:
                tenant = await get_tenant_by_slug(slug)
                print(f"Tenant '{slug}' already exists (id={tenant['id']}) — continuing.")

            schema = "tenant_" + tenant["id"].replace("-", "_")
            await set_tenant_search_path(session, tenant["id"])

            if not await _schema_has_users_table(session, schema):
                print(
                    f"\nSchema {schema} has no tables yet — migrations haven't run for this "
                    f"tenant. Run this now, then re-run this script with the same --slug:\n\n"
                    f"    docker compose exec api alembic upgrade head\n"
                )
                return

            from ams.data.seed_tenant_masters import seed_tenant_masters
            await seed_tenant_masters(session, label=slug)

            from ams.services.rbac import seed_default_roles
            await seed_default_roles(session)
            print(f"  Default roles (super_admin/admin/accounts_officer/read_only) seeded for {slug}")

            from ams.services.rbac_admin import seed_admin_roles, sync_catalog
            await sync_catalog(session)
            await seed_admin_roles(session)
            print(f"  Admin/Manager/Employee/CA roles + permission catalog seeded for {slug}")

            if admin_email:
                password = admin_password or secrets.token_urlsafe(12)
                pw_hash = hash_password(password)
                await session.execute(
                    text("""
                        INSERT INTO users (id, email, password_hash, name, role, is_active, created_at, updated_at)
                        VALUES (gen_random_uuid(), :email, :pw, :name, 'super_admin', true, NOW(), NOW())
                        ON CONFLICT (email) DO UPDATE
                        SET password_hash = EXCLUDED.password_hash, is_active = true, updated_at = NOW()
                    """),
                    {"email": admin_email, "pw": pw_hash, "name": admin_name},
                )
                print(f"  Admin login ready: {admin_email}")
                if not admin_password:
                    print(f"  Generated password (save this now, shown once): {password}")

        print(f"\nTenant '{slug}' fully provisioned.")
        if admin_email:
            print(f"Log in with X-Tenant-Slug: {slug} (or the tenant's own subdomain), email: {admin_email}")


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--slug", required=True, help="Tenant slug, e.g. government2")
    parser.add_argument("--plan", default="starter", help="Plan name (default: starter)")
    parser.add_argument("--admin-email", default=None, help="First admin login email. Omit to skip user creation.")
    parser.add_argument("--admin-password", default=None, help="Omit to auto-generate and print a random password.")
    parser.add_argument("--admin-name", default="Admin User", help="Display name for the first admin (default: 'Admin User')")
    args = parser.parse_args()

    if args.admin_password and args.admin_password == "demo1234":
        parser.error("Refusing to set a real tenant's admin password to the well-known demo password. Pick something else or omit --admin-password to auto-generate one.")

    asyncio.run(provision(args.slug, args.plan, args.admin_email, args.admin_password, args.admin_name))


if __name__ == "__main__":
    main()
