"""Application configuration loaded from environment variables."""
from functools import lru_cache
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
import secrets


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")

    # Application
    APP_NAME: str = "BulkGmailMailer"
    ENVIRONMENT: str = "development"
    SECRET_KEY: str = "change-me-in-production"
    BASE_URL: str = "http://localhost:8000"
    FRONTEND_BASE_URL: str = "http://localhost:8000"

    # Database / Redis
    DATABASE_URL: str = "postgresql+psycopg://mailer:mailer_dev_pw@localhost:5432/bulkmailer"
    REDIS_URL: str = "redis://localhost:6379/0"
    CELERY_BROKER_DB: int = 1
    CELERY_RESULT_DB: int = 2

    # Security
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 12
    SESSION_COOKIE_NAME: str = "mailer_session"
    CSRF_COOKIE_NAME: str = "mailer_csrf"
    ENCRYPTION_KEY: str = ""  # Fernet key; auto-generated & persisted if empty
    PASSWORD_MIN_LENGTH: int = 10

    # Rate limiting
    RATE_LIMIT_PER_MINUTE: int = 240

    # Uploads
    MAX_UPLOAD_MB: int = 10
    UPLOAD_DIR: str = "uploads"
    ALLOWED_ATTACHMENT_EXTS: str = "pdf,html,htm,txt,png,jpg,jpeg"
    ALLOWED_TEMPLATE_EXTS: str = "html,htm,txt"

    # Recipients / campaign limits
    MAX_RECIPIENTS_PER_CAMPAIGN: int = 2000
    MAX_GMAIL_ACCOUNTS_PER_CAMPAIGN: int = 10
    MAX_SUBJECTS: int = 1000

    # Sending defaults (admin-configurable at runtime via ApplicationSettings table)
    DEFAULT_SEND_INTERVAL_SECONDS: float = 1.0
    DEFAULT_MAX_PER_ACCOUNT_PER_CAMPAIGN: int = 200
    DEFAULT_RETRIES: int = 3
    DEFAULT_RETRY_DELAY_SECONDS: int = 60
    MAX_ATTACHMENT_TOTAL_MB: int = 25
    MAX_ATTACHMENT_PER_FILE_MB: float = 15.0

    # Amount ranges (defaults; admin-configurable)
    AMOUNT_MIN: float = 300.0
    AMOUNT_MAX: float = 1500.0
    SHIPPING_MIN: float = 5.0
    SHIPPING_MAX: float = 50.0

    # Invoice number format tokens:
    #   {prefix}  literal prefix e.g. INV-
    #   {r}       lowercase alphanumeric random block
    #   {R}       uppercase alphanumeric random block
    #   {d}       decimal digits only
    #   {seq4}    4-digit campaign-scoped sequence
    #   {date}    YYYYMMDD
    INVOICE_NUMBER_FORMAT: str = "{prefix}{R4}-{R5}-{R6}-{seq4}"
    INVOICE_PREFIX: str = "INV-"

    # Unsubscribe
    UNSUBSCRIBE_BASE: str = ""  # defaults to BASE_URL
=== db/session ===
"""Async SQLAlchemy engine/session and sync engine for Celery workers."""
import os
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from app.core.config import settings
from app.models import Base

DB_URL = settings.DATABASE_URL
if DB_URL.startswith("postgres://"):
    DB_URL = DB_URL.replace("postgres://", "postgresql://", 1)

sync_engine = create_engine(DB_URL, pool_pre_ping=True, pool_size=5, max_overflow=10)
SyncSession = sessionmaker(bind=sync_engine, expire_on_commit=False)

async_engine = create_async_engine(DB_URL, pool_pre_ping=True, pool_size=5, max_overflow=10)
AsyncSessionLocal = async_sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)


async def init_db() -> None:
    """Create tables (dev convenience; production uses Alembic migrations)."""
    async with async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)


async def close_engines() -> None:
    await async_engine.dispose()
    sync_engine.dispose()


def check_db_health() -> bool:
    try:
        with sync_engine.connect() as c:
            c.execute(text("SELECT 1"))
        return True
    except Exception:
        return False
