from __future__ import annotations

from pathlib import Path
import os
import re


BASE_DIR = Path(__file__).resolve().parent
TEMP_DIR = BASE_DIR / "tempUploads"
LOG_DIR = BASE_DIR / "logs"
CONFIG_FILE = BASE_DIR / "config.properties"

# Odwołanie do zmiennej środowiskowej w wartości: ${NAZWA}
ENV_REF = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")


class AppException(Exception):
    pass


class SimpleConfig:
    _values: dict[str, str] | None = None

    @classmethod
    def load(cls) -> dict[str, str]:
        if cls._values is None:
            if not CONFIG_FILE.exists():
                raise AppException(f"Brak pliku konfiguracyjnego: {CONFIG_FILE.resolve()}")

            values: dict[str, str] = {}
            for raw_line in CONFIG_FILE.read_text(encoding="utf-8").splitlines():
                line = raw_line.strip()

                if not line or line.startswith("#"):
                    continue

                if "=" not in line:
                    continue

                key, value = line.split("=", 1)
                values[key.strip()] = cls._expand_env(value.strip())

            cls._values = values

        return cls._values

    @staticmethod
    def _expand_env(value: str) -> str:
        def replace(match: re.Match[str]) -> str:
            name = match.group(1)
            resolved = os.environ.get(name)
            if not resolved:
                raise AppException(f"Brak zmiennej środowiskowej: {name}")
            return resolved

        return ENV_REF.sub(replace, value)

    @classmethod
    def get_string(cls, key: str, default: str | None = None) -> str:
        value = cls.load().get(key, default)
        if value is None:
            raise AppException(f"Brak wymaganego wpisu w konfiguracji: {key}")
        return value

    @classmethod
    def get_int(cls, key: str, default: int | None = None) -> int:
        value = cls.load().get(key)
        if value is None:
            if default is None:
                raise AppException(f"Brak wymaganego wpisu w konfiguracji: {key}")
            return default

        try:
            return int(value.strip())
        except ValueError as e:
            raise AppException(f"Wpis {key} nie jest liczbą całkowitą: {value}") from e

    @classmethod
    def get_bool(cls, key: str, default: bool = False) -> bool:
        value = cls.load().get(key)
        if value is None:
            return default

        normalized = value.strip().lower()
        return normalized in {"1", "true", "yes", "y", "on"}