"""Clipshelf configuration.

Everything lives in config.json in the install root (created by run.py on
first launch). Paths resolve relative to the install root so a zip can be
unpacked anywhere; DATA_DIR is the single knob an installer flips to move
user data to a platform data directory. The GSI token is derived from a
per-install secret, so downloadable game configs just work with no manual
token step.
"""
import hashlib
import json
import os
import secrets
import sys
from pathlib import Path

APP_NAME = "Clipshelf"
PKG_DIR = Path(__file__).parent.resolve()        # src/product
ROOT = PKG_DIR.parent.parent                     # install root (repo root)
# CLIPSHELF_CONFIG points tests (or a portable install) at another config.json
# CLIPSHELF_CONFIG points tests (or a portable install) at another
# config.json. PRODUCT_CONFIG is the pre-rename spelling and still works, so
# an install made before the rename keeps finding its own data.
CONFIG_FILE = Path(os.getenv("CLIPSHELF_CONFIG") or os.getenv("PRODUCT_CONFIG")
                   or ROOT / "config.json")

DEFAULTS = {
    "vod_dir": "",                 # OBS recording output folder (REQUIRED)
    "data_dir": "data",            # projects/assets/renders/highlights/db live here
    "port": 8420,
    "obs_ws": {"host": "127.0.0.1", "port": 4455, "password": ""},
    "secret": "",                  # filled on first run
}


def load() -> dict:
    cfg = dict(DEFAULTS)
    if CONFIG_FILE.exists():
        try:
            cfg.update(json.loads(CONFIG_FILE.read_text()))
        except Exception as e:
            sys.exit(f"config.json is broken ({e}) — fix or delete it and rerun")
    if not cfg.get("secret"):
        cfg["secret"] = secrets.token_hex(16)
        save(cfg)
    return cfg


def save(cfg: dict):
    CONFIG_FILE.write_text(json.dumps(cfg, indent=2))


CFG = load()

PORT = int(CFG["port"])
BASE_URL = f"http://127.0.0.1:{PORT}"
# NOTE: modules read `config.VOD_DIR` (never `from config import VOD_DIR`) —
# the recordings folder is set in the setup wizard and changeable in
# settings, so it must resolve live rather than bind at import.
VOD_DIR = str(Path(CFG["vod_dir"]).expanduser()) if CFG["vod_dir"] else ""

DATA = (ROOT / CFG["data_dir"]).resolve() if not os.path.isabs(CFG["data_dir"]) \
    else Path(CFG["data_dir"])
DATA_DIR = DATA                     # installer-era alias: the one knob to move
# plugins sit next to config.json (the install root) — visible and drop-in
# friendly, not buried in the data tree
PLUGINS_DIR = CONFIG_FILE.parent / "plugins"
PROJECTS = DATA / "projects"
ASSETS = DATA / "assets"
RENDERS = DATA / "renders"
JOBS = DATA / "jobs"
SOUNDS = DATA / "sounds"
EXPORTS = DATA / "exports"          # published cuts ("My Edits" in the editor)
HIGHLIGHTS_DIR = DATA / "highlights"
INGEST_DIR = DATA / "ingest"        # captured buffers waiting to be trimmed
THUMBS = DATA / "thumbs"
DB_PATH = DATA / "app.db"

for d in (PLUGINS_DIR, PROJECTS, ASSETS, RENDERS, JOBS, SOUNDS, EXPORTS,
          HIGHLIGHTS_DIR, INGEST_DIR, THUMBS):
    d.mkdir(parents=True, exist_ok=True)

# stable per-install GSI token — baked into downloadable game configs
GSI_TOKEN = hashlib.sha256(f"gsi:{CFG['secret']}".encode()).hexdigest()[:24]

def configured() -> bool:
    """False until the user has pointed us at a recordings folder — the app
    boots anyway and routes to the setup wizard."""
    return bool(VOD_DIR and os.path.isdir(VOD_DIR))


def apply(changes: dict):
    """Merge settings into config.json and refresh derived globals."""
    global VOD_DIR
    CFG.update(changes or {})
    save(CFG)
    VOD_DIR = str(Path(CFG["vod_dir"]).expanduser()) if CFG.get("vod_dir") else ""


FRONTEND = PKG_DIR / "frontend"
NODE = os.getenv("NODE", "node")
RENDER_WORKER = ROOT / "render" / "render_worker.js"
