"""edge_functions domain — SQLAlchemy models."""
from __future__ import annotations

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

from sqlalchemy import BigInteger, Boolean, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import 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 FunctionLibrary(Base):
    """Platform pre-built function registry.

    Source of truth is repo seed files (seeds/function_library/*.yaml).
    Ops console controls status/visibility overrides.

    status:     draft | published | deprecated | disabled
    visibility: public | private | internal | enterprise
    """

    __tablename__ = "function_library"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    name: Mapped[str] = mapped_column(String(120), nullable=False)
    slug: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    category: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    runtime: Mapped[str] = mapped_column(String(20), nullable=False, default="js")
    trigger_type: Mapped[str] = mapped_column(String(20), nullable=False, default="http")
    supported_trigger_types: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    # Lifecycle
    status: Mapped[str] = mapped_column(Text, nullable=False, default="published")
    visibility: Mapped[str] = mapped_column(Text, nullable=False, default="public")
    version: Mapped[str] = mapped_column(String(20), nullable=False, default="1.0.0")
    semver_channel: Mapped[str] = mapped_column(Text, nullable=False, default="stable")
    deprecated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    replacement_slug: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    # Plan / capabilities
    minimum_plan: Mapped[str] = mapped_column(Text, nullable=False, default="free")
    required_integrations: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    required_secrets: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    required_capabilities: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    required_bindings: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    # Execution
    execution_mode: Mapped[str] = mapped_column(Text, nullable=False, default="sync")
    timeout_ms: Mapped[int] = mapped_column(Integer, nullable=False, default=10000)
    max_retries: Mapped[int] = mapped_column(Integer, nullable=False, default=3)
    idempotency_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    # Schemas
    input_schema: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    output_schema: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    # Permissions + access
    permission_scope: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    external_access: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    trigger_config_schema: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    # Rich metadata
    tags: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    codegen_hints: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    observability_config: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    billing_config: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    # Code
    code_template: Mapped[Optional[str]] = mapped_column(Text, nullable=True)  # inline source (seeds)
    code_bundle_ref: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)  # R2 compiled bundle
    # Stats
    install_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    is_official: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    # Source ownership + drift tracking
    managed_source: Mapped[str] = mapped_column(Text, nullable=False, default="admin")
    managed_mode: Mapped[str] = mapped_column(Text, nullable=False, default="editable")
    seed_path: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    admin_override: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    manifest_hash: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    latest_seed_hash: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    last_synced_from_seed_at: Mapped[Optional[datetime]] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    current_version_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("function_library_versions.id", ondelete="SET NULL"),
        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 FunctionLibraryVersion(Base):
    """Versioned snapshots of a library function.

    Created when ops publishes a new version. Projects track which version they
    installed via project_function_versions.source_library_version_id.
    """

    __tablename__ = "function_library_versions"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    library_function_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("function_library.id", ondelete="CASCADE"),
        nullable=False,
    )
    version: Mapped[str] = mapped_column(Text, nullable=False)
    semver_channel: Mapped[str] = mapped_column(Text, nullable=False, default="stable")
    code_template: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    code_bundle_ref: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    changelog: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    manifest_hash: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    code_hash: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    config_hash: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    published_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    __table_args__ = (
        UniqueConstraint("library_function_id", "version", name="uq_fn_library_versions"),
    )


