from __future__ import annotations

import shlex
import subprocess
import time
from typing import Any

from .client import AppraisalForgeClient


def build_reply(agent_cmd: str, user_text: str, appraisal_id: int | None = None, timeout_seconds: int = 120) -> str:
    text = (user_text or "").strip()
    cmd = (agent_cmd or "").strip()
    if not cmd:
        return "[relay-worker] No agent command configured."
    if not text:
        return "[relay-worker] Empty message received."

    parts = shlex.split(cmd)
    if not parts:
        return "[relay-worker] Invalid agent command."

    resolved: list[str] = []
    used_placeholder = False
    appraisal_value = "" if appraisal_id is None else str(appraisal_id)
    for part in parts:
        replaced = part.replace("{message}", text).replace("{appraisal_id}", appraisal_value)
        if replaced != part:
            used_placeholder = True
        resolved.append(replaced)
    if not used_placeholder:
        resolved.append(text)

    try:
        result = subprocess.run(
            resolved,
            capture_output=True,
            text=True,
            timeout=max(1, int(timeout_seconds)),
            check=False,
        )
    except subprocess.TimeoutExpired:
        return f"[relay-worker] Agent timed out after {timeout_seconds}s."
    except Exception as exc:
        return f"[relay-worker] Agent execution failed: {exc}"

    stdout = (result.stdout or "").strip()
    stderr = (result.stderr or "").strip()
    if result.returncode != 0:
        if stderr:
            return f"[relay-worker] Agent command failed ({result.returncode}): {stderr[:1200]}"
        return f"[relay-worker] Agent command failed ({result.returncode}) with no stderr."
    if not stdout:
        if stderr:
            return f"[relay-worker] Agent returned no stdout. stderr: {stderr[:1200]}"
        return "[relay-worker] Agent returned no output."
    return stdout[:16000]


def run_worker_forever(
    client: AppraisalForgeClient,
    agent_cmd: str,
    appraisal_id: int | None = None,
    poll_wait_seconds: int = 20,
    poll_limit: int = 1,
    agent_timeout_seconds: int = 120,
) -> None:
    while True:
        try:
            polled = client.relay_poll_messages(
                appraisal_id=appraisal_id,
                limit=poll_limit,
                wait_seconds=poll_wait_seconds,
            )
            messages = polled.get("messages") or []
            if not isinstance(messages, list):
                messages = []
            if not messages:
                continue
            for msg in messages:
                message_id = str(msg.get("id") or "").strip()
                if not message_id:
                    continue
                msg_text = str(msg.get("text") or "")
                msg_appraisal = msg.get("appraisal_id")
                try:
                    msg_appraisal_id = int(msg_appraisal) if msg_appraisal is not None else appraisal_id
                except Exception:
                    msg_appraisal_id = appraisal_id
                reply = build_reply(
                    agent_cmd=agent_cmd,
                    user_text=msg_text,
                    appraisal_id=msg_appraisal_id,
                    timeout_seconds=agent_timeout_seconds,
                )
                client.relay_post_reply(
                    message_id=message_id,
                    appraisal_id=msg_appraisal_id,
                    text=reply,
                )
        except KeyboardInterrupt:
            raise
        except Exception:
            time.sleep(2)
