import enum
from datetime import datetime, timezone, date
from decimal import Decimal
from enum import Enum
from typing import Any, Dict, Optional, List
from uuid import UUID, uuid4


def generate_uuid() -> str:
    """Generate a new UUID string."""
    return str(uuid4())


def generate_uuid_object() -> UUID:
    """Generate a new UUID object."""
    return uuid4()


def get_current_time() -> datetime:
    """Get current UTC time."""
    return datetime.utcnow()


def utcnow() -> datetime:
    """Timezone-aware UTC now for consistent timestamps."""
    return datetime.now(timezone.utc)


from sqlalchemy import (
    BigInteger,
    Boolean,
    Column,
    Date,
    DateTime,
    Index,
    CheckConstraint,
    Integer,
    LargeBinary,
    JSON as SAJSON,
    Numeric,
    String,
    ForeignKey,
    UniqueConstraint,
    Text,
    text,
)
from sqlalchemy import Enum as SQLEnum
from sqlalchemy.dialects.postgresql import UUID as PGUUID, JSONB, ARRAY
from sqlmodel import Field, JSON, SQLModel

from .db import Base  # Import Base from our new db module

# Use JSON for SQLite to keep test DBs portable.
PortableJSONB = JSONB().with_variant(SAJSON, "sqlite")
PortableStringArray = ARRAY(String).with_variant(SAJSON, "sqlite")


class Project(Base, table=True):
    __tablename__ = "projects"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),  # primary_key ONLY here
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id"), index=True
        )  # ForeignKey in Column
    )
    name: str
    description: Optional[str] = None

    source_file_key: Optional[str] = None
    reference_file_key: Optional[str] = None

    created_at: datetime = Field(default_factory=get_current_time, nullable=False)
    updated_at: datetime = Field(default_factory=get_current_time, nullable=False)


class JobStatus(str, enum.Enum):
    QUEUED = "queued"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"


class JobType(str, enum.Enum):
    MATCH = "match"
    DEDUPE = "dedupe"
    DUPLICATE = "duplicate"  # backwards compat
    CRM_GAP_ANALYSIS = "crm_gap_analysis"
    ID_HUNTER = "id_hunter"
    SFDC_BULK_MATCH = "sfdc_bulk_match"


class Job(Base, table=True):
    """Unified job model replacing DuckDB/SQLite job_store"""

    __tablename__ = "jobs"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(
            String(36), primary_key=True, index=True
        ),  # primary_key ONLY here
        description="Job UUID (hyphenated)",
    )
    project_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("projects.id"), nullable=True, index=True
        ),  # ForeignKey in Column - nullable
    )
    account_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("accounts.id"), nullable=True, index=True
        ),  # ForeignKey in Column - nullable
    )

    # Status tracking
    status: JobStatus = Field(
        sa_column=Column(SQLEnum(JobStatus), nullable=False, index=True)
    )
    job_type: JobType = Field(sa_column=Column(SQLEnum(JobType), nullable=False))

    # Configuration and results
    config: Dict[str, Any] = Field(
        default_factory=dict, sa_column=Column(JSON, nullable=False)
    )
    results: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))

    # Tracking
    created_at: datetime = Field(
        sa_column=Column(DateTime(timezone=True), nullable=False),
        default_factory=utcnow,
    )
    updated_at: datetime = Field(
        sa_column=Column(DateTime(timezone=True), onupdate=utcnow, nullable=False),
        default_factory=utcnow,
    )

    # Additional fields from your current implementation
    results_path: Optional[str] = None
    matches_found: Optional[int] = None
    error: Optional[str] = None
    webhook_url: Optional[str] = None

    # For efficient queries - combine both table args
    __table_args__ = (
        Index("idx_job_project_status", "project_id", "status"),
        Index("idx_job_created", "created_at"),
        {"extend_existing": True},  # temporary guard against double imports
    )


class UserRole(str, enum.Enum):
    """User roles for RBAC"""

    VIEWER = "viewer"
    STEWARD = "steward"
    ADMIN = "admin"
    OWNER = "owner"

    @classmethod
    def admin_roles(cls) -> tuple["UserRole", ...]:
        """Roles that should be treated with full admin privileges."""
        return (cls.ADMIN, cls.OWNER)

    @classmethod
    def admin_values(cls) -> set[str]:
        return {role.value for role in cls.admin_roles()}

    @classmethod
    def is_admin_value(cls, value: Any) -> bool:
        if isinstance(value, cls):
            return value in cls.admin_roles()
        if value is None:
            return False
        return str(value).strip().lower() in cls.admin_values()

    @classmethod
    def from_raw(cls, value: Any) -> Optional["UserRole"]:
        """Best-effort conversion from DB/string role values to enum."""
        if isinstance(value, cls):
            return value
        if value is None:
            return None
        normalized = str(value).strip().lower()
        alias_map = {
            "owner": cls.OWNER,
            "admin": cls.ADMIN,
            "manager": cls.STEWARD,  # legacy alias
            "steward": cls.STEWARD,
            "viewer": cls.VIEWER,
        }
        if normalized in alias_map:
            return alias_map[normalized]
        try:
            return cls(normalized)
        except ValueError:
            return None


class Account(Base, table=True):
    __tablename__ = "accounts"
    __table_args__ = (
        Index("idx_accounts_clerk_org_id", "clerk_org_id", unique=True),
        {"extend_existing": True},
    )
    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),  # primary_key ONLY here
    )
    name: str
    stripe_customer_id: Optional[str] = None
    stripe_subscription_id: Optional[str] = None
    telemetry_opt_in: bool = True
    credits_balance: int = Field(default=0, nullable=False)
    purchased_credits: int = Field(
        default=0,
        nullable=False,
        description="One-time purchased credits (never expire)",
    )
    data_region: str = "us-east-1"
    do_not_contact: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("FALSE")),
        description="User requested no proactive outreach",
    )
    do_not_contact_since: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )

    # Seat management fields
    clerk_org_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(64), nullable=True, unique=True, index=True),
        description="Clerk organization ID for multi-user access",
    )
    paid_seat_count: int = Field(
        default=1,
        sa_column=Column(Integer, nullable=False, server_default="1"),
        description="Number of seats customer is paying for (from Stripe)",
    )


class AccountLifecycle(Base, table=True):
    """Manual lifecycle/health overrides and admin notes for PLG segmentation."""

    __tablename__ = "account_lifecycle"
    __table_args__ = {"extend_existing": True}

    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), primary_key=True)
    )
    lifecycle_stage_override: Optional[str] = Field(
        default=None, sa_column=Column(String(32), nullable=True)
    )
    health_score_override: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    tags: Optional[List[str]] = Field(
        default=None, sa_column=Column(PortableJSONB, nullable=True)
    )
    notes: Optional[str] = Field(
        default=None, sa_column=Column(Text, nullable=True)
    )
    updated_by: Optional[str] = Field(
        default=None, sa_column=Column(String(255), nullable=True)
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
            onupdate=utcnow,
        ),
    )


class TrialEmailTemplate(Base, table=True):
    """Email templates for the 30-day trial drip sequence."""

    __tablename__ = "trial_email_templates"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
    )
    slug: str = Field(
        sa_column=Column(String(50), nullable=False, unique=True)
    )
    day_offset: int = Field(
        sa_column=Column(Integer, nullable=False, unique=True)
    )
    subject: str = Field(sa_column=Column(Text, nullable=False))
    body_markdown: str = Field(sa_column=Column(Text, nullable=False))
    enabled: bool = Field(
        default=True,
        sa_column=Column(Boolean, nullable=False, server_default=text("TRUE")),
    )
    sort_order: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
            onupdate=utcnow,
        ),
    )


class TrialEmailSend(Base, table=True):
    """Tracks each trial drip email sent — idempotent on (account_id, template_id)."""

    __tablename__ = "trial_email_sends"
    __table_args__ = (
        Index("idx_tes_account", "account_id"),
        Index("idx_tes_status", "status"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
    )
    template_id: str = Field(
        sa_column=Column(
            String(36),
            ForeignKey("trial_email_templates.id"),
            nullable=False,
        )
    )
    account_id: str = Field(
        sa_column=Column(String(36), nullable=False)
    )
    user_id: str = Field(sa_column=Column(String(36), nullable=False))
    recipient_email: str = Field(sa_column=Column(String(255), nullable=False))
    day_offset: int = Field(sa_column=Column(Integer, nullable=False))
    resend_email_id: Optional[str] = Field(
        default=None, sa_column=Column(String(100), nullable=True)
    )
    status: str = Field(
        default="pending",
        sa_column=Column(String(20), nullable=False, server_default=text("'pending'")),
    )
    is_test: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("FALSE")),
    )
    error_message: Optional[str] = Field(
        default=None, sa_column=Column(Text, nullable=True)
    )
    sent_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class BetaSubmission(Base, table=True):
    """Beta signup submissions from the launch site form."""

    __tablename__ = "beta_submissions"
    __table_args__ = (
        Index("idx_beta_sub_status", "status"),
        Index("idx_beta_sub_domain", "domain"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
    )
    email: str = Field(sa_column=Column(String(255), nullable=False, unique=True))
    name: str = Field(default="", sa_column=Column(String(255), nullable=False, server_default=text("''")))
    use_case: str = Field(default="", sa_column=Column(Text, nullable=False, server_default=text("''")))
    domain: str = Field(default="", sa_column=Column(String(255), nullable=False, server_default=text("''")))
    is_business_email: bool = Field(
        default=True,
        sa_column=Column(Boolean, nullable=False, server_default=text("TRUE")),
    )
    source: str = Field(
        default="launch_site",
        sa_column=Column(String(50), nullable=False, server_default=text("'launch_site'")),
    )
    status: str = Field(
        default="pending",
        sa_column=Column(String(20), nullable=False, server_default=text("'pending'")),
    )
    admin_notes: Optional[str] = Field(
        default=None, sa_column=Column(Text, nullable=True)
    )
    submitted_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    reviewed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )


