"""Application configuration for the Aegis MITRE ATT&CK Coverage Platform.

Loads settings from environment variables and ``.env`` files via
``pydantic-settings``.  Validates critical secrets at import time and raises
``RuntimeError`` (production) or issues a ``UserWarning`` (development) when
unsafe defaults are detected.
"""

# Import os
import os

# Import secrets
import secrets

# Import warnings
import warnings

# Import BaseSettings from pydantic_settings
from pydantic_settings import BaseSettings

# ---------------------------------------------------------------------------
# Detect environment: "production" when AEGIS_ENV or common indicators are set
# ---------------------------------------------------------------------------
_is_production = os.environ.get("AEGIS_ENV", "").lower() == "production"


# Define class Settings
class Settings(BaseSettings):
    """Application settings loaded from environment variables and .env file."""

    # Assign DATABASE_URL = "postgresql://postgres:postgres@postgres:5432/attackdb"
    DATABASE_URL: str = "postgresql://postgres:postgres@postgres:5432/attackdb"

    # ── Security ──────────────────────────────────────────────────────
    # SECRET_KEY has NO safe default.  In development a random key is
    # generated at startup (tokens invalidate on restart — acceptable
    # for local dev).  In production it MUST be supplied via env/.env
    # so tokens survive restarts.
    SECRET_KEY: str = ""
    # Assign ALGORITHM = "HS256"
    ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 480  # 8 hours — /auth/refresh extends active sessions

    # ── Redis ─────────────────────────────────────────────────────────
    REDIS_URL: str = "redis://redis:6379/0"
    # Logical DB indices on the same Redis instance (PATH in URL is overridden).
    REDIS_TOKEN_BLACKLIST_DB: int = 1
    # Assign REDIS_CACHE_DB = 2
    REDIS_CACHE_DB: int = 2

    # ── CORS ─────────────────────────────────────────────────────────
    # Comma-separated list of allowed origins, or a JSON array.
    # In dev this defaults to common local ports; in production set it
    # to the actual frontend domain(s).
    CORS_ORIGINS: str = "http://localhost:3000,http://localhost:5173"

    # ── MinIO / S3 ───────────────────────────────────────────────────
    MINIO_ENDPOINT: str = "minio:9000"
    # Public hostname used in presigned URLs returned to browsers.
    # In production set this to <server-ip>:9000 (or a public FQDN) so
    # the browser can reach MinIO directly.  Defaults to MINIO_ENDPOINT.
    MINIO_PUBLIC_ENDPOINT: str = ""
    MINIO_ACCESS_KEY: str = "minioadmin"
    # Assign MINIO_SECRET_KEY = "minioadmin"
    MINIO_SECRET_KEY: str = "minioadmin"
    # Assign MINIO_BUCKET = "evidence"
    MINIO_BUCKET: str = "evidence"
    # Assign MINIO_SECURE = False  # True → use HTTPS to connect to MinIO
    MINIO_SECURE: bool = False  # True → use HTTPS to connect to MinIO

    # ── Re-testing ───────────────────────────────────────────────────
    MAX_RETEST_COUNT: int = 3  # maximum automatic retests per original test

    # ── Jira Integration ────────────────────────────────────────────
    JIRA_ENABLED: bool = False
    # Assign JIRA_URL = ""
    JIRA_URL: str = ""
    # Assign JIRA_USERNAME = ""
    JIRA_USERNAME: str = ""
    # Assign JIRA_API_TOKEN = ""
    JIRA_API_TOKEN: str = ""
    # Assign JIRA_IS_CLOUD = True
    JIRA_IS_CLOUD: bool = True
    # Assign JIRA_DEFAULT_PROJECT = ""
    JIRA_DEFAULT_PROJECT: str = ""
    JIRA_ISSUE_TYPE_TEST: str = "Task"       # tests (campaign or standalone)
    JIRA_ISSUE_TYPE_CAMPAIGN: str = "Epic"   # campaigns (under Initiative)
    # Jira custom field ID for "Start date" — Jira Cloud team-managed: customfield_10015
    # Override with the correct field ID for your Jira instance if different.
    JIRA_START_DATE_FIELD: str = "customfield_10015"

    # ── Tempo Integration ─────────────────────────────────────────────
    TEMPO_ENABLED: bool = False
    # Assign TEMPO_API_TOKEN = ""
    TEMPO_API_TOKEN: str = ""
    # Assign TEMPO_API_VERSION = 4
    TEMPO_API_VERSION: int = 4
    # Assign TEMPO_DEFAULT_WORK_TYPE = "Red Team"
    TEMPO_DEFAULT_WORK_TYPE: str = "Red Team"
    # Tempo API base URL — use https://api.eu.tempo.io/4 for EU workspaces.
    # Can also be set via system_configs key "tempo.base_url" at runtime.
    TEMPO_BASE_URL: str = ""  # empty → falls back to https://api.tempo.io/4

    # ── OSINT / Intelligence ────────────────────────────────────────
    NVD_API_KEY: str = ""  # optional; increases NVD rate limit from 5/30s to 50/30s
    # Assign STALE_THRESHOLD_DAYS = 365  # days before coverage is considered stale
    STALE_THRESHOLD_DAYS: int = 365  # days before coverage is considered stale

    # ── Reporting ─────────────────────────────────────────────────────
    REPORT_TEMPLATES_DIR: str = "app/templates/reports"
    # Assign REPORT_OUTPUT_DIR = "/tmp/aegis_reports"
    REPORT_OUTPUT_DIR: str = "/app/reports"
    # Assign COMPANY_NAME = "Organization"
    COMPANY_NAME: str = "Organization"
    # Assign COMPANY_LOGO_PATH = "app/templates/reports/assets/logo.png"
    COMPANY_LOGO_PATH: str = "app/templates/reports/assets/logo.png"

    # ── Email / SMTP ──────────────────────────────────────────────────
    SMTP_ENABLED: bool = False
    SMTP_HOST: str = ""
    SMTP_PORT: int = 587
    SMTP_USERNAME: str = ""
    SMTP_PASSWORD: str = ""
    SMTP_FROM_EMAIL: str = "aegis@company.com"
    SMTP_USE_TLS: bool = True
    PLATFORM_URL: str = "http://localhost:5173"  # base URL for links in emails

    # ── Scoring weights (must sum to 100) ────────────────────────────
    SCORING_WEIGHT_TESTS: int = 40
    # Assign SCORING_WEIGHT_DETECTION_RULES = 25
    SCORING_WEIGHT_DETECTION_RULES: int = 25
    # Assign SCORING_WEIGHT_D3FEND = 15
    SCORING_WEIGHT_D3FEND: int = 15
    # Assign SCORING_WEIGHT_RECENCY = 10
    SCORING_WEIGHT_RECENCY: int = 10
    # Assign SCORING_WEIGHT_SEVERITY = 10
    SCORING_WEIGHT_SEVERITY: int = 10
    # Legacy env names (mapped in scoring_config_service)
    SCORING_WEIGHT_FRESHNESS: int = 10
    # Assign SCORING_WEIGHT_PLATFORM_DIVERSITY = 10
    SCORING_WEIGHT_PLATFORM_DIVERSITY: int = 10

    # Define class Config
    class Config:
        """Pydantic BaseSettings configuration — load from .env file."""

        # Assign env_file = ".env"
        env_file = ".env"


