# #region ConfigModels [C:3] [TYPE Module] [SEMANTICS pydantic, model, schedule]
# @defgroup Core Module group.
# @BRIEF Defines the data models for application configuration using Pydantic.
# @LAYER Core
# @RELATION IMPLEMENTS -> [CoreContracts]
# @RELATION IMPLEMENTS -> [ConnectionContracts]
#
# #region CoreContracts [C:2] [TYPE Interface]
# @ingroup Core
# @BRIEF Contract for core configuration models — Pydantic BaseModel subclasses with scheduler config.
# #endregion CoreContracts
#
# #region ConnectionContracts [C:2] [TYPE Interface]
# @ingroup Core
# @BRIEF Contract for database/environment connection configuration models.
# #endregion ConnectionContracts

import uuid
from datetime import datetime, timezone

from pydantic import BaseModel, Field, field_validator

from ..models.storage import StorageConfig
from ..services.llm_prompt_templates import (
    DEFAULT_LLM_ASSISTANT_SETTINGS,
    DEFAULT_LLM_PROMPTS,
    DEFAULT_LLM_PROVIDER_BINDINGS,
)


# #region Schedule [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents a backup schedule configuration.
class Schedule(BaseModel):
    enabled: bool = False
    cron_expression: str = "0 0 * * *"  # Default: daily at midnight


# #endregion Schedule


# #region Environment [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents a Superset environment configuration with async connection pool settings.
class Environment(BaseModel):
    id: str
    name: str
    url: str
    username: str
    password: str  # Will be masked in UI
    stage: str = Field(default="DEV", pattern="^(DEV|PREPROD|PROD)$")
    verify_ssl: bool = True
    timeout: int = 30
    is_default: bool = False
    is_production: bool = False
    backup_schedule: Schedule = Field(default_factory=Schedule)

    # Async connection pool settings (per-environment)
    connection_pool_size: int = Field(
        default=20, ge=1, le=100,
        description="Max concurrent connections to this Superset environment"
    )
    connection_pool_timeout: int = Field(
        default=30, ge=5, le=300,
        description="Seconds to wait for a connection pool slot before timeout"
    )
    request_timeout: int = Field(
        default=30, ge=5, le=300,
        description="Default HTTP request timeout for Superset API calls"
    )
    llm_timeout: int = Field(
        default=120, ge=30, le=600,
        description="Timeout for LLM API calls (seconds)"
    )


# #endregion Environment


# #region AppAsyncRuntimeConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Global application-level async runtime configuration.
# @RATIONALE Separated from EnvironmentConfig because these settings are not per-env:
#   executor workers, shutdown timeout, blocking queue timeout are app-wide.
class AppAsyncRuntimeConfig(BaseModel):
    db_executor_workers: int = Field(
        default=10, ge=1, le=50,
        description="Max worker threads for database blocking operations"
    )
    file_executor_workers: int = Field(
        default=10, ge=1, le=50,
        description="Max worker threads for file I/O blocking operations"
    )
    git_executor_workers: int = Field(
        default=5, ge=1, le=20,
        description="Max worker threads for Git blocking operations"
    )
    graceful_shutdown_timeout: int = Field(
        default=30, ge=10, le=120,
        description="Graceful shutdown timeout in seconds"
    )
    blocking_queue_timeout: int = Field(
        default=30, ge=5, le=120,
        description="Timeout for acquiring blocking executor queue slot"
    )
    event_bus_maxsize: int = Field(
        default=10000, ge=100, le=100000,
        description="Max pending events in EventBus per subscriber queue"
    )


# #endregion AppAsyncRuntimeConfig


# #region LoggingConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Defines the configuration for the application's logging system.
class LoggingConfig(BaseModel):
    level: str = "INFO"
    task_log_level: str = (
        "INFO"  # Minimum level for task-specific logs (DEBUG, INFO, WARNING, ERROR)
    )
    file_path: str | None = None
    max_bytes: int = 10 * 1024 * 1024
    backup_count: int = 5
    enable_belief_state: bool = True


# #endregion LoggingConfig


# #region CleanReleaseConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF Configuration for clean release compliance subsystem.
class CleanReleaseConfig(BaseModel):
    active_policy_id: str | None = None
    active_registry_id: str | None = None


# #endregion CleanReleaseConfig


# #region FeaturesConfig [C:1] [TYPE DataClass]
# @BRIEF Top-level feature flags that toggle entire project features on/off.
# @RATIONALE  Features are read from environment variables on bootstrap and persisted in DB.
#            DB is source of truth after initial bootstrap; env vars only seed defaults.
# @INVARIANT Each feature flag controls visibility of plugin/service in UI and API access.
class FeaturesConfig(BaseModel):
    # ── Core Features ────────────────────────────────────────
    dataset_review: bool = True
    health_monitor: bool = True

    # ── Plugin Features ──────────────────────────────────────
    # Translation subsystem
    translate: bool = True                    # LLM Table Translation (main)
    translate_scheduling: bool = True          # Cron-based translation scheduling
    translate_dictionaries: bool = True        # Terminology dictionary management

    # Dashboard migration & version control
    migration: bool = True                    # Superset Dashboard Migration
    git_integration: bool = True              # Git Integration

    # System tools
    backup: bool = True                       # Superset Dashboard Backup
    debug: bool = True                        # System Diagnostics
    storage_manager: bool = True              # Storage Manager
    dataset_mapper: bool = True               # Dataset Column Mapper
    search_datasets: bool = True              # Search Datasets
    maintenance_banner: bool = True           # Maintenance Banner

    # LLM analysis tools
    llm_dashboard_validation: bool = True     # Dashboard LLM Validation
    llm_documentation: bool = True            # Dataset LLM Documentation


