"""Hosting migration state, generic engine, and profile-shaped runtime files."""
from __future__ import annotations

from types import SimpleNamespace

from src.domain.codegen.port_adapter import runtime_files_for_host
from src.domain.hosting.execution import (
    get_execution_adapter,
    register_execution_adapter,
    unregister_execution_adapter,
    uses_worker_ci,
)
from src.domain.hosting.migration_engine import execute_hosting_migration
from src.domain.hosting.migration_state import (
    begin_or_resume_cohort,
    destination_deploy_allowed,
    mark_cutover_verified,
    mark_destination_prepared,
    mark_migration_failed,
    resolve_execution_provider,
)
from src.domain.hosting.profile import (
    ApplicationHostingRequirements,
    HostingProfile,
    register_hosting_profile,
    unregister_hosting_profile,
)
from src.domain.hosting.project_provider import W4P_STATIC
from src.domain.hosting.routing_ops import (
    register_routing_adapter,
    unregister_routing_adapter,
)


def _settings():
    return SimpleNamespace(hosting_provider="cloudflare_pages")


def test_failed_migration_keeps_execution_on_the_recorded_source():
    state = begin_or_resume_cohort(
        None,
        source="cloudflare_pages",
        target=W4P_STATIC,
        rollback={"live_hostname": "app.example", "preview_hostname": "preview.example"},
    )
    failed = mark_migration_failed(
        state, "runtime_primitives", "runtime_primitives_migration_failed"
    )
    assert failed["status"] == "failed"
    assert failed["allow_destination_deploy"] is False
    assert failed["cutover_verified"] is False
    assert failed["rollback"]["live_hostname"] == "app.example"
    assert destination_deploy_allowed(failed) is False
    selected = resolve_execution_provider(
        None,
        _settings(),
        primary_hosting_provider=W4P_STATIC,
        hosting_migration=failed,
    )
    assert selected == "cloudflare_pages"
    assert uses_worker_ci(selected) is False


def test_prepared_destination_is_not_a_verified_cutover():
    state = mark_destination_prepared(
        begin_or_resume_cohort(None, source="cloudflare_pages", target=W4P_STATIC, rollback={})
    )
    assert state["status"] == "destination_prepared"
    assert state["cutover_verified"] is False
    assert state.get("committed_provider") is None
    assert destination_deploy_allowed(state) is True
    resumed = begin_or_resume_cohort(
        state, source="cloudflare_pages", target=W4P_STATIC, rollback={}
    )
    assert resumed["operation_id"] == state["operation_id"]
    assert resumed["status"] == "destination_prepared"


def test_activation_is_the_only_commit_point():
    prepared = mark_destination_prepared(
        begin_or_resume_cohort(None, source="cloudflare_pages", target=W4P_STATIC, rollback={})
    )
    verified = mark_cutover_verified(prepared, deploy_id="dep_1")
    assert verified is not None
    assert verified["cutover_verified"] is True
    assert verified["status"] == "completed"
    assert verified["committed_provider"] == W4P_STATIC
    retry = mark_migration_failed(verified, "workflows", "later repair failed")
    assert retry["status"] == "completed"
    assert retry["cutover_verified"] is True


def test_resume_after_failure_keeps_the_operation_id():
    failed = mark_migration_failed(
        begin_or_resume_cohort(None, source="cloudflare", target="w4p", rollback={}),
        "workflows",
        "deploy_workflow_repair_failed",
    )
    resumed = begin_or_resume_cohort(
        failed, source="cloudflare_pages", target=W4P_STATIC, rollback={}
    )
    assert resumed["operation_id"] == failed["operation_id"]
    assert resumed["status"] == "in_progress"
    assert resumed["source"] == "cloudflare_pages"
    assert destination_deploy_allowed(resumed) is False


def test_pages_functions_are_omitted_when_the_host_has_no_function_surface():
    files = {
        "functions/api/contact.js": "export const onRequest = () => {}\n",
        "server/utils/p2hs-port-contract.mjs": "export const platformPortContract = {}\n",
        "p2hs/platform-ports.lock.json": "{}\n",
    }
    kept = runtime_files_for_host(files, "none")
    assert "functions/api/contact.js" not in kept
    assert "server/utils/p2hs-port-contract.mjs" in kept
    assert runtime_files_for_host(files, "worker_bundle") == files
    assert runtime_files_for_host(files, "pages_functions") == files


