"""Where ParVagues' sample banks come from, resolved the way SuperDirt does.

ONE definition of "what banks exist", imported by both `bank-check.py` and
`sample-watcher.py`. A watcher and a checker that disagree about what a bank is
would be the worst kind of bug here: the checker says clean, the watcher loads
something else, and the rig plays a sound nobody chose.

Mirrors the three loaders in `start_and_midi.scd`, in the order they run.
Later loaders WIN — `loadSoundFiles` defaults to appendToExisting = false, so a
same-named folder frees the earlier bank's buffers and takes the name.
"""
from __future__ import annotations

import time
from pathlib import Path

AUDIO = {".wav", ".aif", ".aiff", ".flac", ".ogg", ".mp3",
         ".WAV", ".AIF", ".AIFF", ".FLAC"}

LOADERS = [
    # (label, root, folder depth below root, bank-name function)
    ("Dirt-Samples",
     Path("~/.local/share/SuperCollider/downloaded-quarks/Dirt-Samples").expanduser(),
     1, lambda n: n),
    ("extra",
     Path("~/Work/Sound/Samples/extra").expanduser(),
     1, lambda n: n),
    ("drum-machines",
     Path("~/Work/Sound/Samples/tidal-drum-machines/machines").expanduser(),
     2, lambda n: n.replace("-", "")),      # the namingFunction in the boot
]

ROOTS = [str(r) for _, r, _, _ in LOADERS]


def playable(folder: Path) -> list[Path]:
    """Files SuperDirt will actually load.

    It globs with pathMatch("*"), which SKIPS DOTFILES. Some packs ship an
    AppleDouble `._X.wav` beside every real `X.wav`; `ls` hides those too, so
    the folder looks clean while a naive glob double-counts it. That mismatch
    once produced "jbk_kick expected 508, got 254" and the tool was the one
    that was wrong.
    """
    try:
        return sorted(p for p in folder.iterdir()
                      if p.is_file() and not p.name.startswith(".")
                      and p.suffix in AUDIO)
    except OSError:
        return []


def scan() -> dict[str, list[tuple]]:
    """bank name -> [(loader_label, folder_path, n_files, newest_mtime), ...]

    More than one entry means the name is SHADOWED; the last one wins.
    """
    banks: dict[str, list[tuple]] = {}
    for label, root, depth, name_of in LOADERS:
        if not root.is_dir():
            continue
        try:
            if depth == 1:
                folders = sorted(p for p in root.iterdir() if p.is_dir())
            else:
                folders = [g for f in sorted(root.iterdir()) if f.is_dir()
                           for g in sorted(f.iterdir()) if g.is_dir()]
        except OSError:
            continue
        for f in folders:
            files = playable(f)
            if not files:
                continue
            mt = max(p.stat().st_mtime for p in files)
            banks.setdefault(name_of(f.name), []).append(
                (label, f, len(files), mt))
    return banks


def under_a_root(path: str) -> bool:
    p = str(Path(path).expanduser().resolve())
    return any(p == r or p.startswith(r.rstrip("/") + "/") for r in ROOTS)


def fmt_time(mt: float) -> str:
    return time.strftime("%Y-%m-%d %H:%M", time.localtime(mt))
