"""cloud_infra domain — SQLAlchemy models for CF account pool and cloud instance routing."""
from __future__ import annotations

import enum
from datetime import datetime
from typing import Optional
from uuid import UUID, uuid4

from sqlalchemy import Boolean, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM, JSONB
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql import func
from sqlalchemy.types import DateTime

from src.core.database import Base


class CFAccountTier(str, enum.Enum):
    basic = "basic"
    pro = "pro"
    enterprise = "enterprise"


class CFAccountStatus(str, enum.Enum):
    active = "active"
    suspended = "suspended"
    full = "full"


class CloudInstanceStatus(str, enum.Enum):
    active = "active"
    unhealthy = "unhealthy"
    draining = "draining"
    offline = "offline"


class WorkspaceCloudAssignmentStatus(str, enum.Enum):
    active = "active"
    migrating = "migrating"
    released = "released"


class CloudShardType(str, enum.Enum):
    shared = "shared"
    dedicated = "dedicated"
    customer_owned = "customer_owned"


class CloudShardStatus(str, enum.Enum):
    active = "active"
    full = "full"
    draining = "draining"
    offline = "offline"


class ProjectShardStatus(str, enum.Enum):
    active = "active"
    migrating = "migrating"
    draining = "draining"


# Alembic 033_cf_account_pool.py — migrations own the PostgreSQL types.
P2HS_CF_ACCOUNT_TIER = PG_ENUM(
    CFAccountTier,
    name="p2hs_cf_account_tier",
    create_type=False,
)
P2HS_CF_ACCOUNT_STATUS = PG_ENUM(
    CFAccountStatus,
    name="p2hs_cf_account_status",
    create_type=False,
)
P2HS_CLOUD_INSTANCE_STATUS = PG_ENUM(
    CloudInstanceStatus,
    name="p2hs_cloud_instance_status",
    create_type=False,
)
P2HS_WORKSPACE_CLOUD_ASSIGNMENT_STATUS = PG_ENUM(
    WorkspaceCloudAssignmentStatus,
    name="p2hs_workspace_cloud_assignment_status",
    create_type=False,
)
P2HS_CLOUD_SHARD_TYPE = PG_ENUM(
    CloudShardType,
    name="p2hs_cloud_shard_type",
    create_type=False,
)
P2HS_CLOUD_SHARD_STATUS = PG_ENUM(
    CloudShardStatus,
    name="p2hs_cloud_shard_status",
    create_type=False,
)
P2HS_PROJECT_SHARD_STATUS = PG_ENUM(
    ProjectShardStatus,
    name="p2hs_project_shard_status",
    create_type=False,
)


class CFAccount(Base):
    """One row per Cloudflare account managed by ops.

    config_enc stores a Fernet-encrypted JSON blob of all CF credentials
    (api_token, zone_id, namespace IDs, etc.).
    """

    __tablename__ = "cf_accounts"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    account_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    account_name: Mapped[str] = mapped_column(String(120), nullable=False)
    tier: Mapped[CFAccountTier] = mapped_column(
        P2HS_CF_ACCOUNT_TIER, nullable=False, default=CFAccountTier.basic
    )
    status: Mapped[CFAccountStatus] = mapped_column(
        P2HS_CF_ACCOUNT_STATUS, nullable=False, default=CFAccountStatus.active
    )
    max_workspaces: Mapped[int] = mapped_column(Integer, nullable=False, default=500)
    workspace_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    config_enc: Mapped[Optional[str]] = mapped_column(Text, nullable=True)   # Fernet-encrypted JSON
    metadata_: Mapped[Optional[dict]] = mapped_column("metadata", JSONB, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class CloudInstance(Base):
    """One row per running P2HS Cloud gateway instance.

    Registered by the Cloud instance itself on startup via
    POST /v1/internal/cloud-instances/register (CLOUD_INTERNAL_TOKEN auth).
    internal_token_enc stores the Fernet-encrypted CLOUD_INTERNAL_TOKEN
    so Core can route control plane pushes to the correct instance.
    """

    __tablename__ = "cloud_instances"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    instance_id: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
    cf_account_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("cf_accounts.id", ondelete="RESTRICT"),
        nullable=False,
    )
    tier: Mapped[CFAccountTier] = mapped_column(
        P2HS_CF_ACCOUNT_TIER, nullable=False, default=CFAccountTier.basic
    )
    gateway_url: Mapped[str] = mapped_column(Text, nullable=False)
    internal_token_enc: Mapped[str] = mapped_column(Text, nullable=False)  # Fernet-encrypted
    status: Mapped[CloudInstanceStatus] = mapped_column(
        P2HS_CLOUD_INSTANCE_STATUS,
        nullable=False,
        default=CloudInstanceStatus.active,
    )
    last_heartbeat_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    registered_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    metadata_: Mapped[Optional[dict]] = mapped_column("metadata", JSONB, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class WorkspaceCloudAssignment(Base):
    """Maps a workspace to the Cloud instance that serves it.

    One active assignment per workspace. Auto-assigned by Core when the workspace
    first makes a Cloud request; can be manually re-assigned by ops.
    """

    __tablename__ = "workspace_cloud_assignment"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    workspace_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("workspaces.id", ondelete="CASCADE"),
        nullable=False,
    )
    cloud_instance_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("cloud_instances.id", ondelete="RESTRICT"),
        nullable=False,
    )
    status: Mapped[WorkspaceCloudAssignmentStatus] = mapped_column(
        P2HS_WORKSPACE_CLOUD_ASSIGNMENT_STATUS,
        nullable=False,
        default=WorkspaceCloudAssignmentStatus.active,
    )
    assigned_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    released_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (
        UniqueConstraint("workspace_id", name="uq_workspace_cloud_assignment_workspace"),
    )


