"""
SQLAlchemy model factory for third_party_credentials.

This module provides a factory function to create the ThirdPartyCredential
model with a custom SQLAlchemy Base class, allowing services to use their
own declarative base while sharing the schema definition.

Example:
    from sqlalchemy.orm import declarative_base
    from empowernow_common.vault.user_oauth.models import create_third_party_credential_model

    Base = declarative_base()
    ThirdPartyCredential = create_third_party_credential_model(Base)

    # Now use ThirdPartyCredential in your SQLAlchemy session
"""

from datetime import datetime, timezone
import uuid as uuid_lib
from typing import Any, Type

from sqlalchemy import Column, String, LargeBinary, DateTime, Integer, Index
from sqlalchemy.dialects.postgresql import UUID as PGUUID, JSONB
from sqlalchemy.types import ARRAY


def create_third_party_credential_model(Base: Any) -> Type:
    """
    Create ThirdPartyCredential model with custom Base.

    This factory approach allows each service (CRUDService, MCP Gateway, etc.)
    to use their own SQLAlchemy Base while sharing the same table schema.

    Args:
        Base: SQLAlchemy declarative base class

    Returns:
        ThirdPartyCredential model class

    Note:
        The returned model class is specific to the provided Base.
        Creating multiple models with different Bases will result in
        different class objects, but they all map to the same table.
    """

    class ThirdPartyCredential(Base):
        """
        Stores encrypted OAuth credentials for users.

        This table stores both system credentials (API keys, service accounts)
        and user-bound OAuth tokens. User OAuth credentials are identified by:
        - type = "oauth2_user_token"
        - user_arn is set
        - system_key identifies the external system

        Encryption:
            The enc_blob column contains FIPS 140-3 compliant AES-256-GCM
            encrypted token data. See empowernow_common.crypto.fips_aead.

        Multi-tenancy:
            When tenant_id is set, credentials are isolated per tenant.
            Queries should always include tenant_id in the WHERE clause.
        """

        __tablename__ = "third_party_credentials"

        # Primary key
        id = Column(
            PGUUID(as_uuid=True),
            primary_key=True,
            default=uuid_lib.uuid4,
        )

        # Core credential fields
        name = Column(
            String(120),
            nullable=False,
            unique=True,
            index=True,
            comment="Unique name for the credential (tenant:user:system format)",
        )
        type = Column(
            String(30),
            nullable=False,
            index=True,
            comment="Credential type: oauth2_user_token, api_key, service_account, etc.",
        )
        enc_blob = Column(
            LargeBinary,
            nullable=False,
            comment="AES-256-GCM encrypted token payload",
        )
        owner_arn = Column(
            String(256),
            nullable=False,
            index=True,
            default="system",
            comment="ARN of credential owner (system or user ARN)",
        )
        last_rotated = Column(
            DateTime(timezone=True),
            nullable=True,
            comment="When credential was last rotated (for API keys)",
        )

        # User OAuth specific fields
        user_arn = Column(
            String(512),
            nullable=True,
            index=True,
            comment="User ARN for user-bound OAuth tokens",
        )
        tenant_id = Column(
            String(128),
            nullable=True,
            index=True,
            comment="Tenant ID for multi-tenant isolation",
        )
        system_key = Column(
            String(128),
            nullable=True,
            index=True,
            comment="External system identifier (salesforce, google, etc.)",
        )
        system_user_id = Column(
            String(256),
            nullable=True,
            comment="User's ID in the external system",
        )
        system_user_email = Column(
            String(256),
            nullable=True,
            comment="User's email in the external system",
        )
        credential_scopes = Column(
            ARRAY(String),
            nullable=True,
            comment="OAuth scopes granted",
        )
        oauth_metadata = Column(
            JSONB,
            nullable=True,
            comment="Additional OAuth metadata (instance_url, etc.)",
        )
        status = Column(
            String(30),
            nullable=True,
            default="active",
            comment="Credential status: active, needs_reauth, revoked, expired",
        )
        last_refreshed_at = Column(
            DateTime(timezone=True),
            nullable=True,
            comment="When tokens were last refreshed",
        )
        last_used_at = Column(
            DateTime(timezone=True),
            nullable=True,
            comment="When credential was last used for an API call",
        )
        refresh_failure_count = Column(
            Integer,
            nullable=True,
            default=0,
            comment="Number of consecutive refresh failures",
        )

        # Timestamps
        created_at = Column(
            DateTime(timezone=True),
            nullable=False,
            default=lambda: datetime.now(timezone.utc),
            index=True,
        )
        updated_at = Column(
            DateTime(timezone=True),
            nullable=False,
            default=lambda: datetime.now(timezone.utc),
            onupdate=lambda: datetime.now(timezone.utc),
            index=True,
        )

        # Composite index for OAuth lookups
        __table_args__ = (
            Index(
                "ix_oauth_lookup",
                "user_arn",
                "system_key",
                "tenant_id",
                "type",
                "status",
            ),
        )

        def __repr__(self) -> str:
            return f"<ThirdPartyCredential {self.name}:{self.type}>"

    return ThirdPartyCredential
