"""cloud_infra domain — request/response schemas."""
from __future__ import annotations

from datetime import datetime
from typing import Optional
from uuid import UUID

from pydantic import BaseModel, Field, field_validator

from src.domain.cloud_infra.models import (
    CFAccountStatus,
    CFAccountTier,
    CloudInstanceStatus,
    CloudShardStatus,
    CloudShardType,
    ProjectShardStatus,
    WorkspaceCloudAssignmentStatus,
)


# ---------------------------------------------------------------------------
# CF Accounts
# ---------------------------------------------------------------------------


class CFAccountCredentials(BaseModel):
    """All per-account Cloudflare credentials. Stored encrypted as a single JSON blob."""

    api_token: str
    zone_id: str = ""
    w4p_namespace_production: str = ""
    w4p_namespace_preview: str = ""
    w4p_namespace_staging: str = ""
    dispatch_kv_namespace_id: str = ""
    w4p_trusted_workers: bool = False
    worker_compatibility_date: str = "2026-03-25"


class CreateCFAccountRequest(BaseModel):
    account_id: str = Field(..., max_length=64)
    account_name: str = Field(..., max_length=120)
    tier: CFAccountTier = CFAccountTier.basic
    max_workspaces: int = Field(500, ge=1)
    credentials: CFAccountCredentials
    metadata: Optional[dict] = None


class UpdateCFAccountRequest(BaseModel):
    account_name: Optional[str] = Field(None, max_length=120)
    tier: Optional[CFAccountTier] = None
    status: Optional[CFAccountStatus] = None
    max_workspaces: Optional[int] = Field(None, ge=1)
    credentials: Optional[CFAccountCredentials] = None
    metadata: Optional[dict] = None


class CFAccountResponse(BaseModel):
    id: UUID
    account_id: str
    account_name: str
    tier: CFAccountTier
    status: CFAccountStatus
    max_workspaces: int
    workspace_count: int
    credentials_configured: bool
    created_at: datetime
    updated_at: datetime
    metadata: Optional[dict] = None

    model_config = {"from_attributes": True}


# ---------------------------------------------------------------------------
# Cloud Instances
# ---------------------------------------------------------------------------


class RegisterCloudInstanceRequest(BaseModel):
    """Payload sent by a Cloud instance on startup to register itself with Core."""

    instance_id: str = Field(..., max_length=128)
    tier: CFAccountTier
    gateway_url: str
    internal_token: Optional[str] = Field(
        None,
        description="Plain-text CLOUD_INTERNAL_TOKEN — stored encrypted. Required on first register.",
    )
    cf_account_id: Optional[UUID] = Field(
        None, description="Explicit CF account assignment; auto-resolved if omitted"
    )
    metadata: Optional[dict] = None

    @field_validator("tier", mode="before")
    @classmethod
    def normalize_instance_tier(cls, value: object) -> object:
        if isinstance(value, str) and value.strip().lower() in {"shared", "free"}:
            return CFAccountTier.basic
        return value


class UpdateCloudInstanceRequest(BaseModel):
    gateway_url: Optional[str] = None
    status: Optional[CloudInstanceStatus] = None
    metadata: Optional[dict] = None


class CloudInstanceResponse(BaseModel):
    id: UUID
    instance_id: str
    cf_account_id: UUID
    tier: CFAccountTier
    gateway_url: str
    status: CloudInstanceStatus
    last_heartbeat_at: Optional[datetime] = None
    registered_at: datetime
    metadata: Optional[dict] = None

    model_config = {"from_attributes": True}


class CloudInstanceHeartbeatResponse(BaseModel):
    ok: bool
    instance_id: str


# ---------------------------------------------------------------------------
# Workspace Cloud Assignment
# ---------------------------------------------------------------------------


class AssignWorkspaceRequest(BaseModel):
    """Manually assign a workspace to a specific Cloud instance (admin override)."""

    cloud_instance_id: UUID


class WorkspaceCloudAssignmentResponse(BaseModel):
    id: UUID
    workspace_id: UUID
    cloud_instance_id: UUID
    status: WorkspaceCloudAssignmentStatus
    assigned_at: datetime
    released_at: Optional[datetime] = None

    model_config = {"from_attributes": True}


