from typing import Any
from rich.console import Console
from types import ModuleType
from fnmatch import fnmatch
from pathlib import Path
import hashlib

CONFIG_NAME = "trifold.toml"
ALWAYS_IGNORE = [".git/*", CONFIG_NAME]
console = Console()


def p_success(text: str) -> None:
    console.print(f"[green]{text}[/green]")


def p_warning(text: str) -> None:
    console.print(f"[yellow]{text}[/yellow]")


def p_error(text: str) -> None:
    console.print(f"[red]{text}[/red]")


def debug(text: str | dict) -> None:
    console.print(f"[purple]{text}[/purple]")


def file_hash(path: str) -> str:
    """sha256 hash of file"""
    return hashlib.sha256(open(path, "rb").read()).hexdigest().upper()


def is_ignored(path: str, ignorelist: list[str]) -> bool:
    for pattern in ignorelist:
        if fnmatch(path, pattern):
            return True
    return False


async def diff_storage_zone(
    config: dict[str, Any], provider: ModuleType
) -> list[tuple[str, str]]:
    """
    Key utility function that compares local filesystem to remote and returns
    a set of differences that the publish tool can act upon.
    """
    remote = {
        # remove remote_path for comparison
        f.replace(config["remote_path"] + "/", "", 1)
        if config["remote_path"]
        else f: checksum
        for f, checksum in await provider.get_full_storage_zone(
            config, config["remote_path"]
        )
    }
    remote_files = set(remote.keys())
    local_dir = Path.cwd() / config["local_path"]
    local_files = {
        # need posix path even for Windows to match URLs
        f.relative_to(local_dir).as_posix()
        for f in local_dir.rglob("*")
        if f.is_file()
    }
    results = []
    only_local = local_files - remote_files
    only_remote = remote_files - local_files
    both = local_files & remote_files
    for f in only_local:
        if is_ignored(f, config["ignore"] + ALWAYS_IGNORE):
            results.append((f, "ignored"))
        else:
            results.append((f, "only_local"))
    for f in only_remote:
        if is_ignored(f, config.get("remote_ignore", [])):
            results.append((f, "remote_ignored"))
        else:
            results.append((f, "only_remote"))
    for f in both:
        if file_hash(local_dir / f) == remote[f]:
            results.append((f, "same"))
        else:
            results.append((f, "diff"))
    return results