class Product(Base, table=True):
    """Billing product catalog synced from admin database."""

    __tablename__ = "product"
    __table_args__ = {"extend_existing": True}

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True),
    )
    key: str = Field(
        sa_column=Column(Text, nullable=False, unique=True),
    )
    name: str = Field(
        sa_column=Column(Text, nullable=False),
    )
    active: bool = Field(
        default=True,
        sa_column=Column(Boolean, nullable=False, server_default=text("TRUE")),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class Plan(Base, table=True):
    """Billing plans (Stripe prices) associated with products."""

    __tablename__ = "plan"
    __table_args__ = {"extend_existing": True}

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True),
    )
    product_id: UUID = Field(
        sa_column=Column(
            PGUUID(as_uuid=True),
            ForeignKey("product.id", ondelete="CASCADE"),
            nullable=False,
        ),
    )
    name: str = Field(
        sa_column=Column(Text, nullable=False),
    )
    stripe_price_id: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, unique=True, nullable=True),
    )
    billing_interval: str = Field(
        sa_column=Column(Text, nullable=False),
    )
    price_cents: int = Field(
        sa_column=Column(Integer, nullable=False),
    )
    metered: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("FALSE")),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class User(Base, table=True):
    __tablename__ = "users"
    __table_args__ = (
        Index("idx_users_account_seat_status", "account_id", "seat_status"),
        Index("idx_users_clerk_user_id", "clerk_user_id", unique=True),
        {"extend_existing": True},
    )
    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),  # primary_key ONLY here
    )
    account_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("accounts.id"), nullable=True
        ),  # ForeignKey in Column
    )
    email: str = Field(unique=True, index=True)
    role: UserRole = Field(
        default=UserRole.VIEWER,
        sa_column=Column(
            SQLEnum(
                UserRole,
                native_enum=False,
                values_callable=lambda enum: [member.value for member in enum],
                name="userrole",
            ),
            nullable=False,
        ),
    )
    created_at: datetime = Field(default_factory=get_current_time, nullable=False)
    last_login: Optional[datetime] = None

    # Seat management fields
    seat_status: str = Field(
        default="active",
        sa_column=Column(String(20), nullable=False, server_default="active"),
        description="Seat status: active, invited, suspended, removed",
    )
    invited_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="When the user was invited (for pending invitations)",
    )
    clerk_user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(64), nullable=True, unique=True, index=True),
        description="Clerk user ID for linking to Clerk authentication",
    )
    profile_metadata: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column("metadata", JSON, nullable=True),
        description="Arbitrary metadata: display name, Clerk info, etc.",
    )


class AccountMember(Base, table=True):
    """Tracks membership of users in an organization, including invitations and seat mapping."""

    __tablename__ = "account_members"
    __table_args__ = (
        UniqueConstraint("account_id", "user_id", name="uq_account_member_user"),
        Index("idx_account_members_account", "account_id"),
        Index("idx_account_members_user", "user_id"),
        Index("idx_account_members_role_status", "role", "status"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False
        )
    )
    user_id: str = Field(
        sa_column=Column(String(64), nullable=False),
        description="Primary identity provider reference (Clerk)",
    )
    email: Optional[str] = Field(
        default=None, sa_column=Column(String(255), nullable=True)
    )
    role: str = Field(
        default="member",
        sa_column=Column(String(20), nullable=False),
        description="owner | admin | member | viewer",
    )
    status: str = Field(
        default="active",
        sa_column=Column(String(20), nullable=False),
        description="active | invited | suspended | removed",
    )
    allocated_seat_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), nullable=True),
        description="Optional soft reference to a seat allocation",
    )
    invited_by: Optional[str] = Field(
        default=None,
        sa_column=Column(String(64), nullable=True),
        description="Clerk user ID of inviter",
    )
    invited_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    accepted_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    last_active_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    member_metadata: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON, nullable=True)
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class APIKey(Base, table=True):
    """API keys for programmatic access"""

    __tablename__ = "api_keys"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),  # primary_key ONLY here
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id"), index=True
        )  # ForeignKey in Column
    )
    user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("users.id"), nullable=True, index=True
        ),  # ForeignKey in Column
    )

    name: str = Field(description="User-friendly name for the key")
    description: Optional[str] = Field(
        default=None, description="Optional description of the key's purpose"
    )
    key_prefix: str = Field(index=True, description="First 8 chars for identification")
    key_hash: str = Field(description="Bcrypt hash of the full key")
    key_encrypted: Optional[str] = Field(
        default=None, description="Encrypted full key (for retrieval)"
    )

    is_active: bool = Field(default=True)
    # Public API v1 fields (nullable for backward compat)
    tier: Optional[str] = None  # free|pro|enterprise
    qps_limit: Optional[int] = None
    monthly_quota: Optional[int] = None
    status: Optional[str] = None  # active|disabled
    last_used_at: Optional[datetime] = Field(default=None, sa_column=Column(DateTime))
    created_at: datetime = Field(
        default_factory=get_current_time, sa_column=Column(DateTime, nullable=False)
    )
    expires_at: Optional[datetime] = Field(default=None, sa_column=Column(DateTime))

    # Optional restrictions
    allowed_ips: Optional[List[str]] = Field(default=None, sa_column=Column(JSON))
    rate_limit: Optional[int] = Field(default=1000)


# ====================================================================
# Event Models (for billing and telemetry tracking)
# ====================================================================


class UsageQuotaModel(Base, table=True):
    """Per-account usage quotas applied by middleware and admin APIs."""

    __tablename__ = "usage_quotas"
    __table_args__ = {"extend_existing": True}

    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), primary_key=True)
    )
    tier: str = Field(default="free", sa_column=Column(String(32), nullable=False))
    monthly_rows_used: int = Field(default=0, nullable=False)
    enrichment_credits_used: int = Field(default=0, nullable=False)
    subscription_status: str = Field(
        default="unknown",
        sa_column=Column(String(32), nullable=False, server_default="unknown"),
    )
    tier_synced_at: Optional[datetime] = Field(
        default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    tier_sync_source: str = Field(
        default="system_init",
        sa_column=Column(String(32), nullable=False, server_default="system_init"),
    )
    monthly_enrichment_entitlement: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    monthly_match_entitlement: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    daily_runs_used: int = Field(default=0, nullable=False)
    daily_rows_used: int = Field(default=0, nullable=False)
    list_builder_monthly_exports: int = Field(default=0, nullable=False)
    list_builder_daily_exports: int = Field(default=0, nullable=False)
    list_builder_daily_reset_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    active_schedules: int = Field(default=0, nullable=False)
    grace_uses_remaining: int = Field(default=1, nullable=False)
    last_grace_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    reset_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    timezone: str = Field(default="UTC", sa_column=Column(String(50), nullable=False))
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, onupdate=utcnow),
    )


class AccountEntitlements(Base, table=True):
    """Canonical entitlements for account capabilities (synced from billing)."""

    __tablename__ = "account_entitlements"
    __table_args__ = {"extend_existing": True}

    account_id: str = Field(
        sa_column=Column(String(255), ForeignKey("accounts.id"), primary_key=True)
    )
    tier: str = Field(
        default="free", sa_column=Column(String(20), nullable=False)
    )
    addons: List[str] = Field(
        default_factory=list,
        sa_column=Column(JSON, nullable=False, server_default=text("'[]'")),
    )
    monthly_row_limit: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    gremlin_daily_limit: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    ai_report_row_cap: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    byo_enabled: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("false")),
    )
    byo_daily_limit: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    source: str = Field(
        default="system_init",
        sa_column=Column(String(50), nullable=False, server_default=text("'system_init'")),
    )
    synced_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    expires_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    trial_started_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )


class AccountUsage(Base, table=True):
    """Current-period usage counters for rate limiting."""

    __tablename__ = "account_usage"
    __table_args__ = {"extend_existing": True}

    account_id: str = Field(
        sa_column=Column(String(255), ForeignKey("accounts.id"), primary_key=True)
    )
    rows_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_period_start: date = Field(
        sa_column=Column(Date, nullable=False),
    )
    gremlin_messages_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    gremlin_period_start: date = Field(
        sa_column=Column(Date, nullable=False),
    )
    byo_rows_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    byo_period_start: date = Field(
        sa_column=Column(Date, nullable=False),
    )
    list_builder_daily_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    list_builder_monthly_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    list_builder_daily_start: date = Field(
        sa_column=Column(Date, nullable=False),
    )
    list_builder_monthly_start: date = Field(
        sa_column=Column(Date, nullable=False),
    )
    webhook_rows_used: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    webhook_rows_period_start: Optional[date] = Field(
        default=None,
        sa_column=Column(Date, nullable=True),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class UsageLedger(Base, table=True):
    """Append-only audit log for usage events."""

    __tablename__ = "usage_ledger"
    __table_args__ = (
        Index("idx_usage_ledger_account", "account_id", "created_at"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        primary_key=True,
        index=True,
        description="Unique ledger entry identifier",
    )
    account_id: str = Field(
        sa_column=Column(String(255), ForeignKey("accounts.id"), index=True)
    )
    resource: str = Field(
        sa_column=Column(String(30), nullable=False, index=True)
    )
    amount: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    balance_after: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    source: Optional[str] = Field(
        default=None, sa_column=Column(String(100), nullable=True)
    )
    idempotency_key: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), unique=True, nullable=True),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class UsageHistory(SQLModel, table=True):
    """Monthly usage history snapshots for trend analysis and reporting.

    This table provides historical usage data that complements:
    - UsageQuotaModel: Current month snapshot
    - EnrichmentUsage: Detailed enrichment ledger
    - UsageEvent: Generic billing events

    Each row represents aggregated usage for one account in one billing period.
    """

    __tablename__ = "usage_history"
    __table_args__ = (
        Index("idx_usage_history_account_period", "account_id", "period"),
        Index("idx_usage_history_created", "created_at"),
        UniqueConstraint("account_id", "period", name="uq_usage_history_account_period"),
        {"extend_existing": True},
    )

    # Primary key
    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
        description="Unique history record identifier",
    )

    # Account & time period
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True),
        description="Account this usage belongs to",
    )
    period: str = Field(
        sa_column=Column(String(7), index=True),
        description="Billing period in YYYY-MM format",
    )

    # Core usage metrics (aggregated for the period)
    rows_processed: int = Field(
        default=0,
        description="Total rows processed in match/dedupe operations",
    )
    matches_found: int = Field(
        default=0,
        description="Total match results returned",
    )
    dedupes_run: int = Field(
        default=0,
        description="Number of dedupe operations executed",
    )
    enrichments_performed: int = Field(
        default=0,
        description="Number of enrichment operations (scan/preview/apply)",
    )
    enrichment_credits_consumed: int = Field(
        default=0,
        description="Total enrichment credits debited",
    )
    records_enriched: int = Field(
        default=0,
        description="Total records written with enrichment data",
    )
    api_calls: int = Field(
        default=0,
        description="Total API calls to external enrichment providers",
    )
    jobs_completed: int = Field(
        default=0,
        description="Total jobs completed successfully",
    )

    # Financial metrics
    cost_usd: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(18, 2)),
        description="Total provider costs for this period",
    )
    price_usd: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(18, 2)),
        description="Total customer billing for this period",
    )
    margin_usd: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(18, 2)),
        description="Gross margin (price - cost)",
    )

    # Operation breakdown (flexible JSON for detailed breakdown)
    operation_breakdown: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSON),
        description="Detailed breakdown by operation type and outcome",
    )

    # Metadata
    tier_at_snapshot: Optional[str] = Field(
        default=None,
        sa_column=Column(String(50)),
        description="Account tier when this snapshot was created",
    )
    subscription_status: Optional[str] = Field(
        default=None,
        sa_column=Column(String(50)),
        description="Subscription status when snapshot was created",
    )

    # Timestamps
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
        description="When this snapshot was created",
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, onupdate=utcnow),
        description="When this snapshot was last updated",
    )


