"""First-run setup — in the app, not in a terminal.

Everything run.py used to ask on the console lives here as endpoints the
wizard drives: find the recordings folder (read straight out of OBS's own
profile config when we can), test the OBS websocket, pick the voice track
by listening to 3-second samples, and save. No restart: the recordings
folder is resolved live everywhere (see config.VOD_DIR).
"""
import configparser
import glob
import os
import platform
import subprocess
import tempfile
import time
from pathlib import Path

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import FileResponse

from clipshelf import config, db, obs_client

router = APIRouter(tags=["setup"])


# ---------------------------------------------------------------- OBS config
def _obs_config_roots() -> list:
    home = Path.home()
    if os.name == "nt":
        return [Path(os.getenv("APPDATA", home / "AppData/Roaming")) / "obs-studio"]
    if platform.system() == "Darwin":
        return [home / "Library/Application Support/obs-studio"]
    return [home / ".config/obs-studio",
            home / ".var/app/com.obsproject.Studio/config/obs-studio"]


def obs_profile_info() -> dict:
    """Recording folder + replay-buffer state straight from OBS's profile
    ini — the difference between "type a path" and "is this your folder?"."""
    out = {"found": False, "vod_dir": "", "replay_buffer": False, "profile": ""}
    for root in _obs_config_roots():
        if not root.is_dir():
            continue
        inis = sorted(glob.glob(str(root / "basic" / "profiles" / "*" / "basic.ini")),
                      key=lambda p: os.path.getmtime(p), reverse=True)
        for ini in inis:
            cp = configparser.RawConfigParser(strict=False)
            try:
                cp.read(ini, encoding="utf-8")
            except Exception:
                continue
            mode = cp.get("Output", "Mode", fallback="Simple")
            path = ""
            if mode.lower().startswith("adv"):
                path = cp.get("AdvOut", "RecFilePath", fallback="")
                rb = cp.getboolean("AdvOut", "RecRB", fallback=False)
            else:
                path = cp.get("SimpleOutput", "FilePath", fallback="")
                rb = cp.getboolean("SimpleOutput", "RecRB", fallback=False)
            if path and os.path.isdir(path):
                out.update(found=True, vod_dir=path, replay_buffer=bool(rb),
                           profile=os.path.basename(os.path.dirname(ini)))
                return out
    return out


def _guesses() -> list:
    """Fallback candidates when OBS's config isn't readable."""
    home = Path.home()
    cands = [home / "Videos", home / "Movies", home / "Videos" / "OBS",
             home / "Desktop"]
    return [str(c) for c in cands if c.is_dir()]


# ---------------------------------------------------------------- state
@router.get("/api/setup/state")
def setup_state():
    from clipshelf import desktop
    obs_info = obs_profile_info()
    ws = config.CFG.get("obs_ws") or {}
    return {
        "configured": config.configured(),
        "vod_dir": config.VOD_DIR,
        "vod_count": len([f for f in os.listdir(config.VOD_DIR)
                          if f.lower().endswith(".mp4") and not f.endswith("_proxy.mp4")])
        if config.configured() else 0,
        "obs_detected": obs_info,
        "obs_ws": {"host": ws.get("host", "127.0.0.1"), "port": ws.get("port", 4455),
                   "has_password": bool(ws.get("password"))},
        "obs_connected": obs_client.connected(),
        "deadlock_accounts": config.CFG.get("deadlock_accounts", ""),
        "guesses": _guesses(),
        "desktop": desktop.is_desktop(),
        "voice_track": int(config.CFG.get("voice_track") or 1),
        "frameless": bool(config.CFG.get("frameless", True)),
        "whisper_model": str(config.CFG.get("whisper_model") or "small"),
        "sep": os.sep,
    }


@router.get("/api/setup/browse")
def setup_browse(path: str = ""):
    """Folder browser for the wizard (browser mode; desktop mode gets a
    native dialog). Directories only."""
    if not path:
        if os.name == "nt":
            import string
            drives = [f"{d}:\\" for d in string.ascii_uppercase
                      if os.path.exists(f"{d}:\\")]
            return {"path": "", "parent": None, "dirs": drives}
        path = str(Path.home())
    p = Path(path).expanduser()
    if not p.is_dir():
        raise HTTPException(404, "no such folder")
    try:
        dirs = sorted([str(c) for c in p.iterdir()
                       if c.is_dir() and not c.name.startswith(".")],
                      key=str.lower)[:400]
    except PermissionError:
        raise HTTPException(403, "permission denied")
    videos = len([f for f in os.listdir(p) if f.lower().endswith(".mp4")])
    return {"path": str(p), "parent": str(p.parent) if p.parent != p else None,
            "dirs": dirs, "mp4s": videos}


@router.post("/api/setup/pick-folder")
def setup_pick_folder():
    """Native folder dialog — desktop shell only."""
    from clipshelf import desktop
    picked = desktop.pick_folder()
    if picked is None:
        raise HTTPException(400, "native folder picker unavailable in browser mode")
    return {"path": picked}


