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

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 JSONB, 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 ProjectAIConfig(Base):
    """AI provider configuration for a project. One row per project (upsert pattern).

    completion_secret_refs_json holds opaque refs to Core secrets — never raw keys.
    embedding_secret_ref is a ref to the BYOK embedding API key (Core secrets only).
    """

    __tablename__ = "project_ai_config"

    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,
        unique=True,
    )
    # cf_workers_ai | openai | byok
    embedding_provider: Mapped[str] = mapped_column(
        String(32), nullable=False, default="cf_workers_ai"
    )
    embedding_model: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    # BYOK: opaque ref to Core secret storing the embedding API key
    embedding_secret_ref: Mapped[Optional[str]] = mapped_column(String(128), nullable=True)
    # { "openai": "ref:...", "anthropic": "ref:..." } — refs only, never raw keys
    completion_secret_refs_json: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)
    # list of provider slugs available to this project (set from plan entitlements)
    completion_providers_json: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class ProjectAIEmbedConfig(Base):
    """Per-field embedding configuration — which entity fields to auto-embed.

    auto_embed=True: pc-processor queues ai.embed after every create/update.
    model_override=None: uses the project-level embedding model.
    """

    __tablename__ = "project_ai_embed_configs"

    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,
    )
    entity_key: Mapped[str] = mapped_column(String(128), nullable=False)
    field_key: Mapped[str] = mapped_column(String(128), nullable=False)
    auto_embed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    model_override: Mapped[Optional[str]] = mapped_column(String(64), 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(
            "project_id", "entity_key", "field_key",
            name="uq_ai_embed_config_project_entity_field",
        ),
    )


class WorkspaceVectorizeConfig(Base):
    """CF Vectorize index allocation for a workspace. One row per workspace.

    One Vectorize index is shared across all projects in the workspace.
    Vectors carry project_id metadata so queries always filter by project first.
    needs_reembed is set True on plan upgrade (incompatible dimensions) — user must
    trigger bulk re-embed before semantic search works correctly again.
    """

    __tablename__ = "workspace_vectorize_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,
    )
    vectorize_index_id: Mapped[str] = mapped_column(String(128), nullable=False)
    index_name: Mapped[str] = mapped_column(String(128), nullable=False)
    dimensions: Mapped[int] = mapped_column(Integer, nullable=False, default=768)
    metric: Mapped[str] = mapped_column(String(20), nullable=False, default="cosine")
    needs_reembed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
