"""One-time backfill: set owner_user_id on existing client-portal records.

client-portal v1.1.0 enabled rls_enabled on client_account/project_update, but
that does not retroactively set owner_user_id on records created under
v1.0.0 (before RLS existed) -- their actual owner's own session would see
nothing until this runs. This is fully deterministic, not a guess:
client_account already carries the owning app_user_id in its own data (it
was a required field even at v1.0.0), and project_update's owner is simply
whatever its parent client_account's owner turns out to be.

Default is dry-run.

Usage (P2HS-BE root, same .env as the API):

    python -m scripts.backfill_client_portal_ownership --project-id <uuid>
    python -m scripts.backfill_client_portal_ownership --project-id <uuid> --apply
"""
from __future__ import annotations

import argparse
import asyncio
import json
from uuid import UUID

from src.core.orm_import import import_all_orm_models

import_all_orm_models()


def _record_data(record: dict) -> dict:
    raw = record.get("data_json")
    if not raw:
        return {}
    try:
        parsed = json.loads(raw)
        return parsed if isinstance(parsed, dict) else {}
    except (TypeError, ValueError):
        return {}


async def _backfill(project_id: UUID, apply: bool) -> None:
    from src.core.database import async_session_factory
    from src.domain.projects.repository import ProjectRepository
    from src.integrations.cloud_control_client import get_cloud_client_for_workspace

    async with async_session_factory() as db:
        project = await ProjectRepository(db).get_by_id(project_id)
        if not project:
            print(f"[error] project {project_id} not found")
            return
        client = await get_cloud_client_for_workspace(project.workspace_id, db)

    pid = str(project_id)

    # Pass 1: client_account -- owner is its own data.app_user_id.
    account_owner_by_id: dict[str, str] = {}
    accounts_checked = accounts_fixed = accounts_skipped = 0
    cursor = None
    while True:
        page = await client.query_project_records(pid, "client_account", limit=100, cursor=cursor)
        for record in page.get("records", []):
            accounts_checked += 1
            record_id = record["id"]
            owner = record.get("owner_user_id")
            if owner:
                account_owner_by_id[record_id] = owner
                accounts_skipped += 1
                continue
            app_user_id = _record_data(record).get("app_user_id")
            if not app_user_id:
                print(f"[warn] client_account {record_id} has no app_user_id -- cannot backfill, skipping")
                accounts_skipped += 1
                continue
            account_owner_by_id[record_id] = app_user_id
            accounts_fixed += 1
            if apply:
                await client.set_record_owner(pid, "client_account", record_id, app_user_id)
        cursor = page.get("next_cursor")
        if not cursor:
            break

    # Pass 2: project_update -- owner mirrors its parent client_account's owner
    # (resolved in pass 1, including accounts that were already correctly owned).
    updates_checked = updates_fixed = updates_skipped = 0
    cursor = None
    while True:
        page = await client.query_project_records(pid, "project_update", limit=100, cursor=cursor)
        for record in page.get("records", []):
            updates_checked += 1
            record_id = record["id"]
            if record.get("owner_user_id"):
                updates_skipped += 1
                continue
            parent_id = _record_data(record).get("client_account_record_id")
            owner = account_owner_by_id.get(parent_id) if parent_id else None
            if not owner:
                print(f"[warn] project_update {record_id} -> parent {parent_id} has no resolvable owner, skipping")
                updates_skipped += 1
                continue
            updates_fixed += 1
            if apply:
                await client.set_record_owner(pid, "project_update", record_id, owner)
        cursor = page.get("next_cursor")
        if not cursor:
            break

    mode = "APPLIED" if apply else "DRY-RUN"
    print(f"[{mode}] client_account: checked={accounts_checked} fixed={accounts_fixed} skipped={accounts_skipped}")
    print(f"[{mode}] project_update: checked={updates_checked} fixed={updates_fixed} skipped={updates_skipped}")
    if not apply and (accounts_fixed or updates_fixed):
        print("Re-run with --apply to write these changes.")


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Backfill owner_user_id on client-portal records after enabling RLS"
    )
    parser.add_argument("--project-id", required=True, help="Project UUID")
    parser.add_argument("--apply", action="store_true", help="Write changes (default: dry-run)")
    args = parser.parse_args()
    asyncio.run(_backfill(UUID(args.project_id), args.apply))


if __name__ == "__main__":
    main()
