import os
from dataclasses import dataclass

DEFAULT_SECURITY_PERMISSIONS_POLICY = (
    "accelerometer=(), autoplay=(), camera=(), display-capture=(), "
    "geolocation=(), gyroscope=(), microphone=(), payment=(), usb=()"
)
DEFAULT_SECURITY_CSP_POLICY = (
    "default-src 'self'; "
    "base-uri 'self'; "
    "form-action 'self'; "
    "frame-ancestors 'none'; "
    "img-src 'self' data: https:; "
    "script-src 'self'; "
    "style-src 'self' 'unsafe-inline'; "
    "font-src 'self' data:; "
    "connect-src 'self'; "
    "object-src 'none'"
)
DEFAULT_SECURITY_CSP_DOCS_POLICY = (
    "default-src 'self'; "
    "img-src 'self' data: https:; "
    "script-src 'self' 'unsafe-inline'; "
    "style-src 'self' 'unsafe-inline'; "
    "font-src 'self' data:; "
    "connect-src 'self' https:; "
    "object-src 'none'; "
    "frame-ancestors 'none'"
)


def _env_bool(name: str, default: bool) -> bool:
    value = os.getenv(name)
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on"}


def _env_int(name: str, default: int) -> int:
    value = os.getenv(name)
    if value is None:
        return default
    return int(value)


def _env_float(name: str, default: float) -> float:
    value = os.getenv(name)
    if value is None:
        return default
    return float(value)


def _env_str(name: str, default: str) -> str:
    value = os.getenv(name)
    if value is None:
        return default
    return value.strip() or default