# ---------------------------------------------------------------------------
# Routing lookup (internal use — no HTTP endpoint needed, used in-process)
# ---------------------------------------------------------------------------


class WorkspaceRoutingInfo(BaseModel):
    """Resolved gateway URL and decrypted token for a workspace's Cloud instance."""

    workspace_id: UUID
    cloud_instance_id: UUID
    gateway_url: str
    internal_token: str


# ---------------------------------------------------------------------------
# Cloud Shards
# ---------------------------------------------------------------------------


class CreateCloudShardRequest(BaseModel):
    shard_key: str = Field(..., max_length=120, description="Human-readable unique key, e.g. 'shared-us-east-1'")
    region: str = Field("auto", max_length=64)
    shard_type: CloudShardType
    cf_account_id: UUID
    d1_database_id: Optional[str] = Field(None, max_length=128)
    r2_bucket_name: Optional[str] = Field(None, max_length=256)
    dispatch_namespace: Optional[str] = Field(None, max_length=256)
    # Ingress routing metadata (internal/ops only)
    gateway_base_url: Optional[str] = Field(None, description="Internal URL of this shard's pc-gateway deployment")
    worker_name: Optional[str] = Field(None, max_length=128)
    d1_database_name: Optional[str] = Field(None, max_length=128)
    max_projects: int = Field(10000, ge=1)


class UpdateCloudShardRequest(BaseModel):
    region: Optional[str] = Field(None, max_length=64)
    cf_account_id: Optional[UUID] = None
    d1_database_id: Optional[str] = Field(None, max_length=128)
    r2_bucket_name: Optional[str] = Field(None, max_length=256)
    dispatch_namespace: Optional[str] = Field(None, max_length=256)
    gateway_base_url: Optional[str] = None
    worker_name: Optional[str] = Field(None, max_length=128)
    d1_database_name: Optional[str] = Field(None, max_length=128)
    status: Optional[CloudShardStatus] = None
    is_accepting: Optional[bool] = None
    max_projects: Optional[int] = Field(None, ge=1)


class CloudShardResponse(BaseModel):
    id: UUID
    shard_key: str
    region: str
    shard_type: CloudShardType
    cf_account_id: UUID
    d1_database_id: Optional[str] = None
    r2_bucket_name: Optional[str] = None
    dispatch_namespace: Optional[str] = None
    # Ingress routing (internal/ops — never forwarded to generated apps)
    gateway_base_url: Optional[str] = None
    worker_name: Optional[str] = None
    d1_database_name: Optional[str] = None
    status: CloudShardStatus
    is_accepting: bool
    max_projects: int
    created_at: datetime
    updated_at: datetime

    model_config = {"from_attributes": True}


# ---------------------------------------------------------------------------
# Shard Migration Status
# ---------------------------------------------------------------------------


class ReportShardMigrationRequest(BaseModel):
    """Payload sent by a shard's gateway after wrangler d1 migrations apply."""

    shard_id: UUID
    migration_version: str = Field(..., description="Migration bundle name, e.g. '0011_query_import'")
    status: str = Field("applied", description="applied | failed | rolling_back")
    error: Optional[str] = None
    reported_by: Optional[str] = Field(None, description="Gateway worker instance or deploy job ID")


class CloudShardMigrationStatusResponse(BaseModel):
    id: UUID
    shard_id: UUID
    migration_version: str
    applied_at: datetime
    status: str
    error: Optional[str] = None
    reported_by: Optional[str] = None
    created_at: datetime
    updated_at: datetime

    model_config = {"from_attributes": True}


class ShardFleetMigrationRow(BaseModel):
    """One row per shard showing its latest reported migration version."""

    shard_id: UUID
    shard_key: str
    latest_migration_version: Optional[str] = None
    latest_status: Optional[str] = None
    applied_at: Optional[datetime] = None
    lags_head: bool = False  # True when shard is behind the expected fleet head version


# ---------------------------------------------------------------------------
# Project Cloud Assignments
# ---------------------------------------------------------------------------


class AssignProjectToShardRequest(BaseModel):
    shard_id: UUID


class ProjectCloudAssignmentResponse(BaseModel):
    id: UUID
    project_id: UUID
    shard_id: UUID
    status: ProjectShardStatus
    assigned_at: datetime
    shard: Optional[CloudShardResponse] = None

    model_config = {"from_attributes": True}
