from pathlib import Path
from typing import Any

from filelock import FileLock

from core import constants
from core.exceptions import DeleteServiceError
from core.io.filedelete import delete
from core.io.json_store import toggle_mod_load_order
from core.io.umc_parser import parse_umc_file
from core.io.umc_writer import write
from core.logger_config import logger
from core.services.mapcycle_service import get_mod_target_category, regenerate_umc_mapcycle

# ==========================================================================
# 0. VALIDATION
# ==========================================================================


def validate_mod_deletion(mod_id: str) -> None:
    """Validates that a mod can be deleted."""
    if not mod_id or not mod_id.strip():
        msg = "No mod id provided."
        raise DeleteServiceError(msg)

    mod_folder = constants.REPOSITORY / mod_id

    if not mod_folder.exists():
        msg = f"Unknown mod: {mod_id}"
        raise DeleteServiceError(msg)

    if not mod_folder.is_dir():
        msg = f"{mod_id} is not a valid mod folder."
        raise DeleteServiceError(msg)

    manifest = mod_folder / "manifest.json"

    if not manifest.exists():
        msg = f"Manifest missing for mod '{mod_id}'."
        raise DeleteServiceError(msg)


# ==========================================================================
# 1. SUPPRESSION DES MAPS LIÉES AU MOD
# ==========================================================================


def _remove_maps_from_master(umc_data: dict[str, Any], mod_id: str) -> None:
    """Removes every map linked to the specified mod."""
    for category_name, category in umc_data.items():
        maps = category.get("maps", {})
        linked_maps = [name for name, opts in maps.items() if opts.get("target_mods") == mod_id]
        for map_name in linked_maps:
            logger.info(f"Removing map '{map_name}' from category '{category_name}'.")
            del maps[map_name]


# ==========================================================================
# 2. SUPPRESSION DE LA CATÉGORIE SI ELLE N'EST PAS PARTAGÉE
# ==========================================================================


def _remove_category_if_unused(umc_data: dict[str, Any], mod_id: str) -> None:
    """Deletes the target category if it belongs only to this mod."""
    info = get_mod_target_category(mod_id)
    if not info:
        return
    if info["shared"]:
        logger.info(f"Category '{info['category']}' is shared with another mod.")
        return
    category_name = info["category"]
    if category_name.strip().lower() in constants.PERMANENT_CATEGORIES:
        logger.info(f"Category '{category_name}' is permanent, keeping it.")
        return
    category_name = info["category"]
    if category_name in umc_data:
        logger.info(f"Deleting category '{category_name}'.")
        del umc_data[category_name]


# ==========================================================================
# 3. SUPPRESSION COMPLÈTE DU MOD
# ==========================================================================


def delete_mod(mod_id: str, destination: Path | None = None) -> dict[str, Any]:
    """Deletes a mod and cleans every associated resource."""
    validate_mod_deletion(mod_id)
    destination = destination or constants.UMC_MASTER_FILE
    lock = destination.with_suffix(".lock")
    logger.info(f"Deleting mod '{mod_id}'.")
    with FileLock(lock, timeout=10):
        # ------------------------------------------------------------------
        # Disable the mod
        # ------------------------------------------------------------------
        toggle_mod_load_order(mod_id)
        # ------------------------------------------------------------------
        # Load UMC
        # ------------------------------------------------------------------
        umc_data = parse_umc_file(destination)
        # ------------------------------------------------------------------
        # Remove maps linked to this mod
        # ------------------------------------------------------------------
        _remove_maps_from_master(umc_data, mod_id)
        # ------------------------------------------------------------------
        # Remove category if no other mod uses it
        # ------------------------------------------------------------------
        _remove_category_if_unused(umc_data, mod_id)
        # ------------------------------------------------------------------
        # Save UMC
        # ------------------------------------------------------------------
        write(destination=destination, umc_data=umc_data)
        regenerate_umc_mapcycle()
    # ----------------------------------------------------------------------
    # Delete repository folder
    # ----------------------------------------------------------------------
    delete(constants.REPOSITORY / mod_id)
    logger.info(f"Mod '{mod_id}' deleted successfully.")
    return {"status": "success"}


# ==========================================================================
# 4. SUPPRESSION D'UNE MAP PHYSIQUE
# ==========================================================================


def delete_map_files(map_name: str) -> dict[str, Any]:
    """Deletes a map from disk if it is no longer referenced by the UMC."""
    if not map_name or not map_name.strip():
        msg = "No map name provided."
        raise DeleteServiceError(msg)

    map_name = map_name.strip()

    umc_data = parse_umc_file(constants.UMC_MASTER_FILE)

    for category in umc_data.values():
        if map_name in category.get("maps", {}):
            msg = f"Map '{map_name}' is still referenced in the UMC."
            raise DeleteServiceError(msg)

    bsp = constants.MAPS_FOLDER / f"{map_name}.bsp"
    bsp_bz2 = constants.MAPS_FOLDER / f"{map_name}.bsp.bz2"

    deleted = []

    if bsp.exists():
        delete(bsp)
        deleted.append(bsp.name)

    if bsp_bz2.exists():
        delete(bsp_bz2)
        deleted.append(bsp_bz2.name)

    if not deleted:
        msg = f"No map files found for '{map_name}'."
        raise DeleteServiceError(msg)

    logger.info(f"Deleted map files for '{map_name}'.")

    return {"status": "success", "deleted_files": deleted}
