"""Persisted hosting migration, separate from the serving provider.

``primary_hosting_provider`` is what ordinary deploys read only when no
migration is open, or after that migration allows the destination. An open or
failed migration keeps execution on the recorded source. The cohort may still
store the destination early so its own repair steps can prepare W4P; that
write is not activation.
"""
from __future__ import annotations

from datetime import UTC, datetime
from typing import Any
from uuid import uuid4

from src.domain.hosting.profile import canonical_provider_id
from src.domain.hosting.project_provider import project_hosting_provider

_OPEN = "in_progress"
_PREPARED = "destination_prepared"
_FAILED = "failed"
_COMPLETED = "completed"


def migration_evidence(stage: str, *, ok: bool, detail: str = "") -> dict[str, Any]:
    return {
        "stage": stage,
        "ok": ok,
        "detail": detail[:500],
        "at": datetime.now(UTC).replace(microsecond=0).isoformat(),
    }


def _copy(state: dict[str, Any] | None) -> dict[str, Any]:
    current = dict(state or {})
    current["evidence"] = list(current.get("evidence") or [])
    current["rollback"] = dict(current.get("rollback") or {})
    return current


def begin_or_resume_cohort(
    existing: dict[str, Any] | None,
    *,
    source: str,
    target: str,
    rollback: dict[str, Any],
) -> dict[str, Any]:
    """Open a cohort operation, or continue the same one.

    A verified cutover is not reopened. A failed operation keeps its id and
    starts another attempt without inventing a new source provider.
    """

    source_id = canonical_provider_id(source)
    target_id = canonical_provider_id(target)
    state = _copy(existing)
    same_target = (
        state.get("operation_id")
        and canonical_provider_id(str(state.get("target") or "")) == target_id
    )
    if same_target and state.get("status") == _COMPLETED:
        state["evidence"].append(migration_evidence(
            str(state.get("stage") or "complete"),
            ok=True,
            detail="idempotent_retry",
        ))
        return state
    if same_target and state.get("status") == _PREPARED:
        state["evidence"].append(
            migration_evidence("deploy_destination", ok=True, detail="idempotent_retry")
        )
        return state
    if same_target and state.get("status") in {_OPEN, _FAILED}:
        state["status"] = _OPEN
        state["stage"] = "discover"
        state["allow_destination_deploy"] = False
        state["cutover_verified"] = False
        state["evidence"].append(
            migration_evidence("discover", ok=True, detail="resumed")
        )
        return state
    return {
        "operation_id": str(uuid4()),
        "engine": "cohort_w4p",
        "source": source_id,
        "target": target_id,
        "previous_provider": source_id,
        "stage": "discover",
        "status": _OPEN,
        "cutover_verified": False,
        "allow_destination_deploy": False,
        "rollback": {
            "provider": source_id,
            "live_hostname": rollback.get("live_hostname"),
            "preview_hostname": rollback.get("preview_hostname"),
        },
        "evidence": [migration_evidence("discover", ok=True, detail="recorded_previous_provider")],
    }


def note_stage(
    state: dict[str, Any] | None,
    stage: str,
    *,
    ok: bool = True,
    detail: str = "",
) -> dict[str, Any]:
    current = _copy(state)
    if not current.get("operation_id"):
        return current
    current["stage"] = stage
    current["evidence"].append(migration_evidence(stage, ok=ok, detail=detail))
    current["evidence"] = current["evidence"][-50:]
    return current


def allow_destination_deploy(state: dict[str, Any] | None) -> dict[str, Any]:
    """Repairs finished. Destination deploys may start. Traffic is not cut over."""

    current = _copy(state)
    if current.get("status") in {_COMPLETED, _PREPARED} or current.get("cutover_verified"):
        return current
    current["allow_destination_deploy"] = True
    current["stage"] = "deploy_destination"
    current["evidence"].append(
        migration_evidence("deploy_destination", ok=True, detail="repairs_ready")
    )
    current["evidence"] = current["evidence"][-50:]
    return current


