import json
import os
import subprocess
import sys

from .config import PROJECT_DIR


class PortalCaptureError(RuntimeError):
    def __init__(self, message, used_restore_token=False):
        super().__init__(message)
        self.used_restore_token = used_restore_token


def portal_helper_path():
    return os.path.join(PROJECT_DIR, "portal_capture_frame")


def require_portal_helper():
    helper = portal_helper_path()
    if not os.path.exists(helper):
        sys.exit(f"missing {helper}; run `make` in {PROJECT_DIR}")
    if not os.access(helper, os.X_OK):
        sys.exit(f"portal helper is not executable: {helper}")
    return helper


def read_portal_restore_token(path):
    try:
        with open(path, "r", encoding="utf-8") as handle:
            return handle.read().strip()
    except FileNotFoundError:
        return ""
    except OSError as exc:
        sys.exit(f"failed to read portal restore token {path}: {exc}")


def write_portal_restore_token(path, token):
    directory = os.path.dirname(path)
    if directory:
        os.makedirs(directory, exist_ok=True)
    temp_path = f"{path}.tmp"
    try:
        with open(temp_path, "w", encoding="utf-8") as handle:
            handle.write(token)
            handle.write("\n")
        os.replace(temp_path, path)
    except OSError as exc:
        sys.exit(f"failed to write portal restore token {path}: {exc}")


def capture_portal_window(output, restore_token_path, reselect, timeout):
    helper = require_portal_helper()
    restore_token = ""
    if restore_token_path and not reselect:
        restore_token = read_portal_restore_token(restore_token_path)

    command = [helper, "--window", output, "--timeout", str(timeout)]
    if restore_token_path:
        command.append("--persist")
    if restore_token:
        command.extend(["--restore-token", restore_token])

    try:
        result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    except subprocess.CalledProcessError as exc:
        message = exc.stderr.strip() if exc.stderr else str(exc)
        raise PortalCaptureError(message, used_restore_token=bool(restore_token)) from exc

    capture_info = {}
    if result.stdout.strip():
        try:
            capture_info = json.loads(result.stdout)
        except json.JSONDecodeError as exc:
            raise PortalCaptureError(f"failed to parse portal capture metadata: {exc}: {result.stdout!r}") from exc

    new_restore_token = str(capture_info.get("restore_token", "") or "")
    if restore_token_path and new_restore_token:
        write_portal_restore_token(restore_token_path, new_restore_token)

    return capture_info
