"""
SQLAlchemy 2.0 ORM models for billing and BYO API key storage.

Models:
  TenantLlmKey — stores encrypted BYO API keys per tenant per provider
  StripeEvent  — idempotency table for processed Stripe webhook events

Design notes:
  - TenantLlmKey uses AuditBase (same separate declarative base as audit_events)
    because tenant_llm_keys is a sensitive, compliance-relevant table.
  - StripeEvent uses Base (same as tenants/agents) because it is a simple
    idempotency guard, not a sensitive record.
  - TenantLlmKey has RLS enabled (tenant isolation enforced at DB level via
    Alembic migration 005). Only konstruct_app SELECT, INSERT, DELETE are
    granted — UPDATE is not (keys are immutable; to change a key, delete and
    re-create).
  - key_hint stores the last 4 characters of the plaintext API key so the
    portal can display "...ABCD" without decrypting the stored ciphertext.
"""

from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column

from shared.models.audit import AuditBase
from shared.models.tenant import Base


class TenantLlmKey(AuditBase):
    """
    Encrypted BYO API key for a specific LLM provider, scoped to a tenant.

    One row per (tenant_id, provider) pair — enforced by UNIQUE constraint.

    RLS is ENABLED — tenant_id isolation enforced at DB level.
    konstruct_app has SELECT, INSERT, DELETE only (no UPDATE).
    To rotate a key: DELETE existing row, INSERT new row with updated key.

    Fields:
      provider      — LLM provider name, e.g. "openai", "anthropic"
      label         — Human-readable label, e.g. "Production OpenAI Key"
      encrypted_key — Fernet-encrypted API key (via KeyEncryptionService)
      key_hint      — Last 4 chars of plaintext key for portal display ("...XXXX")
      key_version   — Incremented on key rotation (future use for audit)
    """

    __tablename__ = "tenant_llm_keys"
    __table_args__ = (
        UniqueConstraint("tenant_id", "provider", name="uq_tenant_llm_key_provider"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid4,
    )
    tenant_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("tenants.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    provider: Mapped[str] = mapped_column(
        Text,
        nullable=False,
        comment="LLM provider name: openai | anthropic | cohere | groq | etc.",
    )
    label: Mapped[str] = mapped_column(
        Text,
        nullable=False,
        comment="Human-readable label for the key in the portal",
    )
    encrypted_key: Mapped[str] = mapped_column(
        Text,
        nullable=False,
        comment="Fernet-encrypted API key ciphertext — NEVER logged or exposed via API",
    )
    key_hint: Mapped[str | None] = mapped_column(
        String(4),
        nullable=True,
        comment="Last 4 characters of plaintext key for portal display (e.g. 'ABCD')",
    )
    key_version: Mapped[int] = mapped_column(
        Integer,
        nullable=False,
        default=1,
        comment="Incremented on key rotation for audit trail",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )

    def __repr__(self) -> str:
        return f"<TenantLlmKey id={self.id} provider={self.provider!r} tenant={self.tenant_id}>"


class StripeEvent(Base):
    """
    Idempotency guard for processed Stripe webhook events.

    Stripe guarantees at-least-once delivery, so the same event may arrive
    multiple times. This table prevents duplicate processing via an
    INSERT ... ON CONFLICT DO NOTHING pattern checked before each handler.

    Fields:
      event_id     — Stripe event ID (e.g. "evt_1AbCdEfGhIjKlMnO") — primary key
      processed_at — Timestamp when the event was first successfully processed
    """

    __tablename__ = "stripe_events"

    event_id: Mapped[str] = mapped_column(
        Text,
        primary_key=True,
        comment="Stripe event ID — globally unique per Stripe account",
    )
    processed_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )

    def __repr__(self) -> str:
        return f"<StripeEvent event_id={self.event_id!r}>"
