import inspect
from pathlib import Path

import pytest
from src.domain.capabilities.operator_reconciliation import composition_repair_plan
from src.workers.tasks.migrate_runtime_primitives import migrate_runtime_primitives
from src.workers.tasks.migrate_workspace_to_w4p import migrate_workspace_to_w4p


@pytest.mark.asyncio
async def test_all_workspace_queue_is_batched(monkeypatch):
    from scripts.migrate_workspace_to_w4p import _queue_all

    queued: list[str] = []

    async def fake_enqueue(workspace_id: str):
        queued.append(workspace_id)
        return True

    monkeypatch.setattr(
        "src.workers.enqueue.enqueue_migrate_workspace_to_w4p",
        fake_enqueue,
    )

    result = await _queue_all(
        ["w1", "w2", "w3", "w4", "w5"],
        batch_size=2,
        batch_delay_seconds=0,
    )

    assert result == {"queued": ["w1", "w2", "w3", "w4", "w5"], "failed": [], "count": 5}
    assert queued == ["w1", "w2", "w3", "w4", "w5"]


def test_routing_kv_get_uses_one_raw_request():
    from pathlib import Path

    source = Path("src/domain/hosting/routing_plane.py").read_text(encoding="utf-8")
    body = source.split("async def _kv_get", 1)[1].split("async def _kv_put", 1)[0]
    assert body.count("await client.get(") == 1
    assert "await self._request(" not in body


def test_composition_repair_reinstalls_missing_blocked_and_stale_desired_packs():
    plan = composition_repair_plan(
        {
            "desired_feature_packs": ["support", "billing-portal"],
            "missing_packs": ["support"],
            "blocked_installations": [],
            "version_mismatches": ["billing-portal"],
            "composition_errors": [],
        }
    )
    assert [action["pack_slug"] for action in plan["actions"]] == [
        "billing-portal",
        "support",
    ]


def test_composition_repair_does_not_install_outside_the_desired_set():
    plan = composition_repair_plan(
        {
            "desired_feature_packs": ["support"],
            "missing_packs": ["support", "labs"],
            "blocked_installations": ["logistics"],
            "version_mismatches": ["education"],
            "composition_errors": [],
        }
    )
    assert [action["pack_slug"] for action in plan["actions"]] == ["support"]


def test_composition_repair_stops_when_the_composition_is_invalid():
    plan = composition_repair_plan(
        {
            "desired_feature_packs": ["support"],
            "missing_packs": ["support"],
            "composition_errors": ["generation_plan_composition_invalid"],
        }
    )
    assert plan["actions"] == []


def test_workspace_migration_applies_composition_and_backend_after_cloud_bootstrap():
    src = inspect.getsource(migrate_workspace_to_w4p)
    body = src.split("Persist the target before downstream tasks", 1)[1]
    order = [
        "primary_hosting_provider",
        "sync_project_to_cloud",
        "_apply_project_composition",
        "_provision_project_authz",
        "reconcile_project_capabilities",
        "migrate_runtime_primitives",
        "sync_project_env_vars",
        "fix_deploy_workflows",
    ]
    positions = [body.index(name) for name in order]
    assert positions == sorted(positions)
    workflows = body[body.index("fix_deploy_workflows"):]
    assert "sync_runtime_adapters=True" in workflows


def test_runtime_adapter_sync_promotes_the_repaired_preview():
    source = (
        Path(__file__).resolve().parents[1]
        / "src/workers/tasks/fix_deploy_workflows.py"
    ).read_text(encoding="utf-8")
    window = source.split("already pushed the named port contract", 1)[1].split("continue", 1)[0]
    assert "enqueue_merge_and_deploy" in window
    assert "if promote_after_preview" not in window


def test_workflow_repair_does_not_push_main_on_its_own():
    source = (
        Path(__file__).resolve().parents[1]
        / "src/workers/tasks/fix_deploy_workflows.py"
    ).read_text(encoding="utf-8")
    repair = source.split("# ── Main branch", 1)[1]
    assert "push_repo(repo_path, default_branch" not in repair
    assert "enqueue_merge_and_deploy" in repair


def test_runtime_migration_writes_composition_before_port_adapters():
    src = inspect.getsource(migrate_runtime_primitives)
    body = src.split('logger.info("migrate_runtime_primitives_start"', 1)[1]
    compose = body.index("_write_project_composition")
    adapters = body.index("install_catalog_adapters")
    push = body.index("push_repo")
    assert compose < adapters < push