def mark_destination_prepared(state: dict[str, Any] | None) -> dict[str, Any]:
    current = _copy(state)
    if not current.get("operation_id"):
        return current
    if current.get("status") == _COMPLETED or current.get("cutover_verified"):
        return current
    current["status"] = _PREPARED
    current["stage"] = "deploy_destination"
    current["allow_destination_deploy"] = True
    current["cutover_verified"] = False
    current["evidence"].append(
        migration_evidence(
            "deploy_destination",
            ok=True,
            detail="repairs_finished_cutover_not_verified",
        )
    )
    current["evidence"] = current["evidence"][-50:]
    return current


def mark_migration_failed(
    state: dict[str, Any] | None,
    stage: str,
    error: str,
) -> dict[str, Any]:
    """Record the failure. Do not change the persisted serving provider here."""

    current = _copy(state)
    if not current.get("operation_id"):
        return current
    if current.get("cutover_verified") or current.get("status") == _COMPLETED:
        current["evidence"].append(
            migration_evidence(stage, ok=False, detail=f"retry_error_after_cutover:{error}")
        )
        current["evidence"] = current["evidence"][-50:]
        return current
    current["status"] = _FAILED
    current["stage"] = stage
    current["allow_destination_deploy"] = False
    current["cutover_verified"] = False
    current["evidence"].append(migration_evidence(stage, ok=False, detail=error))
    current["evidence"] = current["evidence"][-50:]
    return current


def mark_cutover_verified(
    state: dict[str, Any] | None,
    *,
    deploy_id: str,
) -> dict[str, Any] | None:
    """Activation proved the destination is serving. This is the commit point."""

    if not isinstance(state, dict) or not state.get("operation_id"):
        return None
    current = _copy(state)
    current["cutover_verified"] = True
    current["allow_destination_deploy"] = True
    current["status"] = _COMPLETED
    current["stage"] = "complete"
    current["committed_provider"] = current.get("target")
    current["evidence"].append(migration_evidence(
        "commit_active_provider",
        ok=True,
        detail=f"activation_verified:{deploy_id}",
    ))
    current["evidence"] = current["evidence"][-50:]
    return current


def destination_deploy_allowed(state: dict[str, Any] | None) -> bool:
    """No migration means today's provider selection. An open repair does not."""

    if not state:
        return True
    if state.get("cutover_verified") or state.get("status") in {_PREPARED, _COMPLETED}:
        return True
    return bool(state.get("allow_destination_deploy")) and state.get("status") == _OPEN


def route_activation_allowed(state: dict[str, Any] | None) -> bool:
    return destination_deploy_allowed(state)


def artifact_provider_id(
    primary_hosting_provider: str | None,
    state: dict[str, Any] | None,
) -> str | None:
    """Runtime files follow the destination only after repairs are allowed to deploy it."""

    if destination_deploy_allowed(state) and state and state.get("target"):
        return canonical_provider_id(str(state.get("target")))
    if state and not destination_deploy_allowed(state) and state.get("source"):
        return canonical_provider_id(str(state.get("source")))
    return primary_hosting_provider


def resolve_execution_provider(
    project_created_at,
    settings: Any,
    *,
    primary_hosting_provider: str | None = None,
    hosting_migration: dict[str, Any] | None = None,
    assume_unassigned_w4p: bool = False,
    has_successful_pages_deployment: bool | None = None,
) -> str:
    """Where the next ordinary deploy goes.

    A failed or still-preparing migration uses the recorded source even when
    ``primary_hosting_provider`` was already set to the destination.
    """

    if hosting_migration and not destination_deploy_allowed(hosting_migration):
        source = hosting_migration.get("source") or hosting_migration.get("previous_provider")
        return canonical_provider_id(str(source or "cloudflare_pages"))
    return project_hosting_provider(
        project_created_at,
        settings,
        primary_hosting_provider=primary_hosting_provider,
        assume_unassigned_w4p=assume_unassigned_w4p,
        has_successful_pages_deployment=has_successful_pages_deployment,
    )
