"""Tests for enriched audit trail + audit CLI commands."""

import json
import time

import pytest
from typer.testing import CliRunner

from fliiq.cli.audit import audit_app
from fliiq.runtime.agent.audit import AgentAuditTrail, extract_audit_trail, save_audit_trail

runner = CliRunner()


# --- Enriched audit model tests ---


def test_extract_with_model_and_tokens():
    """New fields (model, provider, tokens) are captured."""
    messages = [
        {"role": "user", "content": "hello"},
        {"role": "assistant", "content": "hi"},
    ]
    trail = extract_audit_trail(
        "hello",
        messages,
        stop_reason="end_turn",
        model="claude-sonnet-4-6",
        provider="anthropic",
        input_tokens=500,
        output_tokens=200,
    )
    assert trail.model == "claude-sonnet-4-6"
    assert trail.provider == "anthropic"
    assert trail.input_tokens == 500
    assert trail.output_tokens == 200


def test_extract_full_tool_inputs():
    """Tool calls now include full input dict, not just keys."""
    messages = [
        {"role": "user", "content": "check weather"},
        {
            "role": "assistant",
            "content": [
                {"type": "tool_use", "id": "tc_1", "name": "weather", "input": {"city": "London", "units": "metric"}},
            ],
        },
    ]
    trail = extract_audit_trail("check weather", messages)
    tc = trail.entries[1].tool_calls[0]
    assert tc["input"] == {"city": "London", "units": "metric"}
    assert tc["input_keys"] == ["city", "units"]


def test_extract_iteration_timestamps():
    """Per-iteration timestamps produce duration_ms on assistant entries."""
    messages = [
        {"role": "user", "content": "go"},
        {"role": "assistant", "content": "thinking..."},
        {"role": "user", "content": "ok"},
        {"role": "assistant", "content": "done"},
    ]
    now = time.time()
    timestamps = [now, now + 2.5, now + 5.0]  # iter1 start, iter2 start, end marker

    trail = extract_audit_trail(
        "go", messages, iteration_timestamps=timestamps,
    )

    # First assistant entry (index 1) should have duration
    assert trail.entries[1].duration_ms == pytest.approx(2500.0, abs=1)
    # Second assistant entry (index 3) — no duration (last iteration, end marker used)
    # iter2 start=now+2.5, end=now+5.0 → 2500ms
    assert trail.entries[3].duration_ms == pytest.approx(2500.0, abs=1)


def test_extract_no_timestamps_graceful():
    """Without timestamps, duration_ms stays None (backward compat)."""
    messages = [
        {"role": "user", "content": "hi"},
        {"role": "assistant", "content": "hello"},
    ]
    trail = extract_audit_trail("hi", messages)
    assert trail.entries[1].duration_ms is None


def test_save_enriched_trail(tmp_path):
    """Enriched fields survive save/load round-trip."""
    trail = AgentAuditTrail(
        prompt="test",
        model="gpt-4o",
        provider="openai",
        input_tokens=1000,
        output_tokens=500,
    )
    path = save_audit_trail(trail, audit_dir=tmp_path)
    data = json.loads(path.read_text())
    assert data["model"] == "gpt-4o"
    assert data["provider"] == "openai"
    assert data["input_tokens"] == 1000
    assert data["output_tokens"] == 500


def test_old_audit_file_loads_gracefully(tmp_path):
    """Old audit files without new fields load with defaults."""
    old_data = {
        "id": "abc123def456",
        "prompt": "old prompt",
        "entries": [],
        "final_text": "done",
        "total_iterations": 1,
        "stop_reason": "end_turn",
        "started_at": "2026-01-01T00:00:00Z",
        "completed_at": "2026-01-01T00:01:00Z",
        "warnings": [],
    }
    path = tmp_path / "20260101_000000_abc123def456.json"
    path.write_text(json.dumps(old_data))

    trail = AgentAuditTrail.model_validate(json.loads(path.read_text()))
    assert trail.model == ""
    assert trail.provider == ""
    assert trail.input_tokens == 0
    assert trail.output_tokens == 0