class UsageEventType(str, Enum):
    """Types of billable usage events."""

    MATCH_REQUEST = "match_request"
    DEDUPE_REQUEST = "dedupe_request"
    API_CALL = "api_call"
    BULK_UPLOAD = "bulk_upload"
    EXPORT = "export"
    LIST_BUILDER_EXPORT = "list_builder_export"
    WEBHOOK = "webhook"
    ROWS_PROCESSED = "rows_processed"
    JOB_COMPLETED = "job_completed"
    CUSTOM = "custom"


class TelemetryEventType(str, Enum):
    """Types of telemetry events."""

    PAGE_VIEW = "page_view"
    FEATURE_USE = "feature_use"
    ERROR = "error"
    PERFORMANCE = "performance"
    USER_ACTION = "user_action"
    SYSTEM_EVENT = "system_event"


class UsageEvent(SQLModel, table=True):
    """Billing events - separate from telemetry"""

    __table_args__ = {"extend_existing": True}

    __tablename__ = "usage_events"

    # Primary key
    id: UUID = Field(
        default_factory=generate_uuid_object,
        primary_key=True,
        index=True,
        description="Unique event identifier",
    )

    # Core fields
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True),
        description="Account that generated this usage",
    )
    project_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("projects.id"), index=True),
        description="Project context for the usage",
    )
    event_type: UsageEventType = Field(index=True, description="Type of billable event")

    # Billing details
    quantity: int = Field(
        default=1,
        ge=0,
        description="Quantity for billing (e.g., number of rows processed)",
    )
    unit_price: Optional[float] = Field(
        default=None, ge=0, description="Price per unit at time of event"
    )
    total_cost: Optional[float] = Field(
        default=None, ge=0, description="Total cost (quantity * unit_price)"
    )

    # Metadata
    event_metadata: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column("metadata", JSON),
        description="Additional event metadata",
    )

    # Request tracking
    request_id: Optional[str] = Field(
        default=None, index=True, max_length=64, description="Associated API request ID"
    )
    job_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("jobs.id"), index=True),
        description="Associated job ID if applicable",
    )

    # Timestamps
    created_at: datetime = Field(
        default_factory=get_current_time,
        index=True,
        description="When the event occurred",
    )
    processed_at: Optional[datetime] = Field(
        default=None, description="When billing processed this event"
    )

    # Billing cycle tracking
    billing_period: Optional[str] = Field(
        default=None,
        index=True,
        max_length=7,  # YYYY-MM format
        description="Billing period (YYYY-MM)",
    )
    invoice_id: Optional[str] = Field(
        default=None,
        index=True,
        max_length=64,
        description="Stripe invoice ID once billed",
    )

    # Legacy fields for compatibility
    timestamp: datetime = Field(
        default_factory=get_current_time, description="Legacy timestamp field"
    )
    idempotency_key: Optional[str] = Field(
        default=None,
        index=True,
        unique=True,
        description="A unique key to prevent double-billing",
    )

    class Config:
        # Create indexes for common queries
        __table_args__ = (
            # Composite index for account billing queries
            Index(
                "idx_usage_events_account_period",
                "account_id",
                "billing_period",
                "event_type",
            ),
            # Index for unprocessed events
            Index(
                "idx_usage_events_unprocessed",
                "processed_at",
                "created_at",
                postgresql_where="processed_at IS NULL",
            ),
            # Ensure positive values
            CheckConstraint("quantity >= 0", name="check_quantity_positive"),
            CheckConstraint("unit_price >= 0", name="check_unit_price_positive"),
            CheckConstraint("total_cost >= 0", name="check_total_cost_positive"),
        )


class TelemetryEvent(SQLModel, table=True):
    __table_args__ = {"extend_existing": True}
    """
    High-volume telemetry events.

    These are write-optimized for high throughput and typically:
    - Streamed to analytics systems (Kafka, Kinesis)
    - Aggregated rather than queried individually
    - Have shorter retention periods
    - Not used for billing
    """
    __tablename__ = "telemetry_events"

    # Minimal indexing for write performance
    id: UUID = Field(
        default_factory=generate_uuid_object,
        primary_key=True,
        description="Unique event identifier",
    )

    # Core fields (minimal indexes)
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), nullable=False),
        description="Account that generated this event",
    )
    user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("users.id")),
        description="Specific user if applicable",
    )
    event_type: TelemetryEventType = Field(description="Type of telemetry event")

    # Event data
    event_name: str = Field(max_length=128, description="Specific event name")
    properties: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON),
        description="Event properties/payload",
    )

    # Context
    session_id: Optional[str] = Field(
        default=None, max_length=64, description="User session identifier"
    )
    request_id: Optional[str] = Field(
        default=None, max_length=64, description="Associated API request ID"
    )

    # Client info
    client_ip: Optional[str] = Field(
        default=None,
        max_length=45,  # IPv6 max length
        description="Client IP address",
    )
    user_agent: Optional[str] = Field(
        default=None, max_length=512, description="Client user agent"
    )
    client_version: Optional[str] = Field(
        default=None, description="The version of the client that sent the event"
    )
    client_type: Optional[str] = Field(
        default=None, description="The type of client (e.g., 'gui', 'api', 'sdk')"
    )

    # Timestamps
    created_at: datetime = Field(
        default_factory=get_current_time, description="When the event occurred"
    )
    timestamp: datetime = Field(
        default_factory=get_current_time, description="Legacy timestamp field"
    )

    # Streaming metadata
    stream_offset: Optional[int] = Field(
        default=None, description="Offset in streaming system"
    )
    stream_partition: Optional[int] = Field(
        default=None, description="Partition in streaming system"
    )

    # Legacy fields for compatibility
    payload: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(JSON), description="Legacy payload field"
    )

    class Config:
        # Minimal indexing for write performance
        __table_args__ = (
            # Only index by time for cleanup/archival
            Index("idx_telemetry_events_created", "created_at"),
            # Optional: Index for specific high-value queries
            Index(
                "idx_telemetry_events_account_type",
                "account_id",
                "event_type",
                "created_at",
            ),
        )


# Type aliases for clarity
BillableEvent = UsageEvent
AnalyticsEvent = TelemetryEvent


class SaaSEventStatus(str, Enum):
    """Lifecycle status for SaaS activity events."""

    STARTED = "started"
    COMPLETED = "completed"
    SUCCESS = "success"
    FAILED = "failed"


class SaaSEvent(SQLModel, table=True):
    """
    Append-only log of user-visible activity for operational reporting.
    """

    __tablename__ = "saas_events"
    __table_args__ = (
        Index("saas_events_org_ts", "org_id", "ts"),
        Index("saas_events_action_ts", "action", "ts"),
        Index("saas_events_status_ts", "status", "ts"),
        Index("saas_events_job_id", "job_id"),
        UniqueConstraint("org_id", "idempotency_key", name="saas_events_org_idem"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        primary_key=True,
        description="Event identifier (UUID).",
    )
    ts: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
        description="Event timestamp.",
    )
    org_id: str = Field(
        sa_column=Column(String(128), nullable=False),
        description="Multi-tenant org identifier.",
    )
    user_email: str = Field(
        sa_column=Column(String(255), nullable=False),
        description="Email of the actor that triggered the action.",
    )
    sheet_id: str = Field(
        sa_column=Column(String(128), nullable=False),
        description="Source Google Sheet identifier.",
    )
    tab_name: Optional[str] = Field(
        default=None,
        sa_column=Column(String(128), nullable=True),
        description="Sheet tab name (if applicable).",
    )
    source_version: Optional[str] = Field(
        default=None,
        sa_column=Column(String(32), nullable=True),
        description="Emitter version identifier (addon/API release).",
    )
    source: str = Field(
        default="sheets_addon",
        sa_column=Column(String(64), nullable=False, server_default="sheets_addon"),
        description="Event origin (sheets_addon, api, automation, etc.).",
    )
    action: str = Field(
        sa_column=Column(String(128), nullable=False),
        description="Action identifier (e.g. match_started).",
    )
    status: SaaSEventStatus = Field(
        sa_column=Column(String(32), nullable=False),
        description="Lifecycle status for the action.",
    )
    rows: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, nullable=True),
        description="Rows processed/affected.",
    )
    duration_ms: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, nullable=True),
        description="Duration in milliseconds.",
    )
    credits_delta: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, nullable=True),
        description="Credits consumed (negative) or refunded (positive).",
    )
    job_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(64), nullable=True),
        description="Job identifier required for multi-step flows.",
    )
    idempotency_key: str = Field(
        sa_column=Column(String(128), nullable=False),
        description="Idempotency key scoped to the org.",
    )
    attrs: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False),
        description="Structured metadata (error codes, balances, etc.). Include credits_balance_before/after for enrichments.",
    )


class MatchFeedbackStatus(str, enum.Enum):
    """Status of a user's feedback on a match."""

    APPROVED = "approved"
    REJECTED = "rejected"
    MAYBE = "maybe"


class MatchFeedbackEvent(SQLModel, table=True):
    __table_args__ = {"extend_existing": True}
    """
    Stores user feedback on match results. This data is critical for
    model retraining and accuracy assessment.
    """
    __tablename__ = "match_feedback_events"

    id: UUID = Field(default_factory=generate_uuid_object, primary_key=True)
    created_at: datetime = Field(
        default_factory=get_current_time, index=True, nullable=False
    )
    account_id: UUID = Field(index=True, nullable=False)
    user_id: Optional[UUID] = Field(default=None, index=True)
    job_id: UUID = Field(index=True, nullable=False)
    source_record_id: str = Field(nullable=False)
    reference_record_id: str = Field(nullable=False)
    status: MatchFeedbackStatus = Field(
        sa_column=Column(SQLEnum(MatchFeedbackStatus), nullable=False)
    )
    notes: Optional[str] = None
    feedback_metadata: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON)
    )


# ====================================================================
# Salesforce Integration Models
# ====================================================================


class IntegrationProvider(str, Enum):
    SALESFORCE = "salesforce"
    HUBSPOT = "hubspot"
    SLACK = "slack"
    EMAIL = "email"
    APOLLO = "apollo"


class IntegrationStatus(str, Enum):
    CONNECTED = "connected"
    DISCONNECTED = "disconnected"
    ERROR = "error"