# Assign settings = Settings()
settings = Settings()

# ---------------------------------------------------------------------------
# Post-init validation for SECRET_KEY
# ---------------------------------------------------------------------------
_UNSAFE_SECRETS = {
    # Literal argument value
    "",
    # Literal argument value
    "change-me-in-production",
    # Literal argument value
    "change-me-in-production-use-a-long-random-string",
}

# Check: settings.SECRET_KEY in _UNSAFE_SECRETS
if settings.SECRET_KEY in _UNSAFE_SECRETS:
    # Check: _is_production
    if _is_production:
        # Raise RuntimeError
        raise RuntimeError(
            # Literal argument value
            "CRITICAL: SECRET_KEY is not configured.  "
            # Literal argument value
            "Set a strong random value (>= 32 chars) via the SECRET_KEY "
            # Literal argument value
            "environment variable or in your .env file before running in "
            # Literal argument value
            "production.  Example:  openssl rand -hex 32"
        )
    # Development: auto-generate an ephemeral key and warn
    settings.SECRET_KEY = secrets.token_hex(32)
    # Call warnings.warn()
    warnings.warn(
        # Literal argument value
        "SECRET_KEY was not set — using an auto-generated ephemeral key. "
        # Literal argument value
        "JWT tokens will be invalidated on every restart.  "
        # Literal argument value
        "Set SECRET_KEY in your environment for persistent sessions.",
        # Keyword argument: stacklevel
        stacklevel=2,
    )

# ---------------------------------------------------------------------------
# SEC-002: Reject default credentials in production
# ---------------------------------------------------------------------------
if _is_production:
    # Assign _DEFAULT_CREDS = {
    _DEFAULT_CREDS = {
        ("MINIO_ACCESS_KEY", settings.MINIO_ACCESS_KEY, "minioadmin"),
        ("MINIO_SECRET_KEY", settings.MINIO_SECRET_KEY, "minioadmin"),
    }
    # Iterate over _DEFAULT_CREDS
    for name, current, default in _DEFAULT_CREDS:
        # Check: current == default
        if current == default:
            # Raise RuntimeError
            raise RuntimeError(
                f"CRITICAL: {name} is using the default value '{default}'. "
                f"Set a strong value via the {name} environment variable "
                f"before running in production."
            )
