"""Run one Core codegen job with a selected repo CLI, regardless of ``AI_PROVIDER``.

Example (PowerShell)::

    python scripts/verify_codex_cli_codegen.py --execute `
      --workspace-id <workspace-uuid> `
      --project-id <project-uuid> `
      --prompt "Add a small settings page with a name field and save button"

The provider override is stored only on this run. It does not modify ``.env``
and other queued runs continue to use the configured provider (normally Cursor).
Codex CLI authentication is taken from its normal login, or ``CODEX_API_KEY``.
On a server with a restricted service ``PATH``, set ``CODEX_CLI_PATH`` to the
full executable path (often ``$HOME/.local/bin/codex``).
"""

from __future__ import annotations

import argparse
import asyncio
import shutil
import sys
import time
from pathlib import Path
from uuid import UUID

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

from src.config import AIProvider, get_settings  # noqa: E402
from src.core.database import async_session_factory  # noqa: E402
from src.core.orm_import import import_all_orm_models  # noqa: E402
from src.domain.codegen.agent_facade import AgentCodegenFacade  # noqa: E402
from src.domain.projects.repository import ProjectRepository  # noqa: E402
from src.integrations.codex_cli import resolve_codex_cli_path  # noqa: E402
from src.workers.enqueue import enqueue_codegen_run  # noqa: E402

import_all_orm_models()


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--execute", action="store_true", help="Actually enqueue the run.")
    parser.add_argument("--provider", choices=("codex", "claude"), default="codex")
    parser.add_argument("--workspace-id", required=True, help="Workspace UUID owning the project.")
    parser.add_argument("--project-id", required=True, help="Project UUID to modify.")
    parser.add_argument("--prompt", required=True, help="Test prompt sent to Codex CLI.")
    parser.add_argument("--timeout-seconds", type=int, default=1800)
    parser.add_argument("--poll-seconds", type=float, default=5.0)
    return parser.parse_args()


async def create_run(workspace_id: UUID, project_id: UUID, prompt: str, provider: str):
    async with async_session_factory() as db:
        project = await ProjectRepository(db).get_with_repo(project_id)
        if not project:
            raise RuntimeError(f"project_not_found:{project_id}")
        if project.workspace_id != workspace_id:
            raise RuntimeError(f"project_not_in_workspace:{project_id}")
        run = await AgentCodegenFacade(db).service.create_run(
            project_id=project_id,
            user_prompt=prompt,
            created_by=getattr(project, "created_by", None),
            supersede_active=True,
            provider_override=provider,
        )
        await db.commit()
        return run.id


async def poll_run(run_id, timeout: int, interval: float):
    from src.domain.codegen.repository import CodegenRunRepository

    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        async with async_session_factory() as db:
            run = await CodegenRunRepository(db).get_by_id(run_id)
            if run and run.status in {
                "completed", "completed_with_limitations", "completed_with_l3",
                "failed", "blocked", "canceled", "user_input_required", "awaiting_user_input",
            }:
                return run
        await asyncio.sleep(interval)
    raise TimeoutError(f"codegen_timeout:{run_id}")


async def main(args: argparse.Namespace) -> int:
    settings = get_settings()
    if not args.execute:
        print("overall = fail\nmissing_prerequisite = rerun with --execute")
        return 2
    if not settings.database_url or not settings.redis_url:
        print("overall = fail\nmissing_prerequisite = DATABASE_URL and REDIS_URL are required")
        return 2
    cli_path = (
        resolve_codex_cli_path(settings.codex_cli_path)
        if args.provider == "codex"
        else shutil.which("claude")
    )
    if cli_path is None:
        print(
            "overall = fail\n"
            f"missing_prerequisite = {args.provider} CLI not found. "
            f"Set {'CODEX_CLI_PATH' if args.provider == 'codex' else 'CLAUDE_CLI_PATH'} to the full executable path."
        )
        return 2
    workspace_id = UUID(args.workspace_id)
    project_id = UUID(args.project_id)
    run_id = await create_run(workspace_id, project_id, args.prompt, args.provider)
    print(f"provider_override = {args.provider}\nrun_id = {run_id}")
    if not await enqueue_codegen_run(str(run_id), project_id=str(project_id)):
        print("overall = fail\nerror = enqueue_failed")
        return 1
    run = await poll_run(run_id, args.timeout_seconds, args.poll_seconds)
    print(f"effective_provider = {args.provider}\nstatus = {run.status}")
    if run.error_message:
        print(f"error = {run.error_message}")
    return 0 if run.status in {"completed", "completed_with_limitations", "completed_with_l3"} else 1


if __name__ == "__main__":
    try:
        raise SystemExit(asyncio.run(main(parse_args())))
    except (ValueError, RuntimeError, TimeoutError) as exc:
        print(f"overall = fail\nerror = {type(exc).__name__}: {exc}")
        raise SystemExit(1)
