from datetime import datetime from typing import Any from uuid import uuid4 from sqlalchemy import ( JSON, Boolean, DateTime, Float, ForeignKey, Index, Integer, String, UniqueConstraint, false, func, ) from sqlalchemy.orm import Mapped, mapped_column from ai_platform.infrastructure.persistence.database import Base class AppCredential(Base): __tablename__ = "app_credentials" app_id: Mapped[str] = mapped_column(String(128), primary_key=True) verifier: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) lead_scoring: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) 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(), nullable=False ) class AppCredentialAuditEvent(Base): __tablename__ = "app_credential_audit_events" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) app_id: Mapped[str] = mapped_column(String(128), nullable=False) actor: Mapped[str] = mapped_column(String(254), nullable=False) action: Mapped[str] = mapped_column(String(16), nullable=False) occurred_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) class AdminUser(Base): __tablename__ = "admin_users" email: Mapped[str] = mapped_column(String(254), primary_key=True) password_hash: Mapped[str] = mapped_column(String(512), nullable=False) class AdminSession(Base): __tablename__ = "admin_sessions" id: Mapped[str] = mapped_column(String(64), primary_key=True) admin_email: Mapped[str] = mapped_column(ForeignKey("admin_users.email"), nullable=False) csrf_hash: Mapped[str] = mapped_column(String(64), nullable=False) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) class AIProfile(Base): __tablename__ = "ai_profiles" version: Mapped[str] = mapped_column(String(64), primary_key=True) model: Mapped[str] = mapped_column(String(128), nullable=False) provider: Mapped[str] = mapped_column( String(32), default="aihub", server_default="aihub", nullable=False ) base_url: Mapped[str | None] = mapped_column(String(2048)) system_prompt: Mapped[str] = mapped_column(String(8000), nullable=False) timeout_seconds: Mapped[int] = mapped_column(Integer, nullable=False) limits: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False) active: Mapped[bool] = mapped_column( Boolean, default=False, server_default=false(), nullable=False ) class AIProfileAuditEvent(Base): __tablename__ = "ai_profile_audit_events" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) actor: Mapped[str] = mapped_column(String(254), nullable=False) version: Mapped[str] = mapped_column(String(64), nullable=False) provider: Mapped[str] = mapped_column(String(32), nullable=False) occurred_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) class ScoringRun(Base): __tablename__ = "scoring_runs" __table_args__ = ( UniqueConstraint("app_id", "account_id", "request_id", name="uq_run_app_account_request"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4())) app_id: Mapped[str] = mapped_column(ForeignKey("app_credentials.app_id"), index=True) account_id: Mapped[str] = mapped_column(String(255), nullable=False) request_id: Mapped[str] = mapped_column(String(36), nullable=False) fingerprint: Mapped[str] = mapped_column(String(64), nullable=False) request: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False) profile_version: Mapped[str] = mapped_column(String(64), nullable=False) status: Mapped[str] = mapped_column(String(24), default="pending", index=True) rule_score: Mapped[int | None] = mapped_column(Integer) ai_score: Mapped[int | None] = mapped_column(Integer) ai_confidence: Mapped[float | None] = mapped_column(Float) ai_reasoning: Mapped[str | None] = mapped_column(String(2_000)) error_code: Mapped[str | None] = mapped_column(String(64)) final_score: Mapped[int | None] = mapped_column(Integer) degraded: Mapped[bool] = mapped_column(Boolean, default=False, server_default=false()) result_mode: Mapped[str | None] = mapped_column(String(32)) breakdown: Mapped[list[dict[str, Any]] | None] = mapped_column(JSON) ai_evidence: Mapped[list[dict[str, Any]] | None] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), index=True ) completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) class ScoringNotification(Base): __tablename__ = "scoring_notifications" __table_args__ = (Index("ix_scoring_notifications_due", "dead", "next_attempt_at"),) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4())) run_id: Mapped[str] = mapped_column( ForeignKey("scoring_runs.id", ondelete="CASCADE"), unique=True, nullable=False ) app_id: Mapped[str] = mapped_column(String(128), nullable=False) account_id: Mapped[str] = mapped_column(String(255), nullable=False) request_id: Mapped[str] = mapped_column(String(36), nullable=False) status: Mapped[str] = mapped_column(String(24), nullable=False) attempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False) next_attempt_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) delivered_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) dead: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)