#!/usr/bin/env python3
"""
Gradecraft Companion — Resolve Workspace Script
==================================================
Drop a 6-char code (CC-XXXX) into the dialog. The script fetches the
generated .drx from the Gradecraft API, imports it as a PowerGrade,
and applies it to the current clip on the Color Page.

Install:
1. Copy this file to Resolve's user-script folder:
   - macOS:   ~/Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Utility/
   - Windows: %APPDATA%\\Blackmagic Design\\DaVinci Resolve\\Support\\Fusion\\Scripts\\Utility\\
   - Linux:   ~/.local/share/DaVinciResolve/Fusion/Scripts/Utility/
2. In Resolve: Workspace > Scripts > Utility > Gradecraft

Requires: Resolve Studio (Free version blocks scripting).
Author: Gradecraft / felix
"""

import os
import sys
import json
import urllib.request
import urllib.error
import shutil
import tempfile
import platform

# --- Configuration ---------------------------------------------------------
API_BASE = os.environ.get("GRADECRAFT_API", "https://gradecraft.reelyze.pro/api")
APP_NAME = "Gradecraft"
VERSION  = "0.1.0"

# --- Resolve API bootstrap -------------------------------------------------
def get_resolve():
    try:
        import DaVinciResolveScript as dvr_script
        return dvr_script.scriptapp("Resolve")
    except ImportError:
        # Fallback: manual path
        sys_paths = {
            "Darwin":  "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting/Modules/",
            "Windows": "C:\\ProgramData\\Blackmagic Design\\DaVinci Resolve\\Support\\Developer\\Scripting\\Modules\\",
            "Linux":   "/opt/resolve/Developer/Scripting/Modules/"
        }
        sys.path.append(sys_paths.get(platform.system(), ""))
        import DaVinciResolveScript as dvr_script
        return dvr_script.scriptapp("Resolve")

def powergrade_dir():
    """Find the PowerGrades folder of the current user database."""
    home = os.path.expanduser("~")
    candidates = {
        "Darwin":  os.path.join(home, "Library/Application Support/Blackmagic Design/DaVinci Resolve/Support/Resolve Disk Database/Resolve Projects/Users/guest/PowerGrades"),
        "Windows": os.path.join(os.environ.get("APPDATA", ""), "Blackmagic Design", "DaVinci Resolve", "Support", "Resolve Disk Database", "Resolve Projects", "Users", "guest", "PowerGrades"),
        "Linux":   os.path.join(home, ".local/share/DaVinciResolve/Support/Resolve Disk Database/Resolve Projects/Users/guest/PowerGrades")
    }
    return candidates.get(platform.system())

# --- Core ------------------------------------------------------------------
def fetch_drx(code):
    """Fetch generated .drx from the Gradecraft API."""
    url = f"{API_BASE}/grade/{code}"
    req = urllib.request.Request(url, headers={
        "User-Agent": f"{APP_NAME}/{VERSION}",
        "Accept": "application/xml"
    })
    with urllib.request.urlopen(req, timeout=30) as resp:
        if resp.status != 200:
            raise RuntimeError(f"API returned {resp.status}")
        ct = resp.headers.get("Content-Type", "")
        body = resp.read()
        # API may return JSON envelope with {drx_xml, recipe_name, rationale_url}
        if "json" in ct:
            payload = json.loads(body)
            return payload["drx_xml"], payload.get("recipe_name", code), payload.get("rationale_url")
        return body.decode("utf-8"), code, None

def install_powergrade(drx_xml, name):
    """Write .drx into PowerGrades folder so Resolve picks it up."""
    pg_dir = powergrade_dir()
    if not pg_dir or not os.path.isdir(pg_dir):
        # Fallback: write to temp; user can drag it in
        tmp = tempfile.mkdtemp(prefix="gradecraft_")
        out = os.path.join(tmp, f"Gradecraft_{name}.drx")
        with open(out, "w", encoding="utf-8") as f:
            f.write(drx_xml)
        return out, False
    out = os.path.join(pg_dir, f"Gradecraft_{name}.drx")
    with open(out, "w", encoding="utf-8") as f:
        f.write(drx_xml)
    return out, True

def apply_to_current_clip(resolve, drx_path, recipe_name):
    """Switch to Color page; ImportGrade onto current clip."""
    proj_mgr = resolve.GetProjectManager()
    project  = proj_mgr.GetCurrentProject()
    if not project:
        raise RuntimeError("No project open")
    # Switch to Color Page
    resolve.OpenPage("color")
    timeline = project.GetCurrentTimeline()
    if not timeline:
        raise RuntimeError("No timeline open")
    clip = timeline.GetCurrentVideoItem()
    if not clip:
        raise RuntimeError("No clip selected on timeline")
    # ImportGrade is the API hook (Resolve 18+)
    if hasattr(clip, "ImportGrade"):
        ok = clip.ImportGrade(drx_path)
        return bool(ok)
    # Fallback path: user drags from PowerGrade bin
    return False

# --- UI --------------------------------------------------------------------
def main():
    resolve = get_resolve()
    fusion  = resolve.Fusion() if hasattr(resolve, "Fusion") else None

    code = None
    if fusion:
        ui = fusion.UIManager
        disp = bmd.UIDispatcher(ui) if "bmd" in globals() else None
        if disp:
            win = disp.AddWindow({"WindowTitle": APP_NAME, "ID": "gradecraft", "Geometry": [400, 200, 360, 160]}, [
                ui.VGroup({"Spacing": 8}, [
                    ui.Label({"Text": "Paste your Gradecraft code:", "Weight": 0.2}),
                    ui.LineEdit({"ID": "code", "PlaceholderText": "CC-7K3M"}),
                    ui.Button({"ID": "apply", "Text": "Apply Grade"})
                ])
            ])
            itm = win.GetItems()
            def on_apply(ev):
                disp.ExitLoop()
            win.On.apply.Clicked = on_apply
            win.Show()
            disp.RunLoop()
            code = itm["code"].Text.strip()
            win.Hide()

    if not code:
        # CLI fallback
        try:
            code = input(f"{APP_NAME} — paste your code (e.g. CC-7K3M): ").strip()
        except EOFError:
            code = ""
    if not code:
        print("[Gradecraft] No code given, exiting.")
        return 1

    print(f"[Gradecraft] Fetching grade for code: {code}")
    try:
        drx_xml, recipe_name, rationale_url = fetch_drx(code)
    except urllib.error.HTTPError as e:
        print(f"[Gradecraft] API error {e.code}: {e.reason}")
        return 2
    except Exception as e:
        print(f"[Gradecraft] Fetch failed: {e}")
        return 3

    drx_path, in_pg = install_powergrade(drx_xml, recipe_name)
    print(f"[Gradecraft] Wrote .drx → {drx_path}")
    if not in_pg:
        print("[Gradecraft] PowerGrades folder not found — drag the file into Resolve manually.")
        return 0

    try:
        ok = apply_to_current_clip(resolve, drx_path, recipe_name)
        if ok:
            print(f"[Gradecraft] Applied '{recipe_name}' to current clip.")
        else:
            print(f"[Gradecraft] Imported as PowerGrade '{recipe_name}'. Drag onto a clip to apply.")
    except Exception as e:
        print(f"[Gradecraft] Apply failed: {e}")
        print("[Gradecraft] PowerGrade is in your bin — drag it onto the clip.")

    if rationale_url:
        print(f"[Gradecraft] Rationale: {rationale_url}")

    return 0

if __name__ == "__main__":
    sys.exit(main())
