#!/usr/bin/env python3
"""
AutoJanet Agent Entrypoint

Bootstraps an agent container:
1. Authenticates to OpenBao via AppRole (OPENBAO_ROLE_ID + OPENBAO_SECRET_ID)
2. Fetches all secrets for AGENT_ROLE from OpenBao
3. Writes an opencode-compatible ~/.config/opencode/ environment
4. Runs opencode non-interactively with the task as the prompt

Environment variables (injected by dispatcher Job):
  AGENT_ROLE        — e.g. "coder"
  TASK_ID           — Vikunja task ID
  TASK_TITLE        — Vikunja task title (used as initial prompt)
  OPENBAO_ADDR      — e.g. "http://openbao.openbao.svc.cluster.local:8200"
  OPENBAO_ROLE_ID   — AppRole role_id
  OPENBAO_SECRET_ID — AppRole secret_id
  LITELLM_BASE_URL  — e.g. "https://llm.ctz.fyi"
  VIKUNJA_BASE_URL  — e.g. "https://tasks.ctz.fyi"
  FORGEJO_BASE_URL  — e.g. "https://git.ctz.fyi"
"""

import json
import logging
import os
import subprocess
import sys
import tempfile
from pathlib import Path

import httpx

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
    stream=sys.stdout,
)
log = logging.getLogger("entrypoint")

OPENBAO_ADDR = os.environ["OPENBAO_ADDR"]
OPENBAO_ROLE_ID = os.environ["OPENBAO_ROLE_ID"]
OPENBAO_SECRET_ID = os.environ["OPENBAO_SECRET_ID"]
AGENT_ROLE = os.environ["AGENT_ROLE"]
TASK_ID = os.environ["TASK_ID"]
TASK_TITLE = os.environ.get("TASK_TITLE", f"Task {TASK_ID}")
LITELLM_BASE_URL = os.environ.get("LITELLM_BASE_URL", "https://llm.ctz.fyi")
VIKUNJA_BASE_URL = os.environ.get("VIKUNJA_BASE_URL", "https://tasks.ctz.fyi")
FORGEJO_BASE_URL = os.environ.get("FORGEJO_BASE_URL", "https://git.ctz.fyi")

HOME = Path(os.environ.get("HOME", "/home/agent"))
CONFIG_DIR = HOME / ".config" / "opencode"


def get_openbao_token() -> str:
    resp = httpx.post(
        f"{OPENBAO_ADDR}/v1/auth/approle/login",
        json={"role_id": OPENBAO_ROLE_ID, "secret_id": OPENBAO_SECRET_ID},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["auth"]["client_token"]


def get_secret(bao_token: str, path: str, key: str) -> str:
    resp = httpx.get(
        f"{OPENBAO_ADDR}/v1/secret/data/{path}",
        headers={"X-Vault-Token": bao_token},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["data"]["data"][key]


def fetch_role_secrets(bao_token: str, role: str) -> dict:
    """Fetch all secrets for a role. Returns dict of secret_name -> value."""
    secrets = {}
    secret_names = ["litellm-key", "vikunja-token", "forgejo-token", "argocd-token"]
    for name in secret_names:
        try:
            key = "token" if name != "litellm-key" else "key"
            secrets[name] = get_secret(bao_token, f"autojanet/{role}/{name}", key)
            log.info("Fetched secret: %s", name)
        except Exception as e:
            log.warning("Could not fetch %s: %s", name, e)
    return secrets


def write_opencode_config(secrets: dict, role: str) -> None:
    """Write opencode config with the agent's secrets and MCP server tokens."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)

    litellm_key = secrets.get("litellm-key", "")
    vikunja_token = secrets.get("vikunja-token", "")
    forgejo_token = secrets.get("forgejo-token", "")

    config = {
        "model": f"litellm/copilot/claude-sonnet-4.6",
        "providers": {
            "litellm": {
                "apiKey": litellm_key,
                "baseURL": f"{LITELLM_BASE_URL}/v1",
            }
        },
        "mcp": {
            "vikunja": {
                "type": "sse",
                "url": f"{LITELLM_BASE_URL}/mcp/vikunja",
                "headers": {
                    "x-vikunja-token": vikunja_token,
                }
            },
            "forgejo": {
                "type": "sse",
                "url": f"{LITELLM_BASE_URL}/mcp/forgejo",
                "headers": {
                    "x-forgejo-token": forgejo_token,
                }
            },
        }
    }

    config_path = CONFIG_DIR / "config.json"
    config_path.write_text(json.dumps(config, indent=2))
    log.info("Wrote opencode config to %s", config_path)

    # Write AGENTS.md with role-specific instructions
    agent_md_src = Path(f"/app/agents/{role}.agent.md")
    agents_md_dst = CONFIG_DIR / "AGENTS.md"
    if agent_md_src.exists():
        agents_md_dst.write_text(agent_md_src.read_text())
        log.info("Loaded agent instructions from %s", agent_md_src)
    else:
        log.warning("No agent file found at %s", agent_md_src)


def build_prompt(task_id: str, task_title: str) -> str:
    return f"""You are the AutoJanet agent for role: {AGENT_ROLE}

Your current task (Vikunja task #{task_id}):
{task_title}

Instructions:
1. Read the task carefully.
2. Fetch full task details from Vikunja if needed.
3. Complete the task using the tools available to you.
4. Move the task to Done in Vikunja when complete.
5. Open a PR if code was written.
6. Do not ask for confirmation — act autonomously within your constraints.
"""


def run_opencode(prompt: str) -> int:
    """Run opencode non-interactively with the given prompt."""
    with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
        f.write(prompt)
        prompt_file = f.name

    cmd = ["opencode", "run", "--no-input", "--prompt-file", prompt_file]
    log.info("Running: %s", " ".join(cmd))

    result = subprocess.run(cmd, check=False)
    return result.returncode


def main() -> None:
    log.info("Agent entrypoint: role=%s task=%s", AGENT_ROLE, TASK_ID)

    bao_token = get_openbao_token()
    log.info("OpenBao authenticated")

    secrets = fetch_role_secrets(bao_token, AGENT_ROLE)
    write_opencode_config(secrets, AGENT_ROLE)

    prompt = build_prompt(TASK_ID, TASK_TITLE)
    rc = run_opencode(prompt)

    if rc != 0:
        log.error("opencode exited with code %d", rc)
        sys.exit(rc)

    log.info("Agent completed task %s successfully", TASK_ID)


if __name__ == "__main__":
    main()
