import json
import hashlib
from pathlib import Path

# Point this at your real folder on the server (where the zips live)
DB_DIR = Path("/mnt/user/appdata/webserver-php8.4/html/downloads/EldenRingModsDB")
PACKS_JSON = DB_DIR / "packs.json"

# Cache file to avoid re-hashing huge zips every run (server-side only)
HASH_CACHE_JSON = DB_DIR / "hash_cache.json"

REMOVE_MISSING = True  # if True, entries whose zip no longer exists are removed


def load_existing() -> dict:
    if PACKS_JSON.exists():
        try:
            data = json.loads(PACKS_JSON.read_text(encoding="utf-8"))
            if isinstance(data, list):
                # index by zip filename
                return {
                    item.get("zip"): item
                    for item in data
                    if isinstance(item, dict) and item.get("zip")
                }
        except Exception:
            pass
    return {}


def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        while True:
            b = f.read(chunk_size)
            if not b:
                break
            h.update(b)
    return h.hexdigest()


def load_hash_cache() -> dict:
    if HASH_CACHE_JSON.exists():
        try:
            data = json.loads(HASH_CACHE_JSON.read_text(encoding="utf-8"))
            return data if isinstance(data, dict) else {}
        except Exception:
            return {}
    return {}


def save_hash_cache(cache: dict) -> None:
    HASH_CACHE_JSON.write_text(json.dumps(cache, indent=2), encoding="utf-8")


def get_cached_sha256(zip_path: Path, cache: dict) -> str:
    st = zip_path.stat()
    key = zip_path.name
    sig = {"size": st.st_size, "mtime": int(st.st_mtime)}

    entry = cache.get(key)
    if entry and entry.get("sig") == sig and entry.get("sha256"):
        return entry["sha256"]

    digest = sha256_file(zip_path)
    cache[key] = {"sig": sig, "sha256": digest}
    return digest


def write_sidecar_sha256(zip_path: Path, digest: str) -> None:
    # Creates: "Pack.zip.sha256"
    sidecar = zip_path.with_name(zip_path.name + ".sha256")
    sidecar.write_text(digest + "\n", encoding="utf-8")


def main():
    existing = load_existing()
    hash_cache = load_hash_cache()

    zips = sorted(
        [p for p in DB_DIR.iterdir() if p.is_file() and p.suffix.lower() == ".zip"],
        key=lambda p: p.name.lower()
    )

    new_list = []
    zip_names_on_disk = {p.name for p in zips}

    for zp in zips:
        # --- Hash + sidecar generation ---
        digest = get_cached_sha256(zp, hash_cache)
        write_sidecar_sha256(zp, digest)

        # --- Your existing packs.json merge logic ---
        if zp.name in existing:
            item = existing[zp.name]
            item["zip"] = zp.name
            item["name"] = item.get("name") or zp.stem
            item.setdefault("version", "")
            item.setdefault("nexus_url", "")
            item.setdefault("image", "")
            item.setdefault("description", "")

            # Optional: also store sha256 in packs.json (handy for debugging)
            item["sha256"] = digest

            new_list.append(item)
        else:
            new_list.append({
                "name": zp.stem,
                "version": "",
                "zip": zp.name,
                "nexus_url": "",
                "image": "",
                "description": "",
                "sha256": digest  # optional
            })

    if not REMOVE_MISSING:
        for zip_name, item in existing.items():
            if zip_name not in zip_names_on_disk:
                new_list.append(item)

    PACKS_JSON.write_text(json.dumps(new_list, indent=2), encoding="utf-8")
    save_hash_cache(hash_cache)

    print(f"Wrote {PACKS_JSON} with {len(new_list)} entries.")
    print(f"Updated sha256 sidecars for {len(zips)} zip(s). Cache: {HASH_CACHE_JSON}")


if __name__ == "__main__":
    main()