"""Read-only model arbiter status contract for the Pairling Mac daemon."""

from __future__ import annotations

import json
import os
import subprocess
from pathlib import Path
from typing import Mapping


DEFAULT_SINCE = "0000-01-01T00:00:00.000Z"
DEFAULT_LIMIT = 50
DEFAULT_MAX_LIMIT = 200
DEFAULT_ORGANIZER_BIN = str(Path.home() / "projects" / "metal-perception-memory-substrate" / "organizer")
DEFAULT_ORGANIZER_CONFIG = str(Path.home() / "projects" / "metal-perception-memory-substrate" / "organizer.yaml")


class ModelStatusError(ValueError):
    pass


def _env(env: Mapping[str, str] | None) -> Mapping[str, str]:
    return os.environ if env is None else env


def model_status_max_limit(env: Mapping[str, str] | None = None) -> int:
    current_env = _env(env)
    try:
        value = int(current_env.get("COMPANION_MODEL_STATUS_MAX_LIMIT", str(DEFAULT_MAX_LIMIT)))
    except ValueError:
        return DEFAULT_MAX_LIMIT
    return max(1, min(value, 1000))


def organizer_bin(env: Mapping[str, str] | None = None) -> str:
    return _env(env).get("MODEL_ARBITER_ORGANIZER_BIN") or DEFAULT_ORGANIZER_BIN


def organizer_config(env: Mapping[str, str] | None = None) -> str:
    return _env(env).get("MODEL_ARBITER_ORGANIZER_CONFIG") or DEFAULT_ORGANIZER_CONFIG


def build_model_status_command(
    run: str | Path,
    since: str = DEFAULT_SINCE,
    limit: int = DEFAULT_LIMIT,
    *,
    organizer: str | None = None,
    config: str | None = None,
    env: Mapping[str, str] | None = None,
) -> list[str]:
    if not str(run).strip():
        raise ModelStatusError("run is required")
    if limit <= 0:
        raise ModelStatusError("limit must be positive")

    current_limit = min(limit, model_status_max_limit(env))
    return [
        organizer or organizer_bin(env),
        "--config",
        config or organizer_config(env),
        "model",
        "status",
        "--run",
        str(run),
        "--since",
        since,
        "--limit",
        str(current_limit),
        "--json",
    ]


def fetch_model_status(
    run: str | Path,
    since: str = DEFAULT_SINCE,
    limit: int = DEFAULT_LIMIT,
    *,
    timeout_seconds: float = 5.0,
    env: Mapping[str, str] | None = None,
) -> dict:
    command = build_model_status_command(run, since=since, limit=limit, env=env)
    try:
        completed = subprocess.run(command, capture_output=True, text=True, timeout=timeout_seconds, check=False)
    except OSError as exc:
        raise ModelStatusError(f"could not execute model status adapter: {exc}") from exc
    except subprocess.TimeoutExpired as exc:
        raise ModelStatusError("model status adapter timed out") from exc
    if completed.returncode != 0:
        detail = (completed.stderr or completed.stdout or "").strip()[:500]
        raise ModelStatusError(f"model status adapter failed: {detail}")
    try:
        payload = json.loads(completed.stdout)
    except json.JSONDecodeError as exc:
        raise ModelStatusError("model status adapter returned invalid JSON") from exc
    if payload.get("status") != "model_arbiter.readonly.v1" or payload.get("read_only") is not True:
        raise ModelStatusError("model status adapter returned an unexpected contract")
    payload["consumer"] = "pairling"
    payload["adapter"] = "pairling.model_status"
    return payload