class InvestigationType(str, Enum):
    SALESFORCE_SCAN = "salesforce_scan"


class InvestigationStatus(str, Enum):
    QUEUED = "queued"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


class FindingCategory(str, Enum):
    DUPLICATE = "duplicate"
    LINK_CANDIDATE = "link_candidate"


class ActionStatus(str, Enum):
    PREPARED = "prepared"
    APPLIED = "applied"
    FAILED = "failed"


class Integration(Base, table=True):
    __tablename__ = "integrations"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    provider: IntegrationProvider = Field(
        sa_column=Column(
            SQLEnum(
                IntegrationProvider,
                native_enum=False,
                values_callable=lambda enum: [member.value for member in enum],
                name="integrationprovider",
            ),
            nullable=False,
        )
    )
    status: IntegrationStatus = Field(
        default=IntegrationStatus.DISCONNECTED,
        sa_column=Column(
            SQLEnum(
                IntegrationStatus,
                native_enum=False,
                values_callable=lambda enum: [member.value for member in enum],
                name="integrationstatus",
            ),
            nullable=False,
        ),
    )
    scopes: Optional[List[str]] = Field(default=None, sa_column=Column(JSON))

    # Encrypted tokens
    access_token: Optional[str] = None
    refresh_token: Optional[str] = None
    instance_url: Optional[str] = None
    expires_at: Optional[datetime] = None

    config: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
    created_at: datetime = Field(default_factory=get_current_time)
    updated_at: datetime = Field(default_factory=get_current_time)


class GoogleSheetsIntegration(Base, table=True):
    __tablename__ = "google_sheets_integrations"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True, unique=True)
    )
    status: IntegrationStatus = Field(
        default=IntegrationStatus.DISCONNECTED,
        sa_column=Column(
            SQLEnum(
                IntegrationStatus,
                native_enum=False,
                values_callable=lambda enum: [member.value for member in enum],
                name="integrationstatus",
            ),
            nullable=False,
        ),
    )
    scopes: Optional[List[str]] = Field(default=None, sa_column=Column(JSON))
    access_token: Optional[str] = None
    refresh_token: Optional[str] = None
    token_expires_at: Optional[datetime] = None
    user_email: Optional[str] = None
    created_at: datetime = Field(default_factory=get_current_time)
    updated_at: datetime = Field(default_factory=get_current_time)


class Investigation(Base, table=True):
    __tablename__ = "investigations"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    type: InvestigationType
    params: Dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
    schedule_cron: Optional[str] = None
    status: InvestigationStatus = Field(default=InvestigationStatus.QUEUED)
    stats: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
    started_at: Optional[datetime] = None
    finished_at: Optional[datetime] = None
    created_by_user_id: Optional[str] = None
    created_at: datetime = Field(default_factory=get_current_time)


class Finding(Base, table=True):
    __tablename__ = "findings"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    investigation_id: str = Field(
        sa_column=Column(String(36), ForeignKey("investigations.id"), index=True)
    )
    object_type: str  # "Account", "Lead", "Contact"
    category: FindingCategory
    score: float = 0.0
    cluster_id: Optional[str] = None
    records: List[Dict[str, Any]] = Field(default_factory=list, sa_column=Column(JSON))
    suggestion: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
    created_at: datetime = Field(default_factory=get_current_time)


class ActionBatch(Base, table=True):
    __tablename__ = "action_batches"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    investigation_id: str = Field(
        sa_column=Column(String(36), ForeignKey("investigations.id"), index=True)
    )
    created_by_user_id: Optional[str] = None
    items: List[Dict[str, Any]] = Field(default_factory=list, sa_column=Column(JSON))
    preview: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
    status: ActionStatus = Field(default=ActionStatus.PREPARED)
    created_at: datetime = Field(default_factory=get_current_time)


class FieldMappingPolicy(Base, table=True):
    __tablename__ = "field_mapping_policies"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    salesforce_object: str = Field(
        index=True
    )  # "Lead", "Contact", "Account", "Opportunity"
    version: int = Field(default=1)
    is_active: bool = Field(default=True)

    # Core configuration
    matching_rules: Dict[str, Any] = Field(sa_column=Column(JSON))
    # Structure: {
    #   "primary_keys": ["Email", "Phone"],
    #   "matching_fields": [
    #     {"field": "Name", "weight": 0.4, "algorithm": "fuzzy", "enabled": true},
    #     {"field": "Website", "weight": 0.3, "algorithm": "domain", "enabled": true},
    #     {"field": "Phone", "weight": 0.3, "algorithm": "exact", "enabled": true}
    #   ],
    #   "blocking_field": "Domain__c",
    #   "blocking_key_length": 3
    # }

    writable_fields: List[str] = Field(default_factory=list, sa_column=Column(JSON))
    duplicate_threshold: float = Field(default=0.85)
    link_threshold: float = Field(default=0.75)

    # Cached Salesforce metadata
    field_metadata: Dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
    metadata_updated_at: Optional[datetime] = None
    raw_policy: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))

    # Tracking
    policy_hash: str = Field(index=True)  # SHA256 of normalized rules
    created_by: str
    created_at: datetime = Field(default_factory=get_current_time)
    updated_at: datetime = Field(default_factory=get_current_time)


if "audit_events" in Base.metadata.tables:
    # Re-imports during dev hot-reload can leave the old table object registered.
    # Remove it so SQLModel can recreate the mapping without raising duplicates.
    Base.metadata.remove(Base.metadata.tables["audit_events"])


class AuditEvent(Base, table=True):
    __tablename__ = "audit_events"
    __table_args__ = (
        Index("idx_audit_actor_time", "actor_id", "timestamp"),
        Index("idx_audit_action_time", "action", "timestamp"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    timestamp: datetime = Field(default_factory=get_current_time, index=True)

    # Actor information
    actor_id: str = Field(sa_column=Column(String(36), ForeignKey("users.id")))
    actor_ip: Optional[str] = None
    actor_role: str

    # Action details
    action: str  # "field_update", "merge", "export", "policy_change"
    object_type: str  # "Lead", "Account", etc.
    record_ids: List[str] = Field(default_factory=list, sa_column=Column(JSON))

    # Change tracking
    changes: Dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
    # Format: {"Lead:00Q123": {"Account__c": {"before": null, "after": "001456"}}}

    # Context
    investigation_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("investigations.id"), nullable=True),
    )
    action_batch_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("action_batches.id"), nullable=True),
    )
    policy_hash: Optional[str] = None
    policy_version: Optional[int] = None

    # User-provided justification
    justification: Optional[str] = None


class OrgProfile(Base, table=True):
    """Organization profile for business context and matching configuration"""

    __tablename__ = "org_profiles"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )

    # Salesforce org info
    org_id: Optional[str] = Field(default=None, index=True)
    instance_url: Optional[str] = None
    username: Optional[str] = None

    # Business context
    domain: Optional[str] = None  # Primary domain
    company_name: Optional[str] = None
    industry: Optional[str] = None
    sub_industry: Optional[str] = None
    sales_motion: Optional[str] = None  # B2B, B2C, B2B2C
    target_segments: Optional[List[str]] = Field(default=None, sa_column=Column(JSON))

    # Data characteristics
    hierarchy_complexity: Optional[str] = None  # low, medium, high
    geo_focus: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
    data_providers_in_use: Optional[List[str]] = Field(
        default=None, sa_column=Column(JSON)
    )
    known_identifiers: Optional[List[str]] = Field(default=None, sa_column=Column(JSON))

    # Profile metadata
    profile_confidence: Optional[float] = Field(default=0.0)
    source_evidence: Optional[List[Dict[str, Any]]] = Field(
        default=None, sa_column=Column(JSON)
    )

    # Timestamps
    connected_at: datetime = Field(default_factory=get_current_time)
    updated_at: datetime = Field(
        default_factory=get_current_time,
        sa_column=Column(DateTime, default=get_current_time, onupdate=get_current_time),
    )


class SfdcImportChunk(Base, table=True):
    __tablename__ = "sfdc_import_chunks"
    __table_args__ = (
        Index("ix_sfdc_import_chunks_idem_key", "idempotency_key"),
        Index("ix_sfdc_import_chunks_expires", "expires_at"),
        UniqueConstraint(
            "org_id",
            "import_run_id",
            "chunk_index",
            "payload_hash",
            name="uq_sfdc_chunk_identity",
        ),
        {"extend_existing": True},
    )

    id: int = Field(
        sa_column=Column(BigInteger, primary_key=True, autoincrement=True)
    )
    org_id: str = Field(sa_column=Column(String(64), nullable=False))
    import_run_id: str = Field(sa_column=Column(String(128), nullable=False))
    chunk_index: int = Field(sa_column=Column(Integer, nullable=False))
    payload_hash: str = Field(sa_column=Column(String(64), nullable=False))

    idempotency_key: Optional[str] = Field(
        default=None,
        sa_column=Column(String(256), nullable=True),
    )

    status: str = Field(sa_column=Column(String(32), nullable=False))
    salesforce_job_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64))
    )
    mapping_method: Optional[str] = Field(
        default=None, sa_column=Column(String(32))
    )

    result_summary: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(PortableJSONB, nullable=True)
    )
    result_blob: Optional[bytes] = Field(
        default=None, sa_column=Column(LargeBinary, nullable=True)
    )

    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
            onupdate=utcnow,
        ),
    )
    completed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    expires_at: datetime = Field(
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )


# Lightweight Ops Inbox for Slack "Report bad data"
class DataIssue(Base, table=True):
    __tablename__ = "data_issues"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("users.id"), nullable=True, index=True),
    )

    source: str = Field(default="slack")  # 'slack' | 'web' | 'api'
    sf_url: Optional[str] = Field(default=None)
    sf_id: Optional[str] = Field(default=None, index=True)
    object_type: Optional[str] = Field(
        default=None, index=True
    )  # 'Lead' | 'Contact' | 'Account'
    reason: Optional[str] = Field(default=None)  # e.g., 'duplicate', 'stale', 'spam'
    notes: Optional[str] = Field(default=None)
    status: str = Field(default="open", index=True)  # 'open' | 'closed'

    created_at: datetime = Field(
        default_factory=get_current_time, sa_column=Column(DateTime, nullable=False)
    )
    updated_at: datetime = Field(
        default_factory=get_current_time, sa_column=Column(DateTime, nullable=False)
    )


class SlackSubscription(Base, table=True):
    __tablename__ = "slack_subscriptions"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    channel_id: str = Field(sa_column=Column(String(64), index=True))
    team_id: Optional[str] = Field(
        default=None, sa_column=Column(String(32), index=True)
    )
    topic: str = Field(
        sa_column=Column(String(32), index=True)
    )  # 'l2a'|'c2a'|'dedupe'|'digest'
    created_at: datetime = Field(
        default_factory=get_current_time, sa_column=Column(DateTime, nullable=False)
    )


