"""MetaGhost Desktop 1.0.81. Python 3.10+. https://metaghost.io/docs/api#quickstart"""
import json
import os
import shutil
import sys
import time
from urllib.error import HTTPError
from urllib.parse import urlparse, quote
from urllib.request import Request, build_opener, HTTPRedirectHandler, ProxyHandler


class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise RuntimeError("API redirects are not followed; the key stays on the local server.")


def run_job(base_url, api_key, body, timeout_seconds=600, poll_seconds=2, on_accepted=print):
    base = urlparse(base_url)
    if base.scheme != "http" or base.hostname not in ("127.0.0.1", "localhost", "::1") or base.username or base.password or base.query or base.fragment:
        raise ValueError("Run this example on the Desktop computer with an HTTP loopback API URL.")
    if not api_key:
        raise ValueError("Set METAGHOST_API_KEY to the key from Desktop Settings > API.")
    if body.get("driveInputFolder") or body.get("driveOutputFolder"):
        raise ValueError("This starter uses local files and local output. Follow the guide's Drive section for transfer polling and delivery.")
    opener = build_opener(NoRedirect, ProxyHandler({}))

    def request(path, data=None, extra_headers=None, authenticated=True):
        headers = {"Authorization": "Bearer " + api_key} if authenticated else {}
        headers.update(extra_headers or {})
        if data is not None:
            headers["Content-Type"] = "application/json"
        req = Request(base_url.rstrip("/") + path, data=None if data is None else json.dumps(data).encode(), headers=headers)
        try:
            return opener.open(req, timeout=30)
        except HTTPError as error:
            raise RuntimeError(f"HTTP {error.code}: {error.read().decode()}; Retry-After: {error.headers.get('Retry-After', 'not provided')}") from error

    def read_json(path, **kwargs):
        with request(path, **kwargs) as response:
            return json.load(response)

    health = read_json("/health", authenticated=False)
    if health.get("status") != "ok" or health.get("version") != "1.0.81":
        raise RuntimeError("This example targets MetaGhost Desktop 1.0.81. Check the documentation for your installed version before submitting a job.")
    try:
        accepted = read_json("/jobs", data=body)
    except Exception as error:
        raise RuntimeError(f"Submission could not be confirmed. Desktop 1.0.81 does not deduplicate submissions; check GET /api/jobs before submitting again. {error}") from error
    job_id = accepted["jobId"]
    on_accepted(job_id)
    deadline = time.monotonic() + timeout_seconds
    while time.monotonic() < deadline:
        job = read_json("/jobs/" + quote(job_id, safe=""))
        if job["status"] in ("failed", "cancelled"):
            raise RuntimeError(f"Job {job_id}: {job['status']}. {job.get('error') or job.get('errors', [])}")
        if job["status"] == "completed":
            drive = job.get("drive") or {}
            if job.get("errors") or accepted.get("skipped") or drive.get("uploadError") or (drive.get("uploadResult") or {}).get("errors"):
                raise RuntimeError(f"Job {job_id} has partial results. Inspect errors/skipped files before using its output.")
            return job, lambda index: request("/download/" + quote(job_id, safe="") + "?index=" + str(index))
        time.sleep(poll_seconds)
    raise TimeoutError(f"Stopped waiting for {job_id}. The Desktop job was not cancelled; inspect it before retrying.")


if __name__ == "__main__":
    try:
        if len(sys.argv) != 4:
            raise ValueError("Usage: python quickstart.py INPUT_FILE DESKTOP_OUTPUT_FOLDER NEW_DOWNLOAD_FILE")
        source, output_folder, download_file = sys.argv[1:]
        if os.path.exists(download_file):
            raise ValueError("Choose a new download filename. Existing files are never overwritten.")
        body = {"filePath": source, "outputFolder": output_folder, "batchCount": 1}
        if os.environ.get("METAGHOST_PRESET"):
            body["preset"] = os.environ["METAGHOST_PRESET"]
        job, download = run_job(os.environ.get("METAGHOST_API_URL", "http://127.0.0.1:3847/api"), os.environ.get("METAGHOST_API_KEY"), body,
                                on_accepted=lambda job_id: print("Accepted " + job_id, flush=True))
        with download(0) as response, open(download_file, "xb") as destination:
            shutil.copyfileobj(response, destination)
        print(f"Completed {job['jobId']}. Downloaded first output to {download_file}")
    except Exception as error:
        print(str(error), file=sys.stderr)
        sys.exit(1)
