"""Pick the Cloudflare account a deploy runs against.

By default every deploy uses the global Cloudflare settings. A workspace listed in
``hosting_deploy_account_workspace_ids`` deploys to the account assigned to it in Cloud infra,
using that account's token, zone and W4P namespaces. Nothing else changes.
"""

from __future__ import annotations

from collections.abc import Awaitable, Callable
from typing import Any
from uuid import UUID


class DeployAccountError(RuntimeError):
    """The workspace is set to use its assigned account, but no account can be resolved."""


def workspace_uses_account_override(workspace_id: UUID | str, settings: Any) -> bool:
    listed = {w.strip().lower() for w in (settings.hosting_deploy_account_workspace_ids or "").split(",") if w.strip()}
    return str(workspace_id).lower() in listed


async def settings_for_workspace(
    workspace_id: UUID,
    settings: Any,
    resolve_credentials: Callable[[UUID], Awaitable[Any]],
) -> Any:
    """Return settings that point at the workspace's assigned account, or the settings unchanged.

    ``resolve_credentials`` returns the decrypted credentials for the workspace's account. A workspace
    in the override list with no assignment fails closed, rather than deploying to the global account.
    """
    if not workspace_uses_account_override(workspace_id, settings):
        return settings
    try:
        creds = await resolve_credentials(workspace_id)
    except Exception as exc:
        raise DeployAccountError(f"no_assigned_cloud_account:{type(exc).__name__}") from exc
    if not creds.account_id or not creds.api_token:
        raise DeployAccountError("assigned_cloud_account_missing_credentials")
    return settings.model_copy(update={
        "cloudflare_account_id": creds.account_id,
        "cloudflare_api_token": creds.api_token,
        "cloudflare_zone_id": creds.zone_id or "",
        "cloudflare_w4p_namespace_production": creds.w4p_namespace_production or settings.cloudflare_w4p_namespace_production,
        "cloudflare_w4p_namespace_preview": creds.w4p_namespace_preview or settings.cloudflare_w4p_namespace_preview,
        "cloudflare_w4p_namespace_staging": creds.w4p_namespace_staging or settings.cloudflare_w4p_namespace_staging,
        "cloudflare_dispatch_kv_namespace_id": creds.dispatch_kv_namespace_id or settings.cloudflare_dispatch_kv_namespace_id,
    })


def _cutoff(settings: Any):
    from datetime import datetime, timezone
    raw = (settings.hosting_platform_projects_created_after or "").strip()
    if not raw:
        return None
    value = datetime.fromisoformat(raw)
    return value if value.tzinfo else value.replace(tzinfo=timezone.utc)


def project_is_platform(project_created_at, settings: Any) -> bool:
    """New projects (created at or after the cutoff) deploy on the platform."""
    cutoff = _cutoff(settings)
    if cutoff is None or project_created_at is None:
        return False
    created = project_created_at if project_created_at.tzinfo else project_created_at.replace(tzinfo=cutoff.tzinfo)
    return created >= cutoff


def platform_settings(settings: Any) -> Any:
    """Settings for the platform account, from the NEW_* values in the environment."""
    if not (settings.new_cloudflare_account_id and settings.new_cloudflare_api_token):
        raise DeployAccountError("platform_account_not_configured")
    if not settings.new_hosting_public_apex:
        raise DeployAccountError("platform_apex_not_configured")
    return settings.model_copy(update={
        "cloudflare_account_id": settings.new_cloudflare_account_id,
        "cloudflare_api_token": settings.new_cloudflare_api_token,
        "cloudflare_zone_id": "",
        "hosting_public_apex": settings.new_hosting_public_apex,
        "cloudflare_dispatch_kv_namespace_id": settings.new_cloudflare_dispatch_kv_namespace_id,
        "cloudflare_w4p_namespace": settings.new_cloudflare_w4p_namespace or settings.cloudflare_w4p_namespace,
        "cloudflare_w4p_namespace_production": (
            settings.new_cloudflare_w4p_namespace_production
            or settings.new_cloudflare_w4p_namespace
            or settings.cloudflare_w4p_namespace_production
        ),
        "cloudflare_w4p_namespace_preview": (
            settings.new_cloudflare_w4p_namespace_preview
            or settings.new_cloudflare_w4p_namespace
            or settings.cloudflare_w4p_namespace_preview
        ),
        "cloudflare_w4p_namespace_staging": (
            settings.new_cloudflare_w4p_namespace_staging
            or settings.new_cloudflare_w4p_namespace
            or settings.cloudflare_w4p_namespace_staging
        ),
        "cloudflare_worker_compatibility_date": (
            settings.new_cloudflare_worker_compatibility_date
            or settings.cloudflare_worker_compatibility_date
        ),
    })


async def settings_for_project(
    workspace_id: UUID,
    project_created_at,
    settings: Any,
    resolve_credentials: Callable[[UUID], Awaitable[Any]],
) -> Any:
    """Settings a project's deploy runs with: the platform for new projects, the workspace override
    for listed workspaces, and the global settings otherwise."""
    if project_is_platform(project_created_at, settings) and settings.hosting_platform_account_enabled:
        return platform_settings(settings)
    return await settings_for_workspace(workspace_id, settings, resolve_credentials)
