"""
Public schema ORM models for ModuleDesk.

These tables live in the PostgreSQL 'public' schema and are shared across
all tenants. Each tenant organisation gets its own isolated schema (e.g.
tenant_acme) for ticket/product/AI data; the public schema holds the
multi-tenant control plane: organisations, users, billing, and audit.

Use PublicBase (not Base from db.py) for these models so Alembic can
manage them independently from the per-tenant tenant schema.
"""

from __future__ import annotations

import datetime as dt
from typing import List, Optional

from sqlalchemy import (
    Column,
    Integer,
    String,
    Boolean,
    DateTime,
    Text,
    ForeignKey,
    UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase, relationship, Mapped, mapped_column


class PublicBase(DeclarativeBase):
    """Declarative base for all public-schema models."""


class Organization(PublicBase):
    """
    A tenant organisation — one per ModuleDesk customer (seller).

    plan values: 'internal' | 'free' | 'basic' | 'pro'
    'internal' is the platform owner account: unlimited everything, no billing.
    """
    __tablename__ = "organizations"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    slug: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
    schema_name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)

    # Billing
    plan: Mapped[str] = mapped_column(String(20), nullable=False, default="free")
    stripe_customer_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    stripe_subscription_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)

    # AI credits (org-wide pool, non-accumulating)
    ai_credits_used: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    ai_credits_reset_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, nullable=True)

    # Encrypted secrets (Fernet-encrypted at rest)
    openai_api_key_encrypted: Mapped[Optional[bytes]] = mapped_column(Text, nullable=True)
    addons_api_key_encrypted: Mapped[Optional[bytes]] = mapped_column(Text, nullable=True)

    # Onboarding state
    email_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    onboarding_complete: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    # Suspension (set by platform admin)
    is_suspended: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    suspended_reason: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)

    created_at: Mapped[dt.datetime] = mapped_column(
        DateTime, default=dt.datetime.utcnow, nullable=False
    )

    users: Mapped[List[User]] = relationship(
        "User", back_populates="org", cascade="all,delete-orphan"
    )
    audit_logs: Mapped[List[PublicAuditLog]] = relationship(
        "PublicAuditLog", back_populates="org", cascade="all,delete-orphan"
    )


class User(PublicBase):
    """
    A ModuleDesk user account, always tied to one organisation.

    role values: 'platform_admin' | 'admin' | 'agent'
    'platform_admin' can access /admin/* (operator console).
    'admin' is the org owner/manager.
    'agent' is a support agent within the org.
    """
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    org_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False
    )
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
    password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
    role: Mapped[str] = mapped_column(String(20), nullable=False, default="admin")

    # Email verification
    email_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    email_verify_token: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    email_verify_sent_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, nullable=True)

    # Team management
    invited_by_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )

    created_at: Mapped[dt.datetime] = mapped_column(
        DateTime, default=dt.datetime.utcnow, nullable=False
    )
    last_login_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, nullable=True)

    org: Mapped[Organization] = relationship(
        "Organization", back_populates="users", lazy="joined"
    )
    invited_by: Mapped[Optional[User]] = relationship(
        "User", remote_side="User.id", foreign_keys=[invited_by_id]
    )
    audit_logs: Mapped[List[PublicAuditLog]] = relationship(
        "PublicAuditLog", back_populates="user"
    )


class Plan(PublicBase):
    """
    Static plan definitions — seeded on deploy.

    module_limit: max modules on this plan (None = unlimited)
    ai_credits_per_month: platform AI credits per month (None = unlimited / BYOK)
    template_limit: max predefined message templates (None = unlimited)
    """
    __tablename__ = "plans"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    name: Mapped[str] = mapped_column(String(20), unique=True, nullable=False)
    price_per_module_cents: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    ai_credits_per_month: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    module_limit: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    template_limit: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    features_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True)


class PublicAuditLog(PublicBase):
    """
    Audit trail for platform-level and org-level actions.

    Replaces the tenant-schema AuditLog that previously FK'd to users_admin.
    Now lives in public schema and references public.users + public.organizations.
    """
    __tablename__ = "audit_log"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    org_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("organizations.id", ondelete="SET NULL"), nullable=True
    )
    user_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )
    action: Mapped[str] = mapped_column(String(100), nullable=False)
    payload_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    ip_address: Mapped[Optional[str]] = mapped_column(String(45), nullable=True)
    created_at: Mapped[dt.datetime] = mapped_column(
        DateTime, default=dt.datetime.utcnow, nullable=False
    )

    org: Mapped[Optional[Organization]] = relationship("Organization", back_populates="audit_logs")
    user: Mapped[Optional[User]] = relationship("User", back_populates="audit_logs")