class ProjectFunction(Base):
    """A deployed function for a project.

    slug is unique per project and becomes the invocation identifier.
    active_version_id points to the currently deployed version.
    trigger_config is trigger-specific JSON:
      HTTP:     { "path": "/api/fn/my-fn", "method": "POST" }
      Event:    { "event_type": "auth.signed_up" }
      Schedule: { "cron": "0 * * * *", "timezone": "UTC" }
      Webhook:  (resolved via project_webhook_endpoints)
    """

    __tablename__ = "project_functions"

    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,
    )
    slug: Mapped[str] = mapped_column(String(64), nullable=False)
    name: Mapped[str] = mapped_column(String(120), nullable=False)
    description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    runtime: Mapped[str] = mapped_column(String(20), nullable=False, default="js")
    trigger_type: Mapped[str] = mapped_column(String(20), nullable=False, default="http")
    trigger_config: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    active_version_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True), nullable=True
    )
    # pending | deploying | active | error | disabled
    status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
    # public | authenticated | private
    access_policy: Mapped[str] = mapped_column(String(20), nullable=False, default="authenticated")
    library_fn_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("function_library.id", ondelete="SET NULL"),
        nullable=True,
    )
    secret_refs: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    # Snapshot + editability for library-sourced functions
    editable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    code_snapshot: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    config_snapshot: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    created_by: Mapped[str] = mapped_column(String(20), nullable=False, default="user")
    # Set when a feature pack installed this function; null for codegen/user functions.
    pack_slug: Mapped[Optional[str]] = mapped_column(String(80), nullable=True)
    pack_version: Mapped[Optional[str]] = mapped_column(String(40), 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()
    )
    deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)

    __table_args__ = (
        UniqueConstraint("project_id", "slug", name="uq_project_function_project_slug"),
    )


class ProjectFunctionVersion(Base):
    """One version of a project function.

    code_bundle_ref is the R2 key for custom code.
    library_fn_id + library_fn_version are set for library-sourced deployments.
    """

    __tablename__ = "project_function_versions"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    function_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("project_functions.id", ondelete="CASCADE"),
        nullable=False,
    )
    version_number: Mapped[int] = mapped_column(Integer, nullable=False)
    code_bundle_ref: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
    library_fn_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("function_library.id", ondelete="SET NULL"),
        nullable=True,
    )
    library_fn_version: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
    source_library_version_id: Mapped[Optional[UUID]] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("function_library_versions.id", ondelete="SET NULL"),
        nullable=True,
    )
    # pending | deploying | deployed | rolled_back | failed
    status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
    deployed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    rollback_from_version: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    __table_args__ = (
        UniqueConstraint(
            "function_id", "version_number", name="uq_project_fn_version_fn_version"
        ),
    )


class ProjectWebhookEndpoint(Base):
    """Maps an opaque webhook token to a project + provider.

    wh_token is the unique token embedded in `cloud.p2hs.com/wh/{wh_token}`.
    signing_secret_enc is the provider's webhook signing secret, Fernet-encrypted.
    """

    __tablename__ = "project_webhook_endpoints"

    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,
    )
    wh_token: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    provider: Mapped[str] = mapped_column(String(40), nullable=False)
    events_json: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    signing_secret_enc: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    auto_registered: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    registered_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    function_slug: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=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 WorkerRuntimeVersion(Base):
    """Platform runtime release — each row is a Project Runtime Worker bundle."""

    __tablename__ = "worker_runtime_versions"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    version: Mapped[str] = mapped_column(String(20), nullable=False, unique=True)
    bundle_ref: Mapped[str] = mapped_column(String(512), nullable=False)
    # draft | canary | stable | deprecated
    status: Mapped[str] = mapped_column(String(20), nullable=False, default="draft")
    release_notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    released_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    deprecated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())


class ProjectFunctionToken(Base):
    """Per-project scoped credential injected into that project's deployed functions.

    Deployed function code calls Cloud's record/entity API using this token instead
    of the platform-wide INTERNAL_CONTROL_TOKEN, so a leaked or misbehaving function
    can only reach its own project's data.
    """

    __tablename__ = "project_function_tokens"

    project_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("projects.id", ondelete="CASCADE"),
        primary_key=True,
    )
    token_enc: Mapped[str] = mapped_column(Text, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    rotated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)


class WorkspaceRuntimeConfig(Base):
    """Per-workspace runtime version pinning.

    runtime_version=None means auto-update to latest stable.
    """

    __tablename__ = "workspace_runtime_config"

    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,
        unique=True,
    )
    runtime_version: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
    auto_update: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    last_updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