class SlackPrefs(Base, table=True):
    __tablename__ = "slack_prefs"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    digest_enabled: bool = Field(default=True)
    digest_day: str = Field(default="mon")  # 'mon'..'sun'
    digest_hour_utc: int = Field(default=9)
    c2a_safe_threshold: float = Field(default=0.80)


# Per-tenant Autofill policy (daemon configuration)
class AutofillPolicy(Base, table=True):
    __tablename__ = "autofill_policies"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )

    # toggles
    l2a_enabled: bool = Field(default=False)
    c2a_enabled: bool = Field(default=False)

    # thresholds
    l2a_threshold: float = Field(default=0.80)
    c2a_threshold: float = Field(default=0.80)
    min_score_gap: float = Field(default=0.05)

    # scope: 'new_only' or 'new_and_backfill'
    scope: str = Field(default="new_only")
    new_window_hours: int = Field(default=48)
    backfill_cap: int = Field(default=2000)

    # C2A writeback
    c2a_writeback_mode: str = Field(default="account")  # 'account' | 'acr'
    b2c_skip: bool = Field(default=True)

    # rate / caps (not enforced yet; placeholder for future)
    per_minute: int = Field(default=60)
    daily_cap: int = Field(default=10000)

    # cursors
    l2a_last_run: Optional[datetime] = None
    c2a_last_run: Optional[datetime] = None

    # audit
    updated_at: datetime = Field(default_factory=get_current_time)


class ScheduledJob(Base, table=True):
    """Scheduled SFDC data refresh jobs"""

    __tablename__ = "scheduled_jobs"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True)
    )
    sheet_id: str = Field(
        description="Google Sheets spreadsheet ID",
        sa_column=Column(String(255), index=True),
    )
    sheet_gid: Optional[str] = Field(
        default=None,
        description="Google Sheets tab ID (gid)",
        sa_column=Column(String(32)),
    )

    job_type: str = Field(
        description="Type: 'soql_query', 'report_export', 'sheet_refresh'"
    )
    config: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False),
        description="Job configuration: soql/report_id/etc",
    )

    @property
    def cached_sheet_title(self) -> Optional[str]:
        if isinstance(self.config, dict):
            return self.config.get("cached_sheet_title")
        return None

    @cached_sheet_title.setter
    def cached_sheet_title(self, value: Optional[str]) -> None:
        if isinstance(self.config, dict):
            self.config["cached_sheet_title"] = value
        else:
            self.config = {"cached_sheet_title": value}

    schedule: str = Field(description="Schedule: 'daily', 'hourly', or cron expression")
    next_run_at: Optional[datetime] = Field(default=None, index=True)
    last_run_at: Optional[datetime] = None
    last_write_status: Optional[str] = None
    last_write_at: Optional[datetime] = None
    last_write_error: Optional[str] = None

    is_active: bool = Field(default=True, index=True)
    paused_by_system: bool = Field(default=False)
    pause_reason: Optional[str] = None
    error_count: int = Field(default=0)

    created_at: datetime = Field(default_factory=get_current_time)
    updated_at: datetime = Field(default_factory=get_current_time)


class JobRun(Base, table=True):
    """Individual execution record for a scheduled job"""

    __tablename__ = "job_runs"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    job_id: str = Field(
        sa_column=Column(String(36), ForeignKey("scheduled_jobs.id"), index=True)
    )
    idempotency_key: str = Field(
        sa_column=Column(String(64), unique=True, index=True),
        description="Hash to prevent duplicate execution",
    )

    status: str = Field(description="pending, running, success, failed, skipped")
    started_at: datetime = Field(default_factory=get_current_time)
    completed_at: Optional[datetime] = None
    duration_ms: Optional[int] = None

    rows_returned: Optional[int] = None
    error_message: Optional[str] = None
    output_location: Optional[str] = Field(
        description="Redis key or S3 path for results"
    )

    created_at: datetime = Field(default_factory=get_current_time)


class JobRunDLQ(Base, table=True):
    """Dead Letter Queue for failed job executions"""

    __tablename__ = "job_run_dlq"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    job_id: str = Field(sa_column=Column(String(36), index=True))
    run_id: str = Field(sa_column=Column(String(36)))

    error_count: int = Field(default=1)
    last_error: Optional[str] = None
    last_error_at: datetime = Field(default_factory=get_current_time)

    created_at: datetime = Field(default_factory=get_current_time)


class AdminAction(Base, table=True):
    """Audit log for admin actions on customer accounts"""

    __tablename__ = "admin_actions"
    __table_args__ = {"extend_existing": True}

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    admin_user_id: str = Field(
        sa_column=Column(String(36), ForeignKey("users.id"), index=True),
        description="Admin user who performed the action",
    )
    admin_email: str = Field(description="Email of admin user (for audit trail)")

    action: str = Field(
        index=True,
        description="Action type: view_customer, adjust_credits, override_tier, etc.",
    )
    target_account_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("accounts.id"), nullable=True, index=True
        ),
        description="Account that was affected by the action",
    )

    details: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSON, nullable=True),
        description="Action details: {before, after, reason, etc.}",
    )

    created_at: datetime = Field(
        default_factory=get_current_time,
        index=True,
        description="When the action was performed",
    )


# ====================================================================
# Support Tickets
# ====================================================================


class TicketType(str, enum.Enum):
    SUPPORT = "support"
    FEATURE = "feature"


class TicketStatus(str, enum.Enum):
    OPEN = "open"
    IN_PROGRESS = "in_progress"
    WAITING_ON_CUSTOMER = "waiting_on_customer"
    RESOLVED = "resolved"
    CLOSED = "closed"


class TicketSeverity(str, enum.Enum):
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class TicketSource(str, enum.Enum):
    GSHEETS_ADDON = "gsheets_addon"
    WEB = "web"
    API = "api"
    EMAIL = "email"
    GREMLIN = "gremlin"


class TicketNoteDirection(str, enum.Enum):
    INBOUND = "inbound"
    OUTBOUND = "outbound"
    INTERNAL = "internal"


class InsightTicketStatus(str, enum.Enum):
    OPEN = "open"
    SNOOZED = "snoozed"
    KNOWN = "known"
    CLOSED = "closed"


class InsightTicketSeverity(str, enum.Enum):
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class InsightTicketType(str, enum.Enum):
    ERROR_FIRST_SEEN = "error_first_seen"
    ERROR_RATE_REGRESSION = "error_rate_regression"
    USAGE_ANOMALY = "usage_anomaly"


class TelemetryInsightTicket(Base, table=True):
    __tablename__ = "telemetry_insight_tickets"
    __table_args__ = (
        Index("idx_insight_tickets_status", "status"),
        Index("idx_insight_tickets_severity", "severity"),
        Index("idx_insight_tickets_type", "type"),
        Index("idx_insight_tickets_feature", "feature"),
        Index("idx_insight_tickets_last_seen", "last_seen_at"),
        Index("idx_insight_tickets_primary_org", "primary_org_id"),
        Index("idx_insight_tickets_error_code", "error_code"),
        UniqueConstraint("fingerprint", name="uq_fingerprint"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(
            PGUUID(as_uuid=True),
            primary_key=True,
            server_default=text("gen_random_uuid()"),
        ),
    )
    fingerprint: str = Field(sa_column=Column(String(64), nullable=False))
    type: InsightTicketType = Field(
        sa_column=Column(
            SQLEnum(
                InsightTicketType,
                name="insight_ticket_type",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
        )
    )
    severity: InsightTicketSeverity = Field(
        default=InsightTicketSeverity.MEDIUM,
        sa_column=Column(
            SQLEnum(
                InsightTicketSeverity,
                name="insight_ticket_severity",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
            server_default=text("'medium'"),
        ),
    )
    status: InsightTicketStatus = Field(
        default=InsightTicketStatus.OPEN,
        sa_column=Column(
            SQLEnum(
                InsightTicketStatus,
                name="insight_ticket_status",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
            server_default=text("'open'"),
        ),
    )
    title: str = Field(sa_column=Column(String(256), nullable=False))
    tldr: Optional[str] = Field(default=None, sa_column=Column(Text))
    context_json: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(PortableJSONB, nullable=False),
    )

    primary_org_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("accounts.id"), nullable=True),
    )
    affected_org_ids: Optional[List[str]] = Field(
        default=None,
        sa_column=Column(PortableStringArray, nullable=True),
    )

    feature: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    action: Optional[str] = Field(default=None, sa_column=Column(String(128)))
    error_code: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    source_version: Optional[str] = Field(default=None, sa_column=Column(String(32)))

    event_count: int = Field(
        default=1, sa_column=Column(Integer, nullable=False, server_default=text("1"))
    )
    affected_orgs: int = Field(
        default=1, sa_column=Column(Integer, nullable=False, server_default=text("1"))
    )
    affected_paying_orgs: int = Field(
        default=0, sa_column=Column(Integer, nullable=False, server_default=text("0"))
    )
    first_seen_at: datetime = Field(
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
    last_seen_at: datetime = Field(
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )

    ai_triage_hash: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    ai_triage_last_run_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )

    snoozed_until: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    known_since: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )

    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, onupdate=utcnow),
    )
    closed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    closed_by: Optional[str] = Field(default=None, sa_column=Column(String(128)))

    github_issue_url: Optional[str] = Field(default=None, sa_column=Column(String(512)))
    github_issue_linked_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )

    fixed_version: Optional[str] = Field(default=None, sa_column=Column(String(32)))
    fixed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )
    fixed_by: Optional[str] = Field(default=None, sa_column=Column(String(128)))
    fixed_pr_url: Optional[str] = Field(default=None, sa_column=Column(String(512)))

    outreach_status: Optional[str] = Field(
        default=None,
        sa_column=Column(String(32)),
    )
    outreach_sent_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    outreach_email_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(128)),
    )
    outreach_skipped_reason: Optional[str] = Field(
        default=None,
        sa_column=Column(String(128)),
    )