# ---------------------------------------------------------------- OBS test
@router.post("/api/setup/obs-test")
async def setup_obs_test(request: Request):
    body = {}
    try:
        body = await request.json()
    except Exception:
        pass
    ws = dict(config.CFG.get("obs_ws") or {})
    for k in ("host", "port", "password"):
        if k in body and body[k] != "":
            ws[k] = body[k]
    try:
        import obsws_python as obs
    except ImportError:
        raise HTTPException(503, "obsws-python isn't installed")
    try:
        client = obs.ReqClient(host=str(ws.get("host") or "127.0.0.1"),
                               port=int(ws.get("port") or 4455),
                               password=str(ws.get("password") or ""), timeout=4)
        ver = client.get_version()
        rec = client.get_record_status()
        try:
            rb = client.get_replay_buffer_status().output_active
        except Exception:
            rb = False
        return {"ok": True, "obs_version": getattr(ver, "obs_version", "?"),
                "recording": bool(rec.output_active), "replay_buffer": bool(rb)}
    except Exception as e:
        return {"ok": False, "error": f"{type(e).__name__}: {e}"[:200]}


# ---------------------------------------------------------------- audio tracks
def _newest_vod() -> str:
    if not config.configured():
        return ""
    files = [os.path.join(config.VOD_DIR, f) for f in os.listdir(config.VOD_DIR)
             if f.lower().endswith(".mp4") and not f.endswith("_proxy.mp4")]
    return max(files, key=os.path.getmtime) if files else ""


@router.get("/api/setup/tracks")
def setup_tracks():
    """Audio tracks in the newest recording — OBS multi-track setups put the
    mic on its own track, and transcription needs to know which one."""
    path = _newest_vod()
    if not path:
        return {"file": "", "tracks": []}
    r = subprocess.run(
        ["ffprobe", "-v", "error", "-select_streams", "a",
         "-show_entries", "stream=index,channels,codec_name:stream_tags=title",
         "-of", "json", path], capture_output=True, text=True, timeout=60, encoding="utf-8", errors="replace")
    import json as J
    streams = (J.loads(r.stdout or "{}") or {}).get("streams", [])
    return {"file": os.path.basename(path),
            "tracks": [{"n": i + 1, "channels": s.get("channels"),
                        "codec": s.get("codec_name"),
                        "title": (s.get("tags") or {}).get("title", "")}
                       for i, s in enumerate(streams)]}


@router.get("/api/setup/audition")
def setup_audition(track: int = 1, at: float = 0.35):
    """3 seconds of one audio track, so the user can hear which is which."""
    path = _newest_vod()
    if not path:
        raise HTTPException(404, "no recordings yet")
    dur = 0.0
    try:
        p = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
                            "format=duration", "-of", "csv=p=0", path],
                           capture_output=True, text=True, timeout=30, encoding="utf-8", errors="replace")
        dur = float(p.stdout.strip() or 0)
    except Exception:
        pass
    start = max(0.0, dur * max(0.0, min(0.95, at)))
    out = Path(tempfile.gettempdir()) / f"audition-{os.getpid()}-{int(time.time()*1000)}.mp3"
    r = subprocess.run(
        ["ffmpeg", "-nostdin", "-v", "error", "-y", "-ss", f"{start:.2f}",
         "-t", "3", "-i", path, "-map", f"0:a:{max(1, int(track)) - 1}",
         "-ac", "2", "-c:a", "libmp3lame", "-q:a", "5", str(out)],
        capture_output=True, timeout=120)
    if r.returncode != 0 or not out.exists():
        raise HTTPException(400, "couldn't sample that track")
    from starlette.background import BackgroundTask
    return FileResponse(out, media_type="audio/mpeg",
                        background=BackgroundTask(os.unlink, str(out)))


# ---------------------------------------------------------------- save
@router.post("/api/setup/save")
async def setup_save(request: Request):
    body = await request.json()
    changed = {}
    if "vod_dir" in body:
        p = str(body["vod_dir"] or "").strip().strip('"')
        if p and not os.path.isdir(os.path.expanduser(p)):
            raise HTTPException(400, "that folder doesn't exist")
        changed["vod_dir"] = os.path.expanduser(p)
    if "obs_ws" in body and isinstance(body["obs_ws"], dict):
        ws = dict(config.CFG.get("obs_ws") or {})
        for k in ("host", "port", "password"):
            if k in body["obs_ws"]:
                ws[k] = body["obs_ws"][k]
        ws["port"] = int(ws.get("port") or 4455)
        changed["obs_ws"] = ws
    if "voice_track" in body:
        changed["voice_track"] = max(1, min(8, int(body["voice_track"])))
    if not changed:
        raise HTTPException(400, "nothing to save")
    config.apply(changed)
    # reconnect OBS with the new credentials + pick up the new folder now
    try:
        obs_client.reset()
        obs_client.check_and_reconnect()
    except Exception:
        pass
    try:
        db.sync_vod_library()
    except Exception:
        pass
    return {"status": "success", "configured": config.configured(),
            "vod_dir": config.VOD_DIR}