# #endregion FeaturesConfig


# #region DatabaseConnection [C:1] [TYPE DataClass] [SEMANTICS settings,connections,database]
# @ingroup Core
# @BRIEF A reusable database connection configuration stored in GlobalSettings.connections.
# @RATIONALE Typed model prevents config corruption from ad-hoc dicts. Replaces the existing
#           `connections: list[dict]` stub. connection_id references the UUID in this list — not a DB FK.
# @REJECTED Keeping `list[dict]` was rejected — would require per-field validation in every consumer.
#           A separate DB table was rejected — connections are configuration, not domain data.
class DatabaseConnection(BaseModel):
    id: str = Field(default_factory=lambda: uuid.uuid4().hex[:12])
    name: str
    host: str
    port: int = 5432
    database: str
    username: str
    password: str  # Fernet-encrypted at rest; masked as "********" in API responses
    dialect: str = "postgresql"
    extra_params: dict = Field(default_factory=dict)
    pool_size: int = Field(default=5, ge=1, le=100)
    created_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
    updated_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())

    @field_validator("dialect")
    @classmethod
    def validate_dialect(cls, v: str) -> str:
        allowed = {"postgresql", "clickhouse", "mysql"}
        if v.lower() not in allowed:
            raise ValueError(f"Unsupported dialect: {v}. Allowed: {', '.join(sorted(allowed))}")
        return v.lower()

    @field_validator("port")
    @classmethod
    def validate_port(cls, v: int) -> int:
        if v < 1 or v > 65535:
            raise ValueError(f"Port must be between 1 and 65535, got {v}")
        return v

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Connection name must not be empty")
        return v.strip()


# #endregion DatabaseConnection


# #region GlobalSettings [TYPE DataClass]
# @ingroup Core
# @BRIEF Represents global application settings.
class GlobalSettings(BaseModel):
    storage: StorageConfig = Field(default_factory=StorageConfig)
    clean_release: CleanReleaseConfig = Field(default_factory=CleanReleaseConfig)
    default_environment_id: str | None = None
    logging: LoggingConfig = Field(default_factory=LoggingConfig)
    features: FeaturesConfig = Field(default_factory=FeaturesConfig)
    connections: list[DatabaseConnection] = []
    llm: dict = Field(
        default_factory=lambda: {
            "providers": [],
            "default_provider": "",
            "prompts": dict(DEFAULT_LLM_PROMPTS),
            "provider_bindings": dict(DEFAULT_LLM_PROVIDER_BINDINGS),
            **dict(DEFAULT_LLM_ASSISTANT_SETTINGS),
        }
    )

    # Application timezone
    app_timezone: str = "Europe/Moscow"

    @field_validator("app_timezone")
    @classmethod
    def validate_app_timezone(cls, v: str) -> str:
        from zoneinfo import ZoneInfo
        try:
            ZoneInfo(v)
        except (KeyError, TypeError):
            raise ValueError(f"Invalid IANA timezone: {v}") from None
        return v

    # Task retention settings
    task_retention_days: int = 30
    task_retention_limit: int = 100
    pagination_limit: int = 10

    # Migration sync settings
    migration_sync_cron: str = "0 2 * * *"

    # Dataset Review Feature Flags
    ff_dataset_auto_review: bool = True
    ff_dataset_clarification: bool = True
    ff_dataset_execution: bool = True

    # LLM validation retention (days)
    LLM_SCREENSHOT_RETENTION_DAYS: int = 30
    LLM_RAW_RESPONSE_RETENTION_DAYS: int = 30

    # Global worker limit for concurrent validation runs
    GLOBAL_VALIDATION_WORKER_LIMIT: int = 3

    # Allowed languages for translation (BCP-47 codes)
    allowed_languages: list[str] = Field(
        default_factory=lambda: [
            "ru", "en", "de", "fr", "es", "it", "pt", "zh", "ja", "ko",
            "ar", "tr", "nl", "pl", "sv", "da", "fi", "cs", "hu", "ro",
            "vi", "th", "he", "id", "ms",
        ]
    )

    @field_validator("allowed_languages")
    @classmethod
    def validate_allowed_languages(cls, v: list[str]) -> list[str]:
        import re
        for tag in v:
            if not tag or not tag.strip():
                raise ValueError("Empty language code is not allowed")
            if not re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag.strip()):
                raise ValueError(f"Invalid BCP-47 language code: {tag}")
        return v


# #endregion GlobalSettings


# #region AppConfig [TYPE DataClass]
# @ingroup Core
# @BRIEF The root configuration model containing all application settings.
class AppConfig(BaseModel):
    environments: list[Environment] = []
    settings: GlobalSettings


# #endregion AppConfig

# #endregion ConfigModels
