from functools import lru_cache
from ipaddress import ip_network
import os
from pathlib import Path
import re

from dotenv import load_dotenv
from sqlalchemy.engine import URL


def _discover_base_dir() -> Path:
    if os.getenv("LIMRISTEM_MAIL_BASE_DIR"):
        return Path(os.environ["LIMRISTEM_MAIL_BASE_DIR"])
    current_file = Path(__file__).resolve()
    candidate_bin_dir = current_file.parents[1]
    if candidate_bin_dir.name == "bin":
        installed_base = current_file.parents[2]
        if (installed_base / "limristem-mail").is_file() and (installed_base / "config").is_dir():
            return installed_base
    return current_file.parents[1]


_base_dir = _discover_base_dir()
_config_env_candidates = []
if os.getenv("LIMRISTEM_MAIL_ENV_FILE"):
    _config_env_candidates.append(Path(os.environ["LIMRISTEM_MAIL_ENV_FILE"]))
_config_env_candidates.extend((_base_dir / "config" / "limristem-mail.env", Path("/etc/limristem-mail.env")))
for _config_env_path in _config_env_candidates:
    if not _config_env_path.is_file():
        continue
    try:
        load_dotenv(_config_env_path)
        break
    except PermissionError:
        continue
load_dotenv()


def _package_version(base_dir: Path) -> str:
    version_file = base_dir / "version.json"
    try:
        import json

        payload = json.loads(version_file.read_text(encoding="utf-8"))
        if isinstance(payload, list) and payload:
            payload = payload[0]
        if isinstance(payload, dict):
            version = str(payload.get("version", "")).strip()
            if version:
                return version
    except (OSError, ValueError, TypeError):
        pass
    return "0.0.0"


