"""Functions for cache management."""

import json
from pathlib import Path

from . import cache_dir_location, config_dir_location, console, console_info


def clear_cache():
    """Delete all cache files."""

    for file in cache_dir_location.iterdir():
        # extra safety: only delete files ending with .cache  (old)
        #               and files starting with cache_ (new)
        if file.name.endswith(".cache") or file.name.startswith("cache_"):
            console_info.print(f"[bright_black]Deleting cache file: {file}")
            file.unlink()
    console.print("[green]:heavy_check_mark: Cache cleared successfully ")


def _cached_subcommand_manifest_filename(name: str) -> Path:
    return cache_dir_location / f"cache_subcommand_manifest_{name}.json"


def _cached_subcommand_info_filename(name: str) -> Path:
    return cache_dir_location / f"cache_subcommand_info_{name}.json"


def get_cached_subcommand_manifest(executable: Path, name: str) -> dict | None:
    """Return the cached manifest for the given subcommand, or None if not cached."""

    f = _cached_subcommand_manifest_filename(name)
    if f.is_file():
        current_exec_modified_time = int(executable.stat().st_mtime)
        with f.open() as cached_manifest_file:
            cached_manifest = json.load(cached_manifest_file)
            cached_exec_modified_time = cached_manifest["modified_time"]
            del cached_manifest["modified_time"]
        if cached_exec_modified_time == current_exec_modified_time:
            return cached_manifest
        # manifest is outdated, delete it
        f.unlink()
    return None


def save_cached_subcommand_manifest(executable: Path, name: str, manifest: dict):
    """Save the manifest for the given subcommand to the cache."""

    if not config_dir_location.is_dir():
        config_dir_location.mkdir()
    if not cache_dir_location.is_dir():
        cache_dir_location.mkdir()
    f = _cached_subcommand_manifest_filename(name)
    to_save = {**manifest, "modified_time": int(executable.stat().st_mtime)}
    with f.open("w") as cached_manifest_file:
        json.dump(to_save, cached_manifest_file)


def get_cached_subcommand_info(executable: Path, name: str, key: str, *, invalidate_on_exe_update: bool) -> str | None:
    """Return cached extra info for the given subcommand, or None if not cached."""

    f = _cached_subcommand_info_filename(name)
    if f.is_file():
        current_exec_modified_time = int(executable.stat().st_mtime)
        with f.open() as cached_info_file:
            cached_info = json.load(cached_info_file)
            if key not in cached_info:
                return None
            cached_entry = cached_info[key]
            if "modified_time" not in cached_entry or "value" not in cached_entry:
                return None
            cached_exec_modified_time = cached_entry["modified_time"]
        if cached_exec_modified_time == current_exec_modified_time or not invalidate_on_exe_update:
            return cached_entry["value"]
        # key is outdated. We ignore it.
    return None


def save_cached_subcommand_info(executable: Path, name: str, key: str, value: str | None):
    """Save an extra info entry for the given subcommand to the cache."""

    if not config_dir_location.is_dir():
        config_dir_location.mkdir()
    if not cache_dir_location.is_dir():
        cache_dir_location.mkdir()
    f = _cached_subcommand_info_filename(name)

    if f.is_file():
        with f.open() as cached_info_file:
            old_cached_info = json.load(cached_info_file)
    else:
        old_cached_info = {}

    to_save = {**old_cached_info, key: {"modified_time": int(executable.stat().st_mtime), "value": value}}
    with f.open("w") as cached_info_file:
        json.dump(to_save, cached_info_file)