@dataclass(frozen=True)
class Settings:
    app_name: str = os.getenv("APP_NAME", "scholarr")
    database_url: str = os.getenv(
        "DATABASE_URL",
        "postgresql+asyncpg://scholar:scholar@db:5432/scholar",
    )
    database_pool_mode: str = _env_str("DATABASE_POOL_MODE", "auto")
    database_pool_size: int = _env_int("DATABASE_POOL_SIZE", 5)
    database_pool_max_overflow: int = _env_int("DATABASE_POOL_MAX_OVERFLOW", 10)
    database_pool_timeout_seconds: int = _env_int("DATABASE_POOL_TIMEOUT_SECONDS", 30)
    session_secret_key: str = os.getenv("SESSION_SECRET_KEY", "dev-insecure-session-key")
    session_cookie_secure: bool = _env_bool("SESSION_COOKIE_SECURE", False)
    security_headers_enabled: bool = _env_bool("SECURITY_HEADERS_ENABLED", True)
    security_x_content_type_options: str = _env_str("SECURITY_X_CONTENT_TYPE_OPTIONS", "nosniff")
    security_x_frame_options: str = _env_str("SECURITY_X_FRAME_OPTIONS", "DENY")
    security_referrer_policy: str = _env_str("SECURITY_REFERRER_POLICY", "strict-origin-when-cross-origin")
    security_permissions_policy: str = _env_str(
        "SECURITY_PERMISSIONS_POLICY",
        DEFAULT_SECURITY_PERMISSIONS_POLICY,
    )
    security_cross_origin_opener_policy: str = _env_str(
        "SECURITY_CROSS_ORIGIN_OPENER_POLICY",
        "same-origin",
    )
    security_cross_origin_resource_policy: str = _env_str(
        "SECURITY_CROSS_ORIGIN_RESOURCE_POLICY",
        "same-origin",
    )
    security_csp_enabled: bool = _env_bool("SECURITY_CSP_ENABLED", True)
    security_csp_policy: str = _env_str("SECURITY_CSP_POLICY", DEFAULT_SECURITY_CSP_POLICY)
    security_csp_docs_policy: str = _env_str("SECURITY_CSP_DOCS_POLICY", DEFAULT_SECURITY_CSP_DOCS_POLICY)
    security_csp_report_only: bool = _env_bool("SECURITY_CSP_REPORT_ONLY", False)
    security_strict_transport_security_enabled: bool = _env_bool(
        "SECURITY_STRICT_TRANSPORT_SECURITY_ENABLED",
        False,
    )
    security_strict_transport_security_max_age: int = _env_int(
        "SECURITY_STRICT_TRANSPORT_SECURITY_MAX_AGE",
        31_536_000,
    )
    security_strict_transport_security_include_subdomains: bool = _env_bool(
        "SECURITY_STRICT_TRANSPORT_SECURITY_INCLUDE_SUBDOMAINS",
        True,
    )
    security_strict_transport_security_preload: bool = _env_bool(
        "SECURITY_STRICT_TRANSPORT_SECURITY_PRELOAD",
        False,
    )
    login_rate_limit_attempts: int = _env_int("LOGIN_RATE_LIMIT_ATTEMPTS", 5)
    login_rate_limit_window_seconds: int = _env_int(
        "LOGIN_RATE_LIMIT_WINDOW_SECONDS",
        60,
    )
    log_level: str = _env_str("LOG_LEVEL", "INFO")
    log_format: str = _env_str("LOG_FORMAT", "console")
    log_requests: bool = _env_bool("LOG_REQUESTS", True)
    log_uvicorn_access: bool = _env_bool("LOG_UVICORN_ACCESS", False)
    log_request_skip_paths: str = _env_str("LOG_REQUEST_SKIP_PATHS", "/healthz")
    log_redact_fields: str = os.getenv("LOG_REDACT_FIELDS", "")
    scheduler_enabled: bool = _env_bool("SCHEDULER_ENABLED", True)
    scheduler_tick_seconds: int = _env_int("SCHEDULER_TICK_SECONDS", 60)
    ingestion_automation_allowed: bool = _env_bool(
        "INGESTION_AUTOMATION_ALLOWED",
        True,
    )
    ingestion_manual_run_allowed: bool = _env_bool(
        "INGESTION_MANUAL_RUN_ALLOWED",
        True,
    )
    ingestion_min_run_interval_minutes: int = _env_int(
        "INGESTION_MIN_RUN_INTERVAL_MINUTES",
        15,
    )
    ingestion_min_request_delay_seconds: int = _env_int(
        "INGESTION_MIN_REQUEST_DELAY_SECONDS",
        2,
    )
    ingestion_network_error_retries: int = _env_int("INGESTION_NETWORK_ERROR_RETRIES", 1)
    ingestion_retry_backoff_seconds: float = _env_float(
        "INGESTION_RETRY_BACKOFF_SECONDS",
        1.0,
    )
    ingestion_rate_limit_retries: int = _env_int("INGESTION_RATE_LIMIT_RETRIES", 3)
    ingestion_rate_limit_backoff_seconds: float = _env_float(
        "INGESTION_RATE_LIMIT_BACKOFF_SECONDS",
        30.0,
    )
    ingestion_max_pages_per_scholar: int = _env_int(
        "INGESTION_MAX_PAGES_PER_SCHOLAR",
        30,
    )
    ingestion_page_size: int = _env_int("INGESTION_PAGE_SIZE", 100)
    ingestion_alert_blocked_failure_threshold: int = _env_int(
        "INGESTION_ALERT_BLOCKED_FAILURE_THRESHOLD",
        1,
    )
    ingestion_alert_network_failure_threshold: int = _env_int(
        "INGESTION_ALERT_NETWORK_FAILURE_THRESHOLD",
        2,
    )
    ingestion_alert_retry_scheduled_threshold: int = _env_int(
        "INGESTION_ALERT_RETRY_SCHEDULED_THRESHOLD",
        3,
    )
    ingestion_safety_cooldown_blocked_seconds: int = _env_int(
        "INGESTION_SAFETY_COOLDOWN_BLOCKED_SECONDS",
        1800,
    )
    ingestion_safety_cooldown_network_seconds: int = _env_int(
        "INGESTION_SAFETY_COOLDOWN_NETWORK_SECONDS",
        900,
    )
    ingestion_continuation_queue_enabled: bool = _env_bool(
        "INGESTION_CONTINUATION_QUEUE_ENABLED",
        True,
    )
    ingestion_continuation_base_delay_seconds: int = _env_int(
        "INGESTION_CONTINUATION_BASE_DELAY_SECONDS",
        120,
    )
    ingestion_continuation_max_delay_seconds: int = _env_int(
        "INGESTION_CONTINUATION_MAX_DELAY_SECONDS",
        3600,
    )
    ingestion_continuation_max_attempts: int = _env_int(
        "INGESTION_CONTINUATION_MAX_ATTEMPTS",
        6,
    )
    scheduler_queue_batch_size: int = _env_int("SCHEDULER_QUEUE_BATCH_SIZE", 10)
    scheduler_pdf_queue_batch_size: int = _env_int("SCHEDULER_PDF_QUEUE_BATCH_SIZE", 15)
    frontend_enabled: bool = _env_bool("FRONTEND_ENABLED", True)
    frontend_dist_dir: str = _env_str("FRONTEND_DIST_DIR", "/app/frontend/dist")
    scholar_image_upload_dir: str = _env_str(
        "SCHOLAR_IMAGE_UPLOAD_DIR",
        "/tmp/scholarr_uploads/scholar_images",
    )
    scholar_image_upload_max_bytes: int = _env_int(
        "SCHOLAR_IMAGE_UPLOAD_MAX_BYTES",
        2_000_000,
    )
    scholar_name_search_enabled: bool = _env_bool("SCHOLAR_NAME_SEARCH_ENABLED", True)
    scholar_name_search_cache_ttl_seconds: int = _env_int(
        "SCHOLAR_NAME_SEARCH_CACHE_TTL_SECONDS",
        21_600,
    )
    scholar_name_search_blocked_cache_ttl_seconds: int = _env_int(
        "SCHOLAR_NAME_SEARCH_BLOCKED_CACHE_TTL_SECONDS",
        300,
    )
    scholar_name_search_cache_max_entries: int = _env_int(
        "SCHOLAR_NAME_SEARCH_CACHE_MAX_ENTRIES",
        512,
    )
    scholar_name_search_min_interval_seconds: float = _env_float(
        "SCHOLAR_NAME_SEARCH_MIN_INTERVAL_SECONDS",
        8.0,
    )
    scholar_name_search_interval_jitter_seconds: float = _env_float(
        "SCHOLAR_NAME_SEARCH_INTERVAL_JITTER_SECONDS",
        2.0,
    )
    scholar_name_search_cooldown_block_threshold: int = _env_int(
        "SCHOLAR_NAME_SEARCH_COOLDOWN_BLOCK_THRESHOLD",
        1,
    )
    scholar_name_search_cooldown_seconds: int = _env_int(
        "SCHOLAR_NAME_SEARCH_COOLDOWN_SECONDS",
        1800,
    )
    scholar_name_search_alert_retry_count_threshold: int = _env_int(
        "SCHOLAR_NAME_SEARCH_ALERT_RETRY_COUNT_THRESHOLD",
        2,
    )
    scholar_name_search_alert_cooldown_rejections_threshold: int = _env_int(
        "SCHOLAR_NAME_SEARCH_ALERT_COOLDOWN_REJECTIONS_THRESHOLD",
        3,
    )
    scholar_http_user_agent: str = _env_str("SCHOLAR_HTTP_USER_AGENT", "")
    scholar_http_rotate_user_agent: bool = _env_bool("SCHOLAR_HTTP_ROTATE_USER_AGENT", False)
    scholar_http_accept_language: str = _env_str(
        "SCHOLAR_HTTP_ACCEPT_LANGUAGE",
        "en-US,en;q=0.9",
    )
    scholar_http_cookie: str = _env_str("SCHOLAR_HTTP_COOKIE", "")
    unpaywall_enabled: bool = _env_bool("UNPAYWALL_ENABLED", True)
    unpaywall_email: str = _env_str("UNPAYWALL_EMAIL", "")
    unpaywall_timeout_seconds: float = _env_float("UNPAYWALL_TIMEOUT_SECONDS", 4.0)
    unpaywall_min_interval_seconds: float = _env_float("UNPAYWALL_MIN_INTERVAL_SECONDS", 0.6)
    unpaywall_max_items_per_request: int = _env_int("UNPAYWALL_MAX_ITEMS_PER_REQUEST", 20)
    unpaywall_retry_cooldown_seconds: int = _env_int("UNPAYWALL_RETRY_COOLDOWN_SECONDS", 1800)
    pdf_auto_retry_interval_seconds: int = _env_int(
        "PDF_AUTO_RETRY_INTERVAL_SECONDS",
        86_400,
    )
    pdf_auto_retry_first_interval_seconds: int = _env_int(
        "PDF_AUTO_RETRY_FIRST_INTERVAL_SECONDS",
        86_400,
    )
    pdf_auto_retry_max_attempts: int = _env_int("PDF_AUTO_RETRY_MAX_ATTEMPTS", 2)
    unpaywall_pdf_discovery_enabled: bool = _env_bool("UNPAYWALL_PDF_DISCOVERY_ENABLED", True)
    unpaywall_pdf_discovery_max_candidates: int = _env_int("UNPAYWALL_PDF_DISCOVERY_MAX_CANDIDATES", 5)
    unpaywall_pdf_discovery_max_html_bytes: int = _env_int("UNPAYWALL_PDF_DISCOVERY_MAX_HTML_BYTES", 500_000)
    arxiv_enabled: bool = _env_bool("ARXIV_ENABLED", True)
    arxiv_timeout_seconds: float = _env_float("ARXIV_TIMEOUT_SECONDS", 3.0)
    arxiv_min_interval_seconds: float = _env_float("ARXIV_MIN_INTERVAL_SECONDS", 4.0)
    arxiv_rate_limit_cooldown_seconds: float = _env_float("ARXIV_RATE_LIMIT_COOLDOWN_SECONDS", 60.0)
    arxiv_default_max_results: int = _env_int("ARXIV_DEFAULT_MAX_RESULTS", 3)
    arxiv_cache_ttl_seconds: float = _env_float("ARXIV_CACHE_TTL_SECONDS", 900.0)
    arxiv_cache_max_entries: int = _env_int("ARXIV_CACHE_MAX_ENTRIES", 512)
    arxiv_mailto: str = _env_str("ARXIV_MAILTO", "")
    crossref_enabled: bool = _env_bool("CROSSREF_ENABLED", True)
    crossref_max_rows: int = _env_int("CROSSREF_MAX_ROWS", 10)
    crossref_timeout_seconds: float = _env_float("CROSSREF_TIMEOUT_SECONDS", 8.0)
    crossref_min_interval_seconds: float = _env_float("CROSSREF_MIN_INTERVAL_SECONDS", 0.6)
    crossref_max_lookups_per_request: int = _env_int("CROSSREF_MAX_LOOKUPS_PER_REQUEST", 8)

    openalex_api_key: str | None = os.getenv("OPENALEX_API_KEY")
    crossref_api_token: str | None = os.getenv("CROSSREF_API_TOKEN")
    crossref_api_mailto: str | None = os.getenv("CROSSREF_API_MAILTO")


settings = Settings()