class Settings:
    def __init__(self) -> None:
        self.base_dir = _discover_base_dir()
        self.hostname = os.getenv("LIMRISTEM_MAIL_HOSTNAME", os.uname().nodename).strip().rstrip(".").lower()
        self.server_name = os.getenv("LIMRISTEM_MAIL_SERVER_NAME", "Limristem eMail").strip() or "Limristem eMail"
        self.panel_title = os.getenv("LIMRISTEM_MAIL_PANEL_TITLE", f"{self.server_name} Admin Panel").strip() or f"{self.server_name} Admin Panel"
        self.panel_favicon_path = os.getenv("LIMRISTEM_MAIL_PANEL_FAVICON_PATH", "").strip()
        package_version = _package_version(self.base_dir)
        self.app_version = os.getenv("LIMRISTEM_MAIL_VERSION", package_version).strip() or package_version
        self.enable_auto_updates = os.getenv("LIMRISTEM_MAIL_ENABLE_AUTO_UPDATES", "no").lower() in {"1", "true", "yes", "on"}
        default_primary_domain = self.hostname
        self.db_host = os.getenv("LIMRISTEM_MAIL_DB_HOST", "127.0.0.1")
        self.db_port = int(os.getenv("LIMRISTEM_MAIL_DB_PORT", "3306"))
        self.db_name = os.getenv("LIMRISTEM_MAIL_DB_NAME", "limristem-mail")
        self.db_user = os.getenv("LIMRISTEM_MAIL_DB_USER", "limristem-mail")
        self.db_pass = os.getenv("LIMRISTEM_MAIL_DB_PASS", "")
        self.redis_host = os.getenv("LIMRISTEM_MAIL_REDIS_HOST", "127.0.0.1")
        self.redis_port = int(os.getenv("LIMRISTEM_MAIL_REDIS_PORT", "6379"))
        self.redis_password = os.getenv("LIMRISTEM_MAIL_REDIS_PASSWORD")
        self.cache_ttl_seconds = int(os.getenv("LIMRISTEM_MAIL_CACHE_TTL_SECONDS", "3600"))
        self.api_admin_user = os.getenv("LIMRISTEM_MAIL_API_ADMIN_USER", "admin")
        self.api_admin_pass = os.getenv("LIMRISTEM_MAIL_API_ADMIN_PASS")
        self.api_admin_pass_hash = os.getenv("LIMRISTEM_MAIL_API_ADMIN_PASS_HASH")
        self.panel_admin_user = os.getenv("LIMRISTEM_MAIL_PANEL_ADMIN_USER", self.api_admin_user)
        self.panel_admin_pass = os.getenv("LIMRISTEM_MAIL_PANEL_ADMIN_PASS")
        self.panel_admin_pass_hash = os.getenv("LIMRISTEM_MAIL_PANEL_ADMIN_PASS_HASH", self.api_admin_pass_hash)
        self.panel_login_csrf_secret = os.getenv("LIMRISTEM_MAIL_PANEL_LOGIN_CSRF_SECRET")
        self.panel_session_ttl_seconds = int(os.getenv("LIMRISTEM_MAIL_PANEL_SESSION_TTL_SECONDS", "28800"))
        self.api_bind = os.getenv("LIMRISTEM_MAIL_API_BIND", "127.0.0.1")
        self.api_port = int(os.getenv("LIMRISTEM_MAIL_API_PORT", "8080"))
        self.api_auth_fail_limit = int(os.getenv("LIMRISTEM_MAIL_API_AUTH_FAIL_LIMIT", "5"))
        self.api_auth_window_seconds = int(os.getenv("LIMRISTEM_MAIL_API_AUTH_WINDOW_SECONDS", "300"))
        self.api_auth_block_seconds = int(os.getenv("LIMRISTEM_MAIL_API_AUTH_BLOCK_SECONDS", "900"))
        trusted_proxies_raw = os.getenv("LIMRISTEM_MAIL_TRUSTED_PROXIES", "127.0.0.0/8 ::1/128").strip()
        trusted_proxy_entries = [item for item in re.split(r"[\s,]+", trusted_proxies_raw) if item]
        self.trusted_proxies = tuple(self._parse_trusted_proxy(item) for item in trusted_proxy_entries)
        self.enable_api = os.getenv("LIMRISTEM_MAIL_ENABLE_API", "yes").lower() in {"1", "true", "yes", "on"}
        self.enable_web_panel = os.getenv("LIMRISTEM_MAIL_ENABLE_WEB_PANEL", "no").lower() in {"1", "true", "yes", "on"}
        self.ssl_mode = os.getenv("LIMRISTEM_MAIL_SSL_MODE", "letsencrypt").lower()
        self.mail_home = Path(os.getenv("LIMRISTEM_MAIL_MAIL_HOME", "/var/mail/vhosts"))
        self.dkim_keys_dir = Path(os.getenv("LIMRISTEM_MAIL_DKIM_KEYS_DIR", "/var/lib/limristem-mail/dkim"))
        self.primary_domain = os.getenv("LIMRISTEM_MAIL_PRIMARY_DOMAIN", default_primary_domain).strip().rstrip(".").lower()
        self.enable_srs = os.getenv("LIMRISTEM_MAIL_ENABLE_SRS", "no").lower() in {"1", "true", "yes", "on"}
        self.srs_domain = os.getenv("LIMRISTEM_MAIL_SRS_DOMAIN", self.hostname).strip().rstrip(".").lower()
        self.enable_mta_sts = os.getenv("LIMRISTEM_MAIL_ENABLE_MTA_STS", "yes").lower() in {"1", "true", "yes", "on"}
        self.mta_sts_host = os.getenv("LIMRISTEM_MAIL_MTA_STS_HOST", f"mta-sts.{self.primary_domain}").strip().rstrip(".").lower()
        self.mta_sts_mode = os.getenv("LIMRISTEM_MAIL_MTA_STS_MODE", "enforce")
        self.mta_sts_max_age = int(os.getenv("LIMRISTEM_MAIL_MTA_STS_MAX_AGE", "604800"))
        self.mta_sts_id = os.getenv("LIMRISTEM_MAIL_MTA_STS_ID", f"{self.mta_sts_mode}-{self.mta_sts_max_age}")
        self.enable_tls_rpt = os.getenv("LIMRISTEM_MAIL_ENABLE_TLS_RPT", "yes").lower() in {"1", "true", "yes", "on"}
        self.tls_rpt_mailbox = os.getenv("LIMRISTEM_MAIL_TLS_RPT_MAILBOX", f"postmaster@{self.primary_domain}")
        self.vmail_uid = int(os.getenv("LIMRISTEM_MAIL_VMAIL_UID", "150"))
        self.vmail_gid = int(os.getenv("LIMRISTEM_MAIL_VMAIL_GID", "150"))

    @property
    def runtime_bin_dir(self) -> Path:
        bin_dir = self.base_dir / "bin"
        scripts_dir = self.base_dir / "scripts"
        if bin_dir.is_dir() or not scripts_dir.is_dir():
            return bin_dir
        return scripts_dir

    @staticmethod
    def _parse_trusted_proxy(value: str):
        try:
            return ip_network(value, strict=False)
        except ValueError as exc:
            raise ValueError(f"Invalid LIMRISTEM_MAIL_TRUSTED_PROXIES entry: {value}") from exc

    @property
    def database_url(self) -> URL:
        return URL.create(
            "mysql+pymysql",
            username=self.db_user,
            password=self.db_pass,
            host=self.db_host,
            port=self.db_port,
            database=self.db_name,
        )


@lru_cache
def get_settings() -> Settings:
    return Settings()
