"""Validation rules for UMC category/map names and their configuration options.

Every public function here is a pure predicate or raises `ValidationError` --
no disk I/O, no mutation of UMC data. This is the single source of truth for
"is this input allowed", used both by `domain.umc_operations` (in-memory
edits) and `io.map_installer` (physical file checks).
"""

import re
from pathlib import Path

from core import constants
from core.exceptions import ValidationError


def validate_map_name(name: str) -> bool:
    if not isinstance(name, str):
        return False
    name = name.strip()
    if not name:
        return False
    return bool(re.fullmatch(r"^[A-Za-z0-9_\-$&]+$", name))


def validate_category_name(name: str) -> bool:
    if not isinstance(name, str):
        return False
    return bool(name.strip())


def validate_bsp_file(file_path: str | Path) -> bool:
    file_path = Path(file_path)
    return (
        file_path.exists()
        and file_path.is_file()
        and file_path.suffix.lower() == ".bsp"
        and file_path.stat().st_size > 0
    )


def _validate_type(value, expected_type: str) -> bool:
    if expected_type == "string":
        return isinstance(value, str)
    if expected_type == "integer":
        try:
            int(value)
        except (ValueError, TypeError):
            return False
        else:
            return True
    return False


def validate_category_option(key: str, value) -> bool:
    if key not in constants.CATEGORY_OPTIONS:
        return False
    return _validate_type(value, constants.CATEGORY_OPTIONS[key])


def validate_map_option(key: str, value) -> bool:
    if key not in constants.MAP_OPTIONS:
        return False
    return _validate_type(value, constants.MAP_OPTIONS[key])


def validate_category_options(options: dict) -> bool:
    for key, value in options.items():
        if not validate_category_option(key, value):
            msg = f"Invalid category option : {key}={value}"
            raise ValidationError(msg)

    min_p = options.get("default_min_players")
    max_p = options.get("default_max_players")
    if min_p is not None and max_p is not None and int(min_p) > int(max_p):
        msg = "default_min_players cannot be greater than default_max_players"
        raise ValidationError(msg)
    return True


def validate_map_options(options: dict) -> bool:
    for key, value in options.items():
        if not validate_map_option(key, value):
            msg = f"Invalid map option : {key}={value}"
            raise ValidationError(msg)

    min_p = options.get("min_players")
    max_p = options.get("max_players")
    if min_p is not None and max_p is not None and int(min_p) > int(max_p):
        msg_0 = "min_players ne peut pas être supérieur à max_players"
        raise ValidationError(msg_0)
    return True


def validate_map(map_name: str, options: dict | None = None) -> bool:
    if not validate_map_name(map_name):
        msg = f"Invalid map name : {map_name}"
        raise ValidationError(msg)
    if options:
        validate_map_options(options)
    return True


def validate_category(category_name: str, options: dict | None = None) -> bool:
    if not validate_category_name(category_name):
        msg = f"Nom de catégorie invalide : {category_name}"
        raise ValidationError(msg)
    if options:
        validate_category_options(options)
    return True