def test_unbound_live_deploy_does_not_change_the_active_provider():
    run = execute_hosting_migration(
        source="cloudflare_pages",
        target=W4P_STATIC,
        requirements=ApplicationHostingRequirements("react_app"),
        active_provider="cloudflare_pages",
    )
    assert run.committed is False
    assert run.rolled_back is False
    assert run.active_provider == "cloudflare_pages"
    assert run.state["status"] == "failed"
    assert run.state["stage"] == "deploy_destination"
    assert run.state["cutover_verified"] is False


def _register_fake() -> None:
    register_hosting_profile(HostingProfile(
        provider_id="fake_static",
        profile_version=1,
        lifecycle="supported",
        migration_source=True,
        migration_target=True,
        supported_frameworks=frozenset({"html", "react_app", "nuxt_app"}),
        static_assets=True,
        ssr_supported=False,
        server_function_surface="none",
        bff_strategy="static_bff",
        execution_mode="static",
        runtime_restrictions=(),
        build_outputs={"html": ("", "")},
        secret_bindings=(),
        preview_supported=True,
        routing_strategy="fake_dns",
        ssl_lifecycle="provider_managed",
        health_check="fake_health",
        rollback_supported=True,
        workflow_id="fake_static_workflow",
        projection_id="fake_projection",
        migration_requirements=("preview_before_cutover",),
    ))


def _clear_fake() -> None:
    unregister_hosting_profile("fake_static")
    unregister_execution_adapter("fake_static")
    unregister_routing_adapter("fake_static")


class _FakeExecution:
    provider_id = "fake_static"

    def __init__(self, fail_production: bool = False) -> None:
        self.fail_production = fail_production
        self.ready_calls = 0
        self.calls: list[str] = []

    def plan_build(self, framework: str) -> dict:
        del framework
        self.calls.append("build")
        return {"ok": True, "server_function_surface": "none"}

    def prepare(self, context: dict) -> dict:
        del context
        self.calls.append("prepare")
        return {"ok": True, "detail": "prepared"}

    def deploy(self, context: dict) -> dict:
        del context
        self.calls.append("deploy")
        return {"ok": True}

    def readiness(self, context: dict) -> dict:
        del context
        self.calls.append("readiness")
        self.ready_calls += 1
        if self.fail_production and self.ready_calls > 1:
            return {"ok": False, "detail": "production_unverified"}
        return {"ok": True}

    def rollback(self, context: dict) -> dict:
        del context
        self.calls.append("rollback_exec")
        return {"ok": True}


class _FakeRouting:
    provider_id = "fake_static"

    def __init__(self) -> None:
        self.calls: list[str] = []

    def prepare(self, context: dict) -> dict:
        del context
        self.calls.append("prepare")
        return {"ok": True, "routing_strategy": "fake_dns"}

    def activate(self, context: dict) -> dict:
        del context
        self.calls.append("activate")
        return {"ok": True}

    def rollback(self, context: dict) -> dict:
        del context
        self.calls.append("rollback")
        return {"ok": True}


def test_fake_provider_migration_commits_only_after_verified_cutover():
    execution = _FakeExecution()
    routing = _FakeRouting()
    _register_fake()
    register_execution_adapter("fake_static", execution)
    register_routing_adapter("fake_static", routing)
    try:
        run = execute_hosting_migration(
            source="cloudflare_pages",
            target="fake_static",
            requirements=ApplicationHostingRequirements(
                "html",
                requires_server_functions=False,
                requires_bff=False,
            ),
            active_provider="cloudflare_pages",
        )
    finally:
        _clear_fake()
    assert run.committed is True
    assert run.active_provider == "fake_static"
    assert run.state["committed_provider"] == "fake_static"
    assert run.state["cutover_verified"] is True
    assert execution.calls == ["prepare", "build", "deploy", "readiness", "readiness"]
    assert routing.calls == ["prepare", "activate"]
    assert get_execution_adapter("cloudflare_pages").provider_id == "cloudflare_pages"


def test_failed_production_verification_rolls_back_without_committing():
    execution = _FakeExecution(fail_production=True)
    routing = _FakeRouting()
    _register_fake()
    register_execution_adapter("fake_static", execution)
    register_routing_adapter("fake_static", routing)
    try:
        run = execute_hosting_migration(
            source=W4P_STATIC,
            target="fake_static",
            requirements=ApplicationHostingRequirements(
                "html",
                requires_server_functions=False,
                requires_bff=False,
            ),
            active_provider=W4P_STATIC,
        )
    finally:
        _clear_fake()
    assert run.committed is False
    assert run.rolled_back is True
    assert run.active_provider == W4P_STATIC
    assert run.state["cutover_verified"] is False
    assert "rollback" in routing.calls
    assert "rollback_exec" in execution.calls