class TelemetryInsightAction(Base, table=True):
    __tablename__ = "telemetry_insight_actions"
    __table_args__ = (
        Index("idx_insight_actions_ticket", "ticket_id"),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True))
    ticket_id: UUID = Field(
        sa_column=Column(
            PGUUID(as_uuid=True),
            ForeignKey("telemetry_insight_tickets.id", ondelete="CASCADE"),
            nullable=False,
        )
    )
    action_type: str = Field(sa_column=Column(String(64), nullable=False))
    actor_email: Optional[str] = Field(default=None, sa_column=Column(String(128)))
    details: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(PortableJSONB, nullable=True)
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class TelemetryInsightSample(Base, table=True):
    __tablename__ = "telemetry_insight_samples"
    __table_args__ = (
        UniqueConstraint(
            "ticket_id", "saas_event_id", name="uq_ticket_event"
        ),
        Index("idx_insight_samples_ticket", "ticket_id"),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True))
    ticket_id: UUID = Field(
        sa_column=Column(
            PGUUID(as_uuid=True),
            ForeignKey("telemetry_insight_tickets.id", ondelete="CASCADE"),
            nullable=False,
        )
    )
    saas_event_id: UUID = Field(
        sa_column=Column(
            PGUUID(as_uuid=True),
            ForeignKey("saas_events.id", ondelete="CASCADE"),
            nullable=False,
        )
    )
    sampled_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class SupportTicket(Base, table=True):
    __tablename__ = "support_tickets"
    __table_args__ = (
        Index("idx_support_ticket_account_status", "account_id", "status"),
        Index("idx_support_ticket_public_id", "public_id"),
        Index("idx_support_ticket_gmail_thread_id", "gmail_thread_id"),
        UniqueConstraint("identity_key", "idempotency_key", name="uq_support_ticket_idem"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
    )
    public_id: str = Field(
        sa_column=Column(String(32), unique=True, index=True),
        description="Public support ID (SUP-XXXX-YY)",
    )
    idempotency_key: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    identity_key: str = Field(
        sa_column=Column(String(64), nullable=False),
        description="Account ID or hashed email (sha256[:16]) for idempotency",
    )

    account_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(36), ForeignKey("accounts.id"), index=True),
    )
    user_email: str = Field(sa_column=Column(String(255), nullable=False))

    type: TicketType = Field(
        default=TicketType.SUPPORT,
        sa_column=Column(
            SQLEnum(
                TicketType,
                name="ticket_type",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
        ),
    )
    category: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    severity: Optional[TicketSeverity] = Field(
        default=None,
        sa_column=Column(
            SQLEnum(
                TicketSeverity,
                name="ticket_severity",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=True,
        ),
    )
    status: TicketStatus = Field(
        default=TicketStatus.OPEN,
        sa_column=Column(
            SQLEnum(
                TicketStatus,
                name="ticket_status",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
        ),
    )
    source: TicketSource = Field(
        default=TicketSource.GSHEETS_ADDON,
        sa_column=Column(
            SQLEnum(
                TicketSource,
                name="ticket_source",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
        ),
    )

    summary: str = Field(sa_column=Column(String(120), nullable=False))
    description: Optional[str] = Field(default=None, sa_column=Column(Text))
    description_snippet: Optional[str] = Field(default=None, sa_column=Column(String(140)))

    job_id: Optional[str] = Field(default=None, sa_column=Column(String(128)))
    job_verified: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("FALSE")),
    )
    doc_id: Optional[str] = Field(default=None, sa_column=Column(String(128)))
    context_json: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(JSON, nullable=True)
    )
    gmail_thread_id: Optional[str] = Field(
        default=None, sa_column=Column(String(128), nullable=True)
    )

    resolution: Optional[str] = Field(default=None, sa_column=Column(Text))
    resolved_by: Optional[str] = Field(default=None, sa_column=Column(String(255)))

    ai_triage_hash: Optional[str] = Field(
        default=None, sa_column=Column(String(32), nullable=True)
    )
    ai_triage_last_run_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )

    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, onupdate=utcnow),
    )
    resolved_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )


class SupportTicketNote(Base, table=True):
    __tablename__ = "support_ticket_notes"
    __table_args__ = (
        Index("idx_support_ticket_notes_ticket", "ticket_id"),
        Index(
            "uq_support_ticket_notes_gmail_message_id",
            "gmail_message_id",
            unique=True,
            postgresql_where=text("gmail_message_id IS NOT NULL"),
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True))
    ticket_id: str = Field(
        sa_column=Column(String(36), ForeignKey("support_tickets.id"), index=True)
    )
    author_email: Optional[str] = Field(default=None, sa_column=Column(String(255)))
    content: str = Field(sa_column=Column(Text, nullable=False))
    gmail_message_id: Optional[str] = Field(
        default=None, sa_column=Column(String(128), nullable=True)
    )
    rfc822_message_id: Optional[str] = Field(
        default=None, sa_column=Column(String(255), nullable=True)
    )
    gmail_thread_id: Optional[str] = Field(
        default=None, sa_column=Column(String(128), nullable=True)
    )
    direction: TicketNoteDirection = Field(
        default=TicketNoteDirection.INTERNAL,
        sa_column=Column(
            SQLEnum(
                TicketNoteDirection,
                name="ticket_note_direction",
                values_callable=lambda x: [e.value for e in x],
            ),
            nullable=False,
            server_default=text("'internal'"),
        ),
    )
    is_internal: bool = Field(
        default=True,
        sa_column=Column(Boolean, nullable=False, server_default=text("TRUE")),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class MailboxIntegration(Base, table=True):
    __tablename__ = "mailbox_integrations"
    __table_args__ = (
        Index("idx_mailbox_integrations_email", "email", unique=True),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid,
        sa_column=Column(String(36), primary_key=True),
    )
    email: str = Field(sa_column=Column(String(255), nullable=False))
    last_history_id: Optional[str] = Field(
        default=None, sa_column=Column(String(128), nullable=True)
    )
    status: str = Field(
        default="active",
        sa_column=Column(String(32), nullable=False, server_default=text("'active'")),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, onupdate=utcnow),
    )


# ====================================================================
# Contact Enrichment Usage Tracking - CRITICAL FOR COST CONTROL
# ====================================================================


class EnrichmentUsage(Base, table=True):
    """Append-only ledger of every enrichment chunk we bill."""

    __tablename__ = "enrichment_usage"
    __table_args__ = (
        Index("idx_enrichment_usage_account_date", "account_id", "created_at"),
        Index("idx_enrichment_usage_unbilled", "billed", "account_id"),
        CheckConstraint(
            "credits_debited >= 0", name="ck_enrichment_usage_non_negative_credits"
        ),
        CheckConstraint(
            "price_usd >= cost_usd", name="ck_enrichment_usage_margin_non_negative"
        ),
        CheckConstraint(
            "(NOT billed) OR (billed_at IS NOT NULL)",
            name="ck_enrichment_usage_billed_requires_timestamp",
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255), ForeignKey("accounts.id"), index=True, nullable=False
        )
    )
    user_email: Optional[str] = Field(default=None, sa_column=Column(String(255)))

    operation: str = Field(
        sa_column=Column(String(20), nullable=False)
    )  # scan|preview|apply
    provider: str = Field(sa_column=Column(String(20), nullable=False, default="pdl"))
    enrichment_type: str = Field(
        sa_column=Column(String(20), nullable=False, default="contact")
    )
    sfdc_object: Optional[str] = Field(default=None, sa_column=Column(String(40)))

    job_id: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    chunk_id: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    idempotency_key: Optional[str] = Field(default=None, sa_column=Column(String(80)))
    request_hash: Optional[str] = Field(default=None, sa_column=Column(String(64)))

    records_scanned: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    candidates_found: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    pdl_requests: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    matches_found: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    credits_debited: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    records_written: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    fields_updated: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )

    cost_usd: Decimal = Field(
        default=Decimal("0"),
        sa_column=Column(Numeric(18, 2), nullable=False, server_default=text("0")),
    )
    price_usd: Decimal = Field(
        default=Decimal("0"),
        sa_column=Column(Numeric(18, 2), nullable=False, server_default=text("0")),
    )
    margin_usd: Decimal = Field(
        default=Decimal("0"),
        sa_column=Column(Numeric(18, 2), nullable=False, server_default=text("0")),
    )

    billed: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=text("false")),
    )
    billed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True))
    )
    stripe_invoice_id: Optional[str] = Field(
        default=None, sa_column=Column(String(255))
    )

    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    duration_ms: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(18, 3), nullable=True),
    )
    error: Optional[str] = Field(default=None, sa_column=Column(Text))


class EnrichmentQuota(Base, table=True):
    """Per-account wallet + guardrails for enrichment spend."""

    __tablename__ = "enrichment_quota"
    __table_args__ = (
        CheckConstraint(
            "(credits_per_request = 0) OR (credits_per_request >= 1)",
            name="ck_enrichment_quota_credits_per_request_min",
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255), ForeignKey("accounts.id"), unique=True, nullable=False
        )
    )

    daily_limit_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    monthly_limit_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )

    daily_used_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    monthly_used_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )

    allowance_balance_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    pack_balance_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )

    credits_per_request: int = Field(
        default=1,  # Simplified: 1 credit = 1 enrichment
        sa_column=Column(Integer, nullable=False, server_default=text("1")),
    )

    currency_code: str = Field(
        default="USD",
        sa_column=Column(String(3), nullable=False, server_default=text("'USD'")),
    )
    account_timezone: str = Field(
        default="UTC",
        sa_column=Column(String(64), nullable=False, server_default=text("'UTC'")),
    )

    provider_cost_per_request_usd: Decimal = Field(
        default=Decimal("0.28"),  # PDL month-to-month cost
        sa_column=Column(Numeric(18, 2), nullable=False, server_default=text("0.28")),
    )
    price_per_request_usd: Decimal = Field(
        default=Decimal("0.40"),  # Customer price per enrichment
        sa_column=Column(Numeric(18, 2), nullable=False, server_default=text("0.40")),
    )

    enabled: bool = Field(
        default=True,
        sa_column=Column(Boolean, nullable=False, server_default=text("true")),
    )

    daily_reset_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    monthly_reset_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )

    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), onupdate=utcnow),
    )


class EnrichmentFieldMapping(Base, table=True):
    """Custom field mappings for contact/lead enrichment to Salesforce fields."""

    __tablename__ = "enrichment_field_mappings"
    __table_args__ = {"extend_existing": True}

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255),
            ForeignKey("accounts.id", ondelete="CASCADE"),
            unique=True,
            nullable=False,
        )
    )

    # Field mappings stored as JSONB
    # Format: {"email": "Email", "linkedin_url": "LinkedIn__c", ...}
    account_mappings: Dict[str, str] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False, server_default=text("'{}'")),
        description="Field mappings for Account object",
    )
    contact_mappings: Dict[str, str] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False, server_default=text("'{}'")),
        description="Field mappings for Contact object",
    )
    lead_mappings: Dict[str, str] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False, server_default=text("'{}'")),
        description="Field mappings for Lead object",
    )

    # Metadata
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), onupdate=utcnow),
    )
    created_by: Optional[str] = Field(default=None, sa_column=Column(String(255)))
    updated_by: Optional[str] = Field(default=None, sa_column=Column(String(255)))


class EnrichmentReservationStatus(str, Enum):
    RESERVED = "reserved"
    FINALIZED = "finalized"
    RELEASED = "released"
    EXPIRED = "expired"