class CloudShard(Base):
    """One row per D1 database shard.

    Every shard is owned by a specific CF account and contains the D1 database,
    R2 bucket, and dispatch namespace for all projects assigned to it.
    Shared shards serve Basic/Pro; dedicated/customer_owned serve Enterprise.
    """

    __tablename__ = "cloud_shards"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    shard_key: Mapped[str] = mapped_column(Text, nullable=False, unique=True)
    region: Mapped[str] = mapped_column(Text, nullable=False, default="auto")
    shard_type: Mapped[CloudShardType] = mapped_column(
        P2HS_CLOUD_SHARD_TYPE, nullable=False
    )
    cf_account_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("cf_accounts.id", ondelete="RESTRICT"),
        nullable=False,
    )
    d1_database_id: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    r2_bucket_name: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    dispatch_namespace: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    # Ingress routing — internal only, never exposed to generated apps
    gateway_base_url: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    worker_name: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    d1_database_name: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    status: Mapped[CloudShardStatus] = mapped_column(
        P2HS_CLOUD_SHARD_STATUS, nullable=False, default=CloudShardStatus.active
    )
    is_accepting: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    max_projects: Mapped[int] = mapped_column(Integer, nullable=False, default=10000)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class ProjectCloudAssignment(Base):
    """Maps a project to the shard (D1 database) that holds its Cloud data.

    One active assignment per project. Auto-assigned to the default shared shard
    at project bootstrap; can be migrated to an enterprise shard by ops.
    """

    __tablename__ = "project_cloud_assignments"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    project_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("projects.id", ondelete="CASCADE"),
        nullable=False,
    )
    shard_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("cloud_shards.id", ondelete="RESTRICT"),
        nullable=False,
    )
    status: Mapped[ProjectShardStatus] = mapped_column(
        P2HS_PROJECT_SHARD_STATUS, nullable=False, default=ProjectShardStatus.active
    )
    assigned_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )

    __table_args__ = (
        UniqueConstraint("project_id", name="uq_project_cloud_assignments_project"),
    )


class CloudShardMigrationStatus(Base):
    """Fleet-wide D1 schema version tracking per shard.

    Each shard's gateway reports its applied migration version after
    wrangler d1 migrations apply completes. Core alerts when any shard lags
    the expected head migration_version (anti-drift guard).

    status: pending | applied | failed | rolling_back
    """

    __tablename__ = "cloud_shard_migration_status"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    shard_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("cloud_shards.id", ondelete="CASCADE"),
        nullable=False,
    )
    migration_version: Mapped[str] = mapped_column(Text, nullable=False)
    applied_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    status: Mapped[str] = mapped_column(Text, nullable=False, default="applied")
    error: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    reported_by: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    __table_args__ = (
        UniqueConstraint(
            "shard_id", "migration_version",
            name="uq_cloud_shard_migration_shard_version",
        ),
    )
