"""Workflow for regenerating the server-facing UMC mapcycle from the master UMC file."""

import json
from typing import Any

from core import constants
from core.io.umc_parser import parse_umc_file
from core.io.umc_writer import write
from core.logger_config import logger

# ==========================================================================
# 1. CHARGEMENT DE LA CONFIGURATION DES MODS
# ==========================================================================


def _load_active_mods_config() -> dict[str, Any]:
    """Loads target categories, active/disabled mods, and active commands."""
    if not constants.CONFIG_FILE.exists():
        logger.warning(f"Fichier de configuration introuvable : {constants.CONFIG_FILE}")
        return {
            "active_categories": set(),
            "all_mods_categories": set(),
            "disabled_mods": set(),
            "active_mod_commands": {},
        }

    with constants.CONFIG_FILE.open(encoding="utf-8") as f:
        config_data = json.load(f)

    load_order = config_data.get("load_order", [])
    mod_commands_pool = config_data.get("mod_commands", {})
    active_ids = {str(mod_id).strip().lower() for mod_id in load_order}

    active_categories = set()
    all_mods_categories = set()
    all_mods = set()

    if constants.REPOSITORY.exists():
        for mod_folder in constants.REPOSITORY.iterdir():
            if not mod_folder.is_dir():
                continue

            manifest_path = mod_folder / "manifest.json"
            if not manifest_path.exists():
                continue

            mod_name_lower = mod_folder.name.strip().lower()
            all_mods.add(mod_name_lower)

            try:
                with manifest_path.open(encoding="utf-8") as f:
                    manifest = json.load(f)

                target_cat = manifest.get("target_category")
                if target_cat:
                    target_cat_clean = target_cat.strip().lower()
                    all_mods_categories.add(target_cat_clean)

                    if mod_name_lower in active_ids:
                        active_categories.add(target_cat_clean)
            except (OSError, json.JSONDecodeError) as e:
                logger.error(f"Error reading manifest for {mod_folder.name}: {e}")
                continue

    disabled_mods = all_mods - active_ids

    active_mod_commands: dict[str, list[str]] = {}
    for mod_key, commands in mod_commands_pool.items():
        mod_key_clean = mod_key.strip().lower()
        if mod_key_clean in active_ids:
            active_mod_commands[mod_key_clean] = (
                commands if isinstance(commands, list) else [commands]
            )

    return {
        "active_categories": active_categories,
        "all_mods_categories": all_mods_categories,
        "disabled_mods": disabled_mods,
        "active_mod_commands": active_mod_commands,
    }


# ==========================================================================
# 2. SOUS-FONCTIONS UTILITAIRES DE COMPILATION (RESPONSABILITÉS ISOLÉES)
# ==========================================================================


def _should_skip_category(
    cat_name_lower: str, active_categories: set[str], all_mods_categories: set[str]
) -> bool:
    """Détermine si la catégorie doit être ignorée (liée à un mod inactif et non partagée)."""
    if cat_name_lower in constants.PERMANENT_CATEGORIES:
        return False

    is_linked_to_any_mod = cat_name_lower in all_mods_categories
    is_active = cat_name_lower in active_categories

    return is_linked_to_any_mod and not is_active


def _should_skip_map(target_mods_str: str, disabled_mods: set[str]) -> bool:
    """Détermine si une map doit être retirée car elle cible un mod explicitement désactivé."""
    if not target_mods_str:
        return False

    target_mods = [m.strip().lower() for m in target_mods_str.split(",") if m.strip()]
    return any(mod in disabled_mods for mod in target_mods)


def _compile_map_commands(
    target_mods_str: str, manual_command_str: str, active_mod_commands: dict[str, list[str]]
) -> list[str]:
    """Fusionne dynamiquement les commandes liées aux target_mods et la commande manuelle, sans doublons."""
    map_commands_list = []

    if target_mods_str:
        target_mods = [m.strip().lower() for m in target_mods_str.split(",") if m.strip()]
        for mod_key in target_mods:
            if mod_key in active_mod_commands:
                for cmd in active_mod_commands[mod_key]:
                    if cmd not in map_commands_list:
                        map_commands_list.append(cmd)

    if manual_command_str:
        sub_commands = [c.strip() for c in manual_command_str.split(";") if c.strip()]
        for sub_cmd in sub_commands:
            if sub_cmd not in map_commands_list:
                map_commands_list.append(sub_cmd)

    return map_commands_list