# --- CLI tests ---


def _create_audit_file(audit_dir, trail_id="abc123def456", **overrides):
    """Helper to create a test audit file."""
    data = {
        "id": trail_id,
        "prompt": overrides.get("prompt", "test prompt"),
        "entries": overrides.get("entries", []),
        "final_text": overrides.get("final_text", "test response"),
        "total_iterations": overrides.get("total_iterations", 2),
        "stop_reason": overrides.get("stop_reason", "end_turn"),
        "started_at": "2026-03-05T10:00:00Z",
        "completed_at": "2026-03-05T10:01:00Z",
        "warnings": overrides.get("warnings", []),
        "model": overrides.get("model", "claude-sonnet-4-6"),
        "provider": overrides.get("provider", "anthropic"),
        "input_tokens": overrides.get("input_tokens", 1200),
        "output_tokens": overrides.get("output_tokens", 400),
    }
    path = audit_dir / f"20260305_100000_{trail_id}.json"
    path.write_text(json.dumps(data))
    return path


def test_audit_list(tmp_path, monkeypatch):
    """fliiq audit list shows audit trails in table."""
    audit_dir = tmp_path / "audit"
    audit_dir.mkdir()
    _create_audit_file(audit_dir, prompt="What is 2+2?")
    _create_audit_file(audit_dir, trail_id="def789ghi012", prompt="Send an email")

    monkeypatch.setattr("fliiq.cli.audit._audit_dir", lambda: audit_dir)

    result = runner.invoke(audit_app, ["list"])
    assert result.exit_code == 0
    assert "abc123def456" in result.output
    assert "def789ghi012" in result.output
    assert "What is 2+2?" in result.output


def test_audit_list_empty(tmp_path, monkeypatch):
    """fliiq audit list with no files shows message."""
    audit_dir = tmp_path / "audit"
    audit_dir.mkdir()
    monkeypatch.setattr("fliiq.cli.audit._audit_dir", lambda: audit_dir)

    result = runner.invoke(audit_app, ["list"])
    assert result.exit_code == 0
    assert "No audit trails found" in result.output


def test_audit_show(tmp_path, monkeypatch):
    """fliiq audit show displays full audit detail."""
    audit_dir = tmp_path / "audit"
    audit_dir.mkdir()
    _create_audit_file(
        audit_dir,
        prompt="What time is it?",
        model="claude-sonnet-4-6",
        input_tokens=800,
        output_tokens=200,
    )
    monkeypatch.setattr("fliiq.cli.audit._audit_dir", lambda: audit_dir)

    result = runner.invoke(audit_app, ["show", "abc123def456"])
    assert result.exit_code == 0
    assert "What time is it?" in result.output
    assert "claude-sonnet-4-6" in result.output
    assert "800 in / 200 out" in result.output


def test_audit_show_json(tmp_path, monkeypatch):
    """fliiq audit show --json outputs raw JSON."""
    audit_dir = tmp_path / "audit"
    audit_dir.mkdir()
    _create_audit_file(audit_dir)
    monkeypatch.setattr("fliiq.cli.audit._audit_dir", lambda: audit_dir)

    result = runner.invoke(audit_app, ["show", "abc123def456", "--json"])
    assert result.exit_code == 0
    data = json.loads(result.output)
    assert data["id"] == "abc123def456"


def test_audit_show_not_found(tmp_path, monkeypatch):
    """fliiq audit show with bad ID shows error."""
    audit_dir = tmp_path / "audit"
    audit_dir.mkdir()
    monkeypatch.setattr("fliiq.cli.audit._audit_dir", lambda: audit_dir)

    result = runner.invoke(audit_app, ["show", "nonexistent"])
    assert result.exit_code == 1
    assert "No audit trail matching" in result.output
