"""SWE-AF HTTP bridge for all AI remediation.

Routes findings to SWE-AF's DAG executor via AgentField's async API.
Uses httpx for non-blocking HTTP in async context.
"""

from __future__ import annotations

import asyncio
import json
import logging
import os
from typing import TYPE_CHECKING, Any

import httpx

from forge.execution.sweaf_adapter import (
    build_plan_result,
    finding_to_planned_issue,
    sweaf_result_to_coder_fix_results,
    write_issue_files,
)
from forge.schemas import (
    AuditFinding,
    CoderFixResult,
    FixOutcome,
    RemediationItem,
)

if TYPE_CHECKING:
    from forge.config import ForgeConfig
    from forge.schemas import ForgeExecutionState

logger = logging.getLogger(__name__)

_POLL_INTERVAL = 10


async def execute_tier3_via_sweaf(
    tier3_items: list[RemediationItem],
    findings: list[AuditFinding],
    state: ForgeExecutionState,
    cfg: ForgeConfig,
) -> list[CoderFixResult]:
    """Execute findings via SWE-AF's DAG executor."""
    finding_map = {f.id: f for f in findings}

    issues = []
    for item in tier3_items:
        finding = finding_map.get(item.finding_id)
        if not finding:
            continue
        issues.append(finding_to_planned_issue(item, finding))

    if not issues:
        return []

    artifacts_dir = os.path.join(state.repo_path, ".forge-artifacts")
    os.makedirs(artifacts_dir, exist_ok=True)
    write_issue_files(issues, artifacts_dir)
    plan_result = build_plan_result(issues, artifacts_dir)

    # POST to AgentField
    try:
        execution_id = await _post_execution(plan_result, state, cfg)
    except Exception as e:
        logger.error("SWE-AF bridge: POST failed: %s", e)
        return _failed_results(tier3_items, str(e))

    # Poll for completion
    try:
        result = await _poll_execution(execution_id, cfg)
    except Exception as e:
        logger.error("SWE-AF bridge: poll failed: %s", e)
        return _failed_results(tier3_items, str(e))

    # Record SWE-AF cost to FORGE telemetry
    _record_sweaf_cost(result)

    return sweaf_result_to_coder_fix_results(result, finding_map)


async def _post_execution(
    plan_result: dict[str, Any],
    state: ForgeExecutionState,
    cfg: ForgeConfig,
) -> str:
    """POST async execution to AgentField. Returns execution_id."""
    url = f"{cfg.sweaf_agentfield_url}/api/v1/execute/async/{cfg.sweaf_node_id}.execute"

    # SWE-AF needs a git URL to clone, not a local path
    repo_url = cfg.repo_url or ""
    if not repo_url:
        # Try to detect git remote from the local repo
        try:
            import subprocess
            result = subprocess.run(
                ["git", "-C", state.repo_path, "remote", "get-url", "origin"],
                capture_output=True, text=True, timeout=5,
            )
            if result.returncode == 0:
                repo_url = result.stdout.strip()
                # Convert SSH to HTTPS if needed
                if repo_url.startswith("git@github.com:"):
                    repo_url = repo_url.replace("git@github.com:", "https://github.com/")
                if repo_url.endswith(".git"):
                    pass  # keep it
                else:
                    repo_url += ".git"
        except Exception:
            pass

    payload = {
        "input": {
            "plan_result": plan_result,
            "repo_path": f"/workspaces/{repo_url.rstrip('/').split('/')[-1].replace('.git', '')}" if repo_url else state.repo_path,
            "config": {
                "runtime": cfg.sweaf_runtime,
                "max_coding_iterations": cfg.sweaf_max_coding_iterations,
                "max_cost_usd": cfg.sweaf_max_cost_usd,
                "models": {"default": "minimax/minimax-m2.5"},
            },
            "git_config": {
                "repo_url": repo_url,
            },
        },
    }

    headers = {"Content-Type": "application/json"}
    if cfg.sweaf_api_key:
        headers["Authorization"] = f"Bearer {cfg.sweaf_api_key}"

    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.post(url, json=payload, headers=headers)
        resp.raise_for_status()
        body = resp.json()

    execution_id = body.get("execution_id", body.get("id", ""))
    if not execution_id:
        raise ValueError(f"No execution_id in response: {body}")

    logger.info("SWE-AF bridge: started execution %s", execution_id)
    return execution_id


async def _poll_execution(
    execution_id: str,
    cfg: ForgeConfig,
) -> dict[str, Any]:
    """Poll AgentField until execution completes or times out."""
    url = f"{cfg.sweaf_agentfield_url}/api/v1/executions/{execution_id}"

    headers = {}
    if cfg.sweaf_api_key:
        headers["Authorization"] = f"Bearer {cfg.sweaf_api_key}"

    elapsed = 0

    async with httpx.AsyncClient(timeout=30) as client:
        while elapsed < cfg.sweaf_timeout_seconds:
            await asyncio.sleep(_POLL_INTERVAL)
            elapsed += _POLL_INTERVAL

            try:
                resp = await client.get(url, headers=headers)
                resp.raise_for_status()
                body = resp.json()
            except Exception as e:
                logger.warning("SWE-AF bridge: poll error (will retry): %s", e)
                continue

            status = body.get("status", "")
            logger.debug("SWE-AF bridge: %s status=%s", execution_id, status)

            if status in ("completed", "success", "succeeded"):
                return body.get("result", body)
            if status in ("failed", "error", "cancelled"):
                raise RuntimeError(
                    f"SWE-AF execution {execution_id} failed: {body.get('error', status)}"
                )

    raise TimeoutError(
        f"SWE-AF execution {execution_id} timed out after {cfg.sweaf_timeout_seconds}s"
    )


def _record_sweaf_cost(result: Any) -> None:
    """Record SWE-AF cost_summary into FORGE's RunTelemetry."""
    cost_summary = result.get("cost_summary") if isinstance(result, dict) else None
    if not cost_summary:
        return
    try:
        from forge.execution.run_telemetry import _current_run_telemetry

        rt = _current_run_telemetry.get(None)
        if not rt:
            return

        sweaf_cost = cost_summary.get("total_cost_usd", 0)
        sweaf_tokens = cost_summary.get("total_tokens", 0)

        if sweaf_cost > 0:
            import asyncio
            asyncio.get_event_loop().create_task(
                rt.record_invocation(
                    agent_name="sweaf_remediation",
                    model="minimax/minimax-m2.5",
                    input_tokens=sweaf_tokens,
                    output_tokens=0,
                    cost_usd=sweaf_cost,
                )
            )

        logger.info(
            "SWE-AF cost: $%.4f (%d tokens, %d calls)",
            sweaf_cost,
            sweaf_tokens,
            cost_summary.get("total_invocations", 0),
        )
    except Exception:
        pass


def _failed_results(
    items: list[RemediationItem],
    error_msg: str,
) -> list[CoderFixResult]:
    """Generate FAILED_RETRYABLE results for all items."""
    return [
        CoderFixResult(
            finding_id=item.finding_id,
            outcome=FixOutcome.FAILED_RETRYABLE,
            summary=f"SWE-AF bridge error: {error_msg[:200]}",
        )
        for item in items
    ]