class EnrichmentReservation(Base, table=True):
    """Atomic credit reservations to guard outbound provider calls."""

    __tablename__ = "enrichment_reservation"
    __table_args__ = (
        UniqueConstraint(
            "account_id",
            "idempotency_key",
            name="uq_enrichment_reservation_account_idem",
        ),
        Index("idx_enrichment_reservation_account_status", "account_id", "status"),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255), ForeignKey("accounts.id"), index=True, nullable=False
        )
    )
    idempotency_key: str = Field(sa_column=Column(String(80), nullable=False))
    job_id: Optional[str] = Field(default=None, sa_column=Column(String(64)))
    chunk_id: Optional[str] = Field(default=None, sa_column=Column(String(64)))

    required_credits: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False),
    )
    status: EnrichmentReservationStatus = Field(
        default=EnrichmentReservationStatus.RESERVED,
        sa_column=Column(String(12), nullable=False, server_default=text("'reserved'")),
    )
    lease_expires_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    last_heartbeat_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    finalized_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )


class EnrichmentUsageIdempotency(Base, table=True):
    """Deduplicate retries for the same chunk."""

    __tablename__ = "enrichment_usage_idempotency"
    __table_args__ = (
        UniqueConstraint(
            "account_id", "idempotency_key", name="uq_enrichment_usage_idempotency_key"
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255), ForeignKey("accounts.id"), nullable=False, index=True
        )
    )
    idempotency_key: str = Field(sa_column=Column(String(80), nullable=False))
    request_hash: str = Field(sa_column=Column(String(64), nullable=False))
    first_usage_id: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, ForeignKey("enrichment_usage.id"), nullable=True),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class EnrichmentLedgerOutbox(Base, table=True):
    """Retry queue for enrichment ledger finalization."""

    __tablename__ = "enrichment_ledger_outbox"
    __table_args__ = (
        Index("idx_enrichment_ledger_outbox_status", "status", "next_attempt_at"),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(Integer, primary_key=True, autoincrement=True))
    reservation_id: int = Field(
        sa_column=Column(
            Integer, ForeignKey("enrichment_reservation.id"), nullable=False, index=True
        )
    )
    account_id: str = Field(
        sa_column=Column(
            String(255), ForeignKey("accounts.id"), nullable=False, index=True
        )
    )
    idempotency_key: str = Field(sa_column=Column(String(80), nullable=False))
    request_hash: str = Field(sa_column=Column(String(64), nullable=False))
    operation: str = Field(sa_column=Column(String(20), nullable=False))
    enrichment_type: str = Field(sa_column=Column(String(20), nullable=False))
    provider: str = Field(
        default="pdl",
        sa_column=Column(String(20), nullable=False, server_default=text("'pdl'")),
    )
    payload: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False),
    )
    status: str = Field(
        default="pending",
        sa_column=Column(String(20), nullable=False, server_default=text("'pending'")),
    )
    attempt_count: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    next_attempt_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    last_error: Optional[str] = Field(
        default=None,
        sa_column=Column(String(512), nullable=True),
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    updated_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True, onupdate=utcnow),
    )


# ====================================================================
# Enrichment Credit Buckets - For 30-Day Rollover Support
# ====================================================================


class EnrichmentCreditBucket(Base, table=True):
    """
    Credit buckets for FEFO (First-Expiring-First-Out) consumption.

    Each bucket represents a credit issuance from:
    - 'tier': Monthly subscription allowance (20 for Pro, 100 for Scale)
    - 'pack': One-time pack purchase (never expires)
    - 'adjustment': Manual admin adjustment
    """

    __tablename__ = "enrichment_credit_buckets"
    __table_args__ = (
        Index("idx_buckets_account_expires", "account_id", "expires_at"),
        Index(
            "idx_buckets_active",
            "account_id",
            "status",
            "expires_at",
            postgresql_where=text("status = 'active' AND quantity_remaining > 0"),
        ),
        Index("idx_buckets_source", "account_id", "source"),
        CheckConstraint(
            "source IN ('tier', 'pack', 'adjustment')", name="ck_bucket_source"
        ),
        CheckConstraint(
            "status IN ('active', 'expired', 'consumed')", name="ck_bucket_status"
        ),
        CheckConstraint("quantity_remaining >= 0", name="ck_bucket_remaining_positive"),
        CheckConstraint(
            "quantity_remaining <= quantity_total", name="ck_bucket_remaining_lte_total"
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(BigInteger, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255),
            ForeignKey("accounts.id", ondelete="CASCADE"),
            nullable=False,
            index=True,
        )
    )
    source: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="Source of credits: tier | pack | adjustment",
    )
    source_ref: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Reference ID: invoice_id, pack_order_id, etc",
    )
    issued_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    valid_from: date = Field(
        sa_column=Column(Date, nullable=False),
        description="Start date of validity period",
    )
    valid_to: date = Field(
        sa_column=Column(Date, nullable=False),
        description="End date of validity period (EOM for tier)",
    )
    expires_at: date = Field(
        sa_column=Column(Date, nullable=False),
        description="Expiration date (valid_to + 30 days for tier, far-future for pack)",
    )
    quantity_total: int = Field(
        sa_column=Column(Integer, nullable=False),
        description="Total credits issued in this bucket",
    )
    quantity_remaining: int = Field(
        sa_column=Column(Integer, nullable=False), description="Credits still available"
    )
    status: str = Field(
        default="active",
        sa_column=Column(String(20), nullable=False, server_default="active"),
        description="Bucket status: active | expired | consumed",
    )
    notes: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class CreditLedgerKind(str, Enum):
    """Types of credit ledger events."""

    ISSUE = "issue"  # Bucket created (tier or pack)
    RESERVE = "reserve"  # Credits reserved for job
    CONSUME = "consume"  # Credits consumed (job succeeded)
    RELEASE = "release"  # Reservation released (job failed/timeout)
    EXPIRE = "expire"  # Bucket expired
    ADJUST = "adjust"  # Manual adjustment


class EnrichmentCreditLedger(Base, table=True):
    """
    Event-sourced ledger of all credit movements.

    Provides full audit trail and allows reconstruction of balances.
    """

    __tablename__ = "enrichment_credit_ledger"
    __table_args__ = (
        Index("idx_ledger_account_time", "account_id", "occurred_at"),
        Index("idx_ledger_bucket", "bucket_id"),
        Index("idx_ledger_job", "job_id"),
        Index("idx_ledger_user", "user_id", "occurred_at"),
        CheckConstraint(
            "kind IN ('issue', 'reserve', 'consume', 'release', 'expire', 'adjust')",
            name="ck_ledger_kind",
        ),
        {"extend_existing": True},
    )

    id: int = Field(sa_column=Column(BigInteger, primary_key=True, autoincrement=True))
    account_id: str = Field(
        sa_column=Column(
            String(255),
            ForeignKey("accounts.id", ondelete="CASCADE"),
            nullable=False,
            index=True,
        )
    )
    occurred_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    kind: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="Event type: issue | reserve | consume | release | expire | adjust",
    )
    bucket_id: Optional[int] = Field(
        default=None,
        sa_column=Column(
            BigInteger,
            ForeignKey("enrichment_credit_buckets.id", ondelete="SET NULL"),
            nullable=True,
        ),
    )
    job_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(
            String(36), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
        ),
    )
    user_email: Optional[str] = Field(
        default=None, sa_column=Column(String(255), nullable=True)
    )
    quantity: int = Field(
        sa_column=Column(Integer, nullable=False),
        description="Credits moved (positive for issue/release, negative for consume/expire)",
    )
    balance_after: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, nullable=True),
        description="Total balance after this event (for quick reconciliation)",
    )
    event_metadata: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON, nullable=True)
    )
    notes: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))


class SubscriptionEvent(Base, table=True):
    """Chronological log of subscription lifecycle changes."""

    __tablename__ = "subscription_events"
    __table_args__ = (
        Index("idx_subscription_events_account", "account_id", "occurred_at"),
        Index(
            "idx_subscription_events_subscription",
            "stripe_subscription_id",
            "occurred_at",
        ),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False
        )
    )
    stripe_customer_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_subscription_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    plan_id: Optional[UUID] = Field(
        default=None,
        sa_column=Column(
            PGUUID(as_uuid=True),
            ForeignKey("plan.id", ondelete="SET NULL"),
            nullable=True,
        ),
    )
    event_type: str = Field(sa_column=Column(String(40), nullable=False))
    previous_status: Optional[str] = Field(
        default=None, sa_column=Column(String(20), nullable=True)
    )
    status: Optional[str] = Field(
        default=None, sa_column=Column(String(20), nullable=True)
    )
    seat_delta: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    seat_total: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    occurred_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
    )
    event_metadata: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON, nullable=True)
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class SeatTransaction(Base, table=True):
    """Detailed record of seat purchases and removals for audit/reconciliation."""

    __tablename__ = "seat_transactions"
    __table_args__ = (
        Index("idx_seat_transactions_account", "account_id", "recorded_at"),
        Index(
            "idx_seat_transactions_subscription",
            "stripe_subscription_id",
            "recorded_at",
        ),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False
        )
    )
    user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(64), nullable=True),
        description="Optional Clerk user linked to this seat transaction",
    )
    quantity_delta: int = Field(sa_column=Column(Integer, nullable=False))
    seat_total: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    reason: Optional[str] = Field(
        default=None, sa_column=Column(String(40), nullable=True)
    )
    stripe_customer_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_subscription_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_subscription_item_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_invoice_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    recorded_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
    )
    details: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON, nullable=True)
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class CreditTransaction(Base, table=True):
    """Aggregated ledger of credit issuances, consumptions, and adjustments."""

    __tablename__ = "credit_transactions"
    __table_args__ = (
        Index("idx_credit_transactions_account", "account_id", "recorded_at"),
        Index("idx_credit_transactions_invoice", "stripe_invoice_id"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    account_id: str = Field(
        sa_column=Column(
            String(36), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False
        )
    )
    source: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="Source category: tier|pack|adjustment|consumption",
    )
    event_type: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="issue|consume|release|expire|adjust",
    )
    quantity: int = Field(sa_column=Column(Integer, nullable=False))
    balance_after: Optional[int] = Field(
        default=None, sa_column=Column(Integer, nullable=True)
    )
    stripe_invoice_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_charge_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    stripe_customer_id: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    recorded_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
    )
    details: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column("metadata", JSON, nullable=True)
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )


class WebhookAudit(Base, table=True):
    """Tracks inbound webhook delivery attempts for idempotency and diagnostics."""

    __tablename__ = "webhook_audit"
    __table_args__ = (
        UniqueConstraint("provider", "event_id", name="uq_webhook_event"),
        Index("idx_webhook_audit_type", "event_type", "received_at"),
        Index("idx_webhook_audit_scope", "scope", "received_at"),
        {"extend_existing": True},
    )

    id: str = Field(
        default_factory=generate_uuid, sa_column=Column(String(36), primary_key=True)
    )
    provider: str = Field(
        sa_column=Column(String(20), nullable=False, server_default="stripe")
    )
    event_id: str = Field(sa_column=Column(String(64), nullable=False))
    event_type: str = Field(sa_column=Column(String(64), nullable=False))
    scope: Optional[str] = Field(
        default=None, sa_column=Column(String(64), nullable=True)
    )
    status: str = Field(
        default="received",
        sa_column=Column(String(20), nullable=False),
        description="received|processed|failed|ignored",
    )
    attempts: int = Field(
        default=1, sa_column=Column(Integer, nullable=False, server_default="1")
    )
    last_error: Optional[str] = Field(
        default=None, sa_column=Column(Text, nullable=True)
    )
    payload: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(JSON, nullable=True)
    )
    headers: Optional[Dict[str, Any]] = Field(
        default=None, sa_column=Column(JSON, nullable=True)
    )
    received_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=False,
            server_default=text("CURRENT_TIMESTAMP"),
        ),
    )
    processed_at: Optional[datetime] = Field(
        default=None, sa_column=Column(DateTime(timezone=True), nullable=True)
    )


# ====================================================================
# BYO Enrichment Models (Unleashed Tier)
# ====================================================================


class WorkspaceSecret(Base, table=True):
    """Envelope-encrypted credential storage for BYO data providers."""

    __tablename__ = "workspace_secrets"
    __table_args__ = (
        UniqueConstraint("workspace_id", "integration", "key_name", name="uq_workspace_secrets_key"),
        Index("ix_workspace_secrets_workspace", "workspace_id"),
        Index("ix_workspace_secrets_kms_version", "kms_key_version"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")),
    )
    workspace_id: str = Field(
        sa_column=Column(String(255), nullable=False, index=True),
        description="Workspace/org ID that owns this secret",
    )
    integration: str = Field(
        sa_column=Column(String(50), nullable=False),
        description="Integration name: fullenrich, apify, apollo, pdl",
    )
    key_name: str = Field(
        sa_column=Column(String(100), nullable=False),
        description="Key name: api_key, api_token",
    )
    # Envelope encryption fields
    encrypted_value: bytes = Field(
        sa_column=Column(LargeBinary, nullable=False),
        description="AES-256-GCM encrypted secret value",
    )
    encryption_nonce: bytes = Field(
        sa_column=Column(LargeBinary, nullable=False),
        description="12-byte GCM nonce for decryption",
    )
    encrypted_dek: bytes = Field(
        sa_column=Column(LargeBinary, nullable=False),
        description="Data Encryption Key wrapped with KMS",
    )
    kms_key_version: str = Field(
        sa_column=Column(String(255), nullable=False),
        description="KMS key version used for DEK encryption (for rotation)",
    )
    key_suffix: str = Field(
        sa_column=Column(String(10), nullable=False),
        description="Last 4 characters for display purposes",
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    created_by: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="User ID who added this secret",
    )
    validated_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="Last successful validation timestamp",
    )
    terms_accepted_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="Timestamp when provider terms were accepted",
    )


class SecretAuditAction(str, Enum):
    """Actions that can be audited for secret operations."""
    ADDED = "added"
    REPLACED = "replaced"
    REMOVED = "removed"
    VALIDATED = "validated"


class SecretAuditLog(Base, table=True):
    """Audit trail for credential operations."""

    __tablename__ = "secret_audit_log"
    __table_args__ = (
        Index("ix_secret_audit_workspace", "workspace_id", "timestamp"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")),
    )
    workspace_id: str = Field(
        sa_column=Column(String(255), nullable=False),
        description="Workspace/org ID",
    )
    integration: str = Field(
        sa_column=Column(String(50), nullable=False),
        description="Integration name: fullenrich, apify, apollo, pdl",
    )
    action: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="Action: added, replaced, removed, validated",
    )
    actor_email: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Email of user who performed action",
    )
    actor_user_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="User ID who performed action",
    )
    ip_address: Optional[str] = Field(
        default=None,
        sa_column=Column(String(45), nullable=True),
        description="Client IP address (IPv4 or IPv6)",
    )
    user_agent: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, nullable=True),
        description="Client user agent string",
    )
    timestamp: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )


class EnrichmentJobStatus(str, Enum):
    """Status of an enrichment job."""
    QUEUED = "queued"
    RUNNING = "running"
    PAUSED = "paused"
    COMPLETED = "completed"
    FAILED = "failed"


class EnrichmentJobType(str, Enum):
    """Type of enrichment operation."""
    COMPANY = "company"
    CONTACT = "contact"


class BYOEnrichmentJob(Base, table=True):
    """Job tracking for BYO enrichment with Cloud Tasks leasing."""

    __tablename__ = "enrichment_jobs"
    __table_args__ = (
        UniqueConstraint("workspace_id", "run_id", name="uq_enrichment_jobs_run_id"),
        Index("ix_enrichment_jobs_workspace", "workspace_id", "created_at"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")),
    )
    workspace_id: str = Field(
        sa_column=Column(String(255), nullable=False),
        description="Workspace/org ID",
    )
    run_id: str = Field(
        sa_column=Column(String(50), nullable=False),
        description="Idempotency key for this run",
    )
    status: str = Field(
        default=EnrichmentJobStatus.QUEUED.value,
        sa_column=Column(String(20), nullable=False, server_default=text("'queued'")),
        description="Job status: queued, running, paused, completed, failed",
    )
    enrichment_type: str = Field(
        sa_column=Column(String(20), nullable=False),
        description="Enrichment type: company, contact",
    )
    # Row tracking
    row_count: int = Field(
        sa_column=Column(Integer, nullable=False),
        description="Total rows to process",
    )
    cursor: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
        description="Next row index to process (for resume)",
    )
    rows_processed: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_enriched: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_skipped: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_errored: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    credits_consumed: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    # Lease for exactly-once processing
    lease_expires_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="Lease expiration (NULL = not leased)",
    )
    lease_holder: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Task ID holding the lease",
    )
    # Configuration
    chunk_size: int = Field(
        default=50,
        sa_column=Column(Integer, nullable=False, server_default=text("50")),
        description="Rows per Cloud Task",
    )
    max_rows: int = Field(
        default=1000,
        sa_column=Column(Integer, nullable=False, server_default=text("1000")),
        description="Hard cap per job",
    )
    providers: List[str] = Field(
        default_factory=list,
        sa_column=Column(JSON, nullable=False),
        description='Provider priority order: ["foundrygraph", "fullenrich", "pdl"]',
    )
    input_mapping: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False),
        description='Column mappings: {"domain": "A", "company_name": "B"}',
    )
    output_fields: List[str] = Field(
        default_factory=list,
        sa_column=Column(JSON, nullable=False),
        description='Fields to enrich: ["industry", "employee_count"]',
    )
    output_range: Optional[str] = Field(
        default=None,
        sa_column=Column(String(100), nullable=True),
        description='Sheet range for results: "Sheet1!E:H"',
    )
    # Summary only (no per-row results - those go to Sheets)
    summary: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSON, nullable=True),
        description="Job summary: top_errors, provider_breakdown",
    )
    error_message: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, nullable=True),
        description="Error message if job failed",
    )
    # Timestamps
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    started_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    completed_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    created_by: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="User ID who created this job",
    )
    notify_email: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Email address for job completion notification",
    )
    spreadsheet_url: Optional[str] = Field(
        default=None,
        sa_column=Column(String(500), nullable=True),
        description="URL back to the spreadsheet with results",
    )


class ScraperJobStatus(str, Enum):
    """Status of a scraper job."""
    QUEUED = "queued"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class ScraperJob(Base, table=True):
    """Job tracking for BYO Apify web scrapers."""

    __tablename__ = "scraper_jobs"
    __table_args__ = (
        UniqueConstraint("workspace_id", "run_id", name="uq_scraper_jobs_run_id"),
        Index("ix_scraper_jobs_workspace", "workspace_id", "created_at"),
        {"extend_existing": True},
    )

    id: UUID = Field(
        default_factory=generate_uuid_object,
        sa_column=Column(PGUUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")),
    )
    workspace_id: str = Field(
        sa_column=Column(String(255), nullable=False),
        description="Workspace/org ID",
    )
    run_id: str = Field(
        sa_column=Column(String(50), nullable=False),
        description="Idempotency key for this run",
    )
    status: str = Field(
        default=ScraperJobStatus.QUEUED.value,
        sa_column=Column(String(20), nullable=False, server_default=text("'queued'")),
        description="Job status: queued, running, completed, failed, cancelled",
    )
    actor_type: str = Field(
        sa_column=Column(String(50), nullable=False),
        description="Curated actor type ID",
    )
    actor_id: str = Field(
        sa_column=Column(String(200), nullable=False),
        description="Apify actor ID",
    )
    input_mapping: Dict[str, Any] = Field(
        default_factory=dict,
        sa_column=Column(JSON, nullable=False),
        description='Column mappings: {"company": "A"}',
    )
    output_fields: List[str] = Field(
        default_factory=list,
        sa_column=Column(JSON, nullable=False),
        description='Fields to output: ["full_name", "title"]',
    )
    rows_total: int = Field(
        sa_column=Column(Integer, nullable=False),
        description="Total rows to process",
    )
    rows_processed: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_scraped: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_skipped: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    rows_errored: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
    )
    error_message: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, nullable=True),
        description="Error message if job failed",
    )
    inputs_uri: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, nullable=True),
        description="GCS URI for encrypted input rows",
    )
    results_uri: Optional[str] = Field(
        default=None,
        sa_column=Column(Text, nullable=True),
        description="GCS URI for encrypted results",
    )
    cursor: int = Field(
        default=0,
        sa_column=Column(Integer, nullable=False, server_default=text("0")),
        description="Cursor for resume",
    )
    apify_run_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(100), nullable=True),
        description="Last Apify run ID (if applicable)",
    )
    lease_expires_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="Lease expiration (NULL = not leased)",
    )
    lease_holder: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Task ID holding the lease",
    )
    sheet_id: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Spreadsheet ID for scheduled writeback",
    )
    sheet_name: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Sheet tab name for scheduled writeback",
    )
    notify_email: Optional[str] = Field(
        default=None,
        sa_column=Column(String(255), nullable=True),
        description="Email address for job completion notification",
    )
    created_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    updated_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False, server_default=text("CURRENT_TIMESTAMP")),
    )
    started_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    completed_at: Optional[datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    expires_at: datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
        description="Expiry timestamp for cleanup",
    )