# ==========================================================================
# 3. FONCTIONS PRINCIPALES DE COMPILATION DU MAPCYCLE
# ==========================================================================


def _compile_umc_data(
    master_data: dict[str, Any],
    active_categories: set[str],
    all_mods_categories: set[str],
    disabled_mods: set[str],
    active_mod_commands: dict[str, list[str]],
) -> dict[str, Any]:
    """A pure function that filters categories and maps based on mod states."""
    compiled_data: dict[str, Any] = {}

    for cat_name, cat_content in master_data.items():
        cat_name_clean = cat_name.strip()
        cat_name_lower = cat_name_clean.lower()

        if _should_skip_category(cat_name_lower, active_categories, all_mods_categories):
            continue

        if cat_name_lower not in constants.PERMANENT_CATEGORIES and not cat_content.get("maps"):
            continue

        compiled_data[cat_name] = {"options": dict(cat_content.get("options", {})), "maps": {}}

        master_command = compiled_data[cat_name]["options"].get("command", "").strip()
        if master_command:
            compiled_data[cat_name]["options"]["command"] = master_command
        elif cat_name_lower in active_mod_commands:
            compiled_data[cat_name_clean]["options"]["command"] = "; ".join(
                active_mod_commands[cat_name_lower]
            )
        elif "command" in compiled_data[cat_name]["options"]:
            del compiled_data[cat_name]["options"]["command"]

        # --- GESTION DES MAPS ---
        for map_name, map_content in cat_content.get("maps", {}).items():
            map_name_clean = map_name.strip()
            compiled_map_options = dict(map_content)
            target_mods_str = compiled_map_options.get("target_mods", "").strip()

            if _should_skip_map(target_mods_str, disabled_mods):
                continue

            manual_map_command = compiled_map_options.get("command", "").strip()
            compiled_map_options.pop("target_mods", None)

            map_commands_list = _compile_map_commands(
                target_mods_str, manual_map_command, active_mod_commands
            )

            if map_commands_list:
                compiled_map_options["command"] = "; ".join(map_commands_list)
            elif "command" in compiled_map_options:
                del compiled_map_options["command"]

            compiled_data[cat_name_clean]["maps"][map_name_clean] = compiled_map_options

    return compiled_data


def regenerate_umc_mapcycle() -> None:
    """Coordinates the reading of the Master, filtering by mod, and writing to the server."""
    if not constants.UMC_MASTER_FILE.exists():
        logger.warning("Unable to generate the mapcycle: the MASTER file does not exist.")
        return

    logger.info("Start of dynamic compilation of umc_mapcycle")

    mod_config = _load_active_mods_config()
    master_data = parse_umc_file(constants.UMC_MASTER_FILE)

    server_data = _compile_umc_data(
        master_data=master_data,
        active_categories=mod_config["active_categories"],
        all_mods_categories=mod_config["all_mods_categories"],
        disabled_mods=mod_config["disabled_mods"],
        active_mod_commands=mod_config["active_mod_commands"],
    )

    write(destination=constants.UMC_FILE, umc_data=server_data, create_backup=False)
    logger.info("The server file umc_mapcycle.txt has been successfully updated.")


def get_mod_target_category(mod_id: str) -> dict[str, str | bool] | None:
    """Returns the target category of a mod and whether it is shared by other mods."""
    repository = constants.REPOSITORY
    mod_folder = repository / mod_id
    manifest_path = mod_folder / "manifest.json"
    if not manifest_path.exists():
        return None

    with manifest_path.open(encoding="utf-8") as f:
        manifest = json.load(f)

    target_category = manifest.get("target_category")
    if not target_category:
        return None

    shared = False
    target_lower = target_category.strip().lower()

    if repository.exists():
        for folder in repository.iterdir():
            if not folder.is_dir() or folder.name == mod_id:
                continue
            other_manifest = folder / "manifest.json"
            if not other_manifest.exists():
                continue
            try:
                with other_manifest.open(encoding="utf-8") as f:
                    other = json.load(f)
            except (OSError, json.JSONDecodeError) as e:
                logger.warning(f"Skipping unreadable manifest '{other_manifest}': {e}")
                continue
            if other.get("target_category", "").strip().lower() == target_lower:
                shared = True
                break

    return {"category": target_category, "shared": shared}
