"""Queue a workspace/project migration onto W4P.

The worker sets the W4P hosting target, bootstraps Cloud, installs the
composition Core already selected for each project, reconciles capabilities,
projects staff authz, rewrites the app backend adapters, syncs env, and
repairs the deploy workflow. It does not cut hostname traffic; W4P activation
stays health-gated.

Run this on the API/worker server so it uses the server Redis database and
Cloud/GitHub credentials:

  python scripts/migrate_workspace_to_w4p.py WORKSPACE_UUID --dry-run
  python scripts/migrate_workspace_to_w4p.py WORKSPACE_UUID --project PROJECT_UUID
  python scripts/migrate_workspace_to_w4p.py WORKSPACE_UUID
  python scripts/migrate_workspace_to_w4p.py --all-workspaces --dry-run
  python scripts/migrate_workspace_to_w4p.py --all-workspaces --batch-size 10 --batch-delay-seconds 30
"""
from __future__ import annotations

import argparse
import asyncio
import json
import sys
from collections.abc import Sequence
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))


async def _discover_workspace_ids() -> list[str]:
    from sqlalchemy import select

    from src.core.database import async_session_factory
    from src.domain.workspaces.models import Workspace

    async with async_session_factory() as db:
        rows = await db.execute(
            select(Workspace.id)
            .where(Workspace.deleted_at.is_(None))
            .order_by(Workspace.created_at.asc())
        )
        return [str(value) for value in rows.scalars().all()]


async def _queue_all(
    workspace_ids: Sequence[str],
    *,
    batch_size: int = 10,
    batch_delay_seconds: float = 5.0,
) -> dict:
    from src.workers.enqueue import enqueue_migrate_workspace_to_w4p

    if batch_size < 1:
        raise ValueError("batch_size must be at least 1")
    if batch_delay_seconds < 0:
        raise ValueError("batch_delay_seconds cannot be negative")

    queued: list[str] = []
    failed: list[str] = []
    for offset in range(0, len(workspace_ids), batch_size):
        batch = workspace_ids[offset : offset + batch_size]
        for workspace_id in batch:
            if await enqueue_migrate_workspace_to_w4p(workspace_id):
                queued.append(workspace_id)
            else:
                failed.append(workspace_id)
        if offset + batch_size < len(workspace_ids) and batch_delay_seconds:
            await asyncio.sleep(batch_delay_seconds)
    return {"queued": queued, "failed": failed, "count": len(workspace_ids)}


async def _all_workspace_flow(
    *,
    dry_run: bool,
    batch_size: int = 10,
    batch_delay_seconds: float = 5.0,
) -> dict | list[dict]:
    """Discover and process workspaces within one event loop.

    SQLAlchemy's async engine owns loop-bound asyncpg connections. Keeping
    discovery and inventory in one asyncio.run() avoids reusing that pool from
    a closed/different event loop on Python 3.14.
    """
    workspace_ids = await _discover_workspace_ids()
    if not dry_run:
        return await _queue_all(
            workspace_ids,
            batch_size=batch_size,
            batch_delay_seconds=batch_delay_seconds,
        )

    from src.core.database import async_session_factory
    from src.workers.tasks.migrate_workspace_to_w4p import migrate_workspace_to_w4p

    output = []
    for workspace_id in workspace_ids:
        output.append(
            await migrate_workspace_to_w4p(
                {"db_factory": async_session_factory},
                workspace_id,
                dry_run=True,
            )
        )
    return output


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("workspace_id", nargs="?")
    parser.add_argument(
        "--all-workspaces",
        action="store_true",
        help="Discover every non-deleted workspace and queue one migration per workspace.",
    )
    parser.add_argument(
        "--project",
        dest="project_ids",
        action="append",
        default=None,
        help="Restrict migration; repeat for multiple project UUIDs.",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print inventory without mutations; with --all-workspaces, print every workspace inventory.",
    )
    parser.add_argument(
        "--batch-size",
        type=int,
        default=10,
        help="With --all-workspaces, queue this many workspace jobs per batch (default: 10).",
    )
    parser.add_argument(
        "--batch-delay-seconds",
        type=float,
        default=5.0,
        help="Pause between all-workspaces queue batches (default: 5).",
    )
    args = parser.parse_args()

    if args.all_workspaces and args.workspace_id:
        parser.error("do not pass WORKSPACE_UUID with --all-workspaces")
    if not args.all_workspaces and not args.workspace_id:
        parser.error("WORKSPACE_UUID is required unless --all-workspaces is used")
    if args.all_workspaces and args.project_ids:
        parser.error("--project can only be used with one WORKSPACE_UUID")
    if args.batch_size < 1:
        parser.error("--batch-size must be at least 1")
    if args.batch_delay_seconds < 0:
        parser.error("--batch-delay-seconds cannot be negative")

    if args.all_workspaces:
        summary = asyncio.run(
            _all_workspace_flow(
                dry_run=args.dry_run,
                batch_size=args.batch_size,
                batch_delay_seconds=args.batch_delay_seconds,
            )
        )
        if args.dry_run:
            print(json.dumps(summary, indent=2, default=str))
            return
        print(json.dumps(summary, indent=2))
        raise SystemExit(0 if not summary["failed"] else 1)

    if args.dry_run:
        from src.core.database import async_session_factory
        from src.workers.tasks.migrate_workspace_to_w4p import migrate_workspace_to_w4p

        result = asyncio.run(
            migrate_workspace_to_w4p(
                {"db_factory": async_session_factory},
                args.workspace_id,
                project_ids=args.project_ids,
                dry_run=True,
            )
        )
        print(json.dumps(result, indent=2, default=str))
        return

    from src.workers.enqueue import enqueue_migrate_workspace_to_w4p

    queued = asyncio.run(
        enqueue_migrate_workspace_to_w4p(args.workspace_id, project_ids=args.project_ids)
    )
    print(json.dumps({"queued": queued, "workspace_id": args.workspace_id}, indent=2))
    raise SystemExit(0 if queued else 1)


if __name__ == "__main__":
    main()
