"""应用配置(环境变量)。"""

from __future__ import annotations

import os
from functools import lru_cache
from pathlib import Path
from urllib.parse import urlparse

from app.core.paths import DB_FILE, PROJECT_ROOT

DEFAULT_SQLITE_URL = f"sqlite:///{DB_FILE}"


def is_production() -> bool:
    env = (os.getenv("COMPILOT_ENV") or os.getenv("ENV") or "").strip().lower()
    return env in ("production", "prod")


def is_development() -> bool:
    return not is_production()


@lru_cache
def get_database_url() -> str:
    return (os.getenv("DATABASE_URL") or os.getenv("COMPILOT_DATABASE_URL") or DEFAULT_SQLITE_URL).strip()


def database_dialect() -> str:
    url = get_database_url()
    if url.startswith("postgres://") or url.startswith("postgresql://"):
        return "postgres"
    return "sqlite"


def sqlite_path_from_url(url: str) -> Path:
    parsed = urlparse(url)
    if parsed.path:
        return Path(parsed.path)
    return DB_FILE


def get_jwt_secret() -> str:
    """JWT 签名密钥:生产环境必须通过环境变量提供。"""
    secret = (os.getenv("JWT_SECRET") or os.getenv("COMPILOT_JWT_SECRET") or "").strip()
    if secret:
        return secret
    if is_production():
        raise RuntimeError(
            "生产环境必须设置 JWT_SECRET 或 COMPILOT_JWT_SECRET 环境变量"
        )
    from app.core.security import load_dev_jwt_secret

    return load_dev_jwt_secret()


def jwt_expire_hours() -> int:
    raw = (os.getenv("JWT_EXPIRE_HOURS") or os.getenv("COMPILOT_JWT_EXPIRE_HOURS") or "").strip()
    if raw:
        try:
            return max(1, int(raw))
        except ValueError:
            pass
    return 24 * 7


def allow_default_admin_login() -> bool:
    """是否允许使用种子账号 admin/admin123 登录(生产默认关闭)。"""
    raw = (os.getenv("COMPILOT_ALLOW_DEFAULT_ADMIN") or "").strip().lower()
    if raw in ("1", "true", "yes", "on"):
        return True
    if raw in ("0", "false", "no", "off"):
        return False
    return is_development()


def rate_limit_login() -> tuple[int, int]:
    """(次数, 窗口秒) — 按客户端 IP。"""
    return _rate_pair("COMPILOT_RATE_LOGIN", default=(10, 60))


def rate_limit_chat() -> tuple[int, int]:
    return _rate_pair("COMPILOT_RATE_CHAT", default=(30, 60))


def terminal_tool_enabled() -> bool:
    raw = (os.getenv("COMPILOT_TERMINAL_ENABLED") or "").strip().lower()
    if raw in ("1", "true", "yes", "on"):
        return True
    if raw in ("0", "false", "no", "off"):
        return False
    return is_development()


def terminal_timeout_seconds() -> int:
    raw = (os.getenv("COMPILOT_TERMINAL_TIMEOUT") or "").strip()
    try:
        return min(max(5, int(raw)), 300) if raw else 120
    except ValueError:
        return 120


def terminal_max_output_chars() -> int:
    raw = (os.getenv("COMPILOT_TERMINAL_MAX_OUTPUT") or "").strip()
    try:
        return min(max(1000, int(raw)), 100_000) if raw else 40_000
    except ValueError:
        return 40_000


def bing_search_tool_enabled() -> bool:
    raw = (os.getenv("COMPILOT_BING_SEARCH_ENABLED") or "").strip().lower()
    if raw in ("1", "true", "yes", "on"):
        return True
    if raw in ("0", "false", "no", "off"):
        return False
    return is_development()


def bing_search_timeout_seconds() -> int:
    raw = (os.getenv("COMPILOT_BING_SEARCH_TIMEOUT") or "").strip()
    try:
        return min(max(5, int(raw)), 120) if raw else 30
    except ValueError:
        return 30


def bing_search_max_results() -> int:
    raw = (os.getenv("COMPILOT_BING_SEARCH_MAX_RESULTS") or "").strip()
    try:
        return min(max(1, int(raw)), 20) if raw else 8
    except ValueError:
        return 8


def bing_search_user_agent() -> str:
    custom = (os.getenv("COMPILOT_BING_SEARCH_USER_AGENT") or "").strip()
    if custom:
        return custom
    return (
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    )


def web_fetch_tool_enabled() -> bool:
    raw = (os.getenv("COMPILOT_WEB_FETCH_ENABLED") or "").strip().lower()
    if raw in ("1", "true", "yes", "on"):
        return True
    if raw in ("0", "false", "no", "off"):
        return False
    return is_development()


def web_fetch_js_enabled() -> bool:
    raw = (os.getenv("COMPILOT_WEB_FETCH_JS_ENABLED") or "").strip().lower()
    if raw in ("0", "false", "no", "off"):
        return False
    if raw in ("1", "true", "yes", "on"):
        return True
    return web_fetch_tool_enabled()


def web_fetch_timeout_seconds() -> int:
    raw = (os.getenv("COMPILOT_WEB_FETCH_TIMEOUT") or "").strip()
    try:
        return min(max(5, int(raw)), 120) if raw else 45
    except ValueError:
        return 45


def web_fetch_max_content_chars() -> int:
    raw = (os.getenv("COMPILOT_WEB_FETCH_MAX_CONTENT") or "").strip()
    try:
        return min(max(2000, int(raw)), 200_000) if raw else 50_000
    except ValueError:
        return 50_000


def web_fetch_user_agent() -> str:
    custom = (os.getenv("COMPILOT_WEB_FETCH_USER_AGENT") or "").strip()
    if custom:
        return custom
    return bing_search_user_agent()


def browser_tool_enabled() -> bool:
    raw = (os.getenv("COMPILOT_BROWSER_ENABLED") or "").strip().lower()
    if raw in ("1", "true", "yes", "on"):
        return True
    if raw in ("0", "false", "no", "off"):
        return False
    return is_development()


def browser_max_snapshot_chars() -> int:
    raw = (os.getenv("COMPILOT_BROWSER_MAX_SNAPSHOT") or "").strip()
    try:
        return min(max(2000, int(raw)), 100_000) if raw else 30_000
    except ValueError:
        return 30_000


def browser_idle_seconds() -> int:
    raw = (os.getenv("COMPILOT_BROWSER_IDLE_SECONDS") or "").strip()
    try:
        return min(max(60, int(raw)), 7200) if raw else 1800
    except ValueError:
        return 1800


def _rate_pair(env_key: str, *, default: tuple[int, int]) -> tuple[int, int]:
    raw = (os.getenv(env_key) or "").strip()
    if not raw:
        return default
    parts = raw.split(",")
    if len(parts) != 2:
        return default
    try:
        return max(1, int(parts[0])), max(1, int(parts[1]))
    except ValueError:
        return default


def load_dotenv_file() -> None:
    """加载项目根目录 .env(若存在)。"""
    env_path = PROJECT_ROOT / ".env"
    if not env_path.is_file():
        return
    for line in env_path.read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        key, _, val = line.partition("=")
        key = key.strip()
        val = val.strip().strip('"').strip("'")
        if key and key not in os.environ:
            os.environ[key] = val