"""Tests for TelemetryEmitter — constitutional LLM telemetry SDK."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from abi_control_core.sdk.telemetry_emitter import (
    VALID_PROVIDERS,
    VALID_ROLES,
    VALID_STATUSES,
    TelemetryEmitter,
)


@pytest.fixture
def emitter(tmp_path: Path) -> TelemetryEmitter:
    return TelemetryEmitter(output_dir=tmp_path, run_id="test-run-001")


def _emit_default(emitter: TelemetryEmitter, **overrides) -> dict:
    defaults = dict(
        provider="anthropic",
        model="claude-opus-4-6",
        role="builder",
        duration_ms=1000,
    )
    defaults.update(overrides)
    return emitter.emit(**defaults)


class TestEmit:
    def test_basic_emit_returns_record(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter)
        assert record["provider"] == "anthropic"
        assert record["model"] == "claude-opus-4-6"
        assert record["role"] == "builder"
        assert record["duration_ms"] == 1000
        assert record["status"] == "success"
        assert record["run_id"] == "test-run-001"
        assert "invocation_id" in record
        assert "timestamp" in record

    def test_emit_writes_ndjson(self, emitter: TelemetryEmitter):
        _emit_default(emitter)
        _emit_default(emitter, role="verifier")

        ndjson = emitter._file.read_text(encoding="utf-8").strip().split("\n")
        assert len(ndjson) == 2
        r1 = json.loads(ndjson[0])
        r2 = json.loads(ndjson[1])
        assert r1["role"] == "builder"
        assert r2["role"] == "verifier"

    def test_emit_with_tokens(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter, tokens_in=100, tokens_out=50)
        assert record["tokens_in"] == 100
        assert record["tokens_out"] == 50
        assert record["total_tokens"] == 150

    def test_emit_partial_tokens(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter, tokens_in=100)
        assert record["total_tokens"] == 100

    def test_emit_no_tokens(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter)
        assert record["total_tokens"] is None

    def test_emit_with_cost(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter, cost_usd=0.05)
        assert record["cost_usd"] == 0.05

    def test_emit_with_error(self, emitter: TelemetryEmitter):
        record = _emit_default(emitter, status="failure", error_class="RateLimitError")
        assert record["status"] == "failure"
        assert record["error_class"] == "RateLimitError"

    def test_emit_with_task_metadata(self, emitter: TelemetryEmitter):
        record = _emit_default(
            emitter, task_id="IGF-001", task_category="infra", repo="abi-orchestrator"
        )
        assert record["task_id"] == "IGF-001"
        assert record["task_category"] == "infra"
        assert record["repo"] == "abi-orchestrator"


class TestValidation:
    def test_invalid_role_raises(self, emitter: TelemetryEmitter):
        with pytest.raises(ValueError, match="Invalid role"):
            _emit_default(emitter, role="hacker")

    def test_invalid_provider_raises(self, emitter: TelemetryEmitter):
        with pytest.raises(ValueError, match="Invalid provider"):
            _emit_default(emitter, provider="skynet")

    def test_invalid_status_raises(self, emitter: TelemetryEmitter):
        with pytest.raises(ValueError, match="Invalid status"):
            _emit_default(emitter, status="maybe")

    def test_all_valid_roles(self, emitter: TelemetryEmitter):
        for role in VALID_ROLES:
            record = _emit_default(emitter, role=role)
            assert record["role"] == role

    def test_all_valid_providers(self, emitter: TelemetryEmitter):
        for provider in VALID_PROVIDERS:
            record = _emit_default(emitter, provider=provider)
            assert record["provider"] == provider

    def test_all_valid_statuses(self, emitter: TelemetryEmitter):
        for status in VALID_STATUSES:
            record = _emit_default(emitter, status=status)
            assert record["status"] == status


class TestSummary:
    def test_summary_empty(self, emitter: TelemetryEmitter):
        s = emitter.summary()
        assert s["total_calls"] == 0
        assert s["total_tokens"] == 0
        assert s["success_rate"] == 0.0

    def test_summary_aggregates(self, emitter: TelemetryEmitter):
        _emit_default(emitter, tokens_in=100, tokens_out=50, cost_usd=0.01)
        _emit_default(emitter, role="verifier", tokens_in=200, tokens_out=100, cost_usd=0.02)
        _emit_default(emitter, status="failure", duration_ms=500)

        s = emitter.summary()
        assert s["total_calls"] == 3
        assert s["total_tokens"] == 450
        assert s["total_cost_usd"] == 0.03
        assert s["total_duration_ms"] == 2500
        assert s["success_rate"] == pytest.approx(2 / 3)

    def test_summary_by_role(self, emitter: TelemetryEmitter):
        _emit_default(emitter, tokens_in=100, tokens_out=50)
        _emit_default(emitter, role="verifier", tokens_in=200, tokens_out=100)

        s = emitter.summary()
        assert "builder" in s["by_role"]
        assert "verifier" in s["by_role"]
        assert s["by_role"]["builder"]["calls"] == 1
        assert s["by_role"]["verifier"]["tokens"] == 300

    def test_summary_line(self, emitter: TelemetryEmitter):
        _emit_default(emitter, tokens_in=1000, tokens_out=500, cost_usd=0.10)
        line = emitter.summary_line()
        assert "1 calls" in line
        assert "1,500 tokens" in line
        assert "$0.10" in line
        assert "100% success" in line

    def test_summary_line_no_data(self, emitter: TelemetryEmitter):
        _emit_default(emitter)
        line = emitter.summary_line()
        assert "N/A" in line  # no tokens or cost


class TestFileHandling:
    def test_creates_output_dir(self, tmp_path: Path):
        out = tmp_path / "nested" / "deep"
        emitter = TelemetryEmitter(output_dir=out, run_id="test")
        assert out.exists()
        _emit_default(emitter)
        assert emitter._file.exists()

    def test_appends_to_existing_file(self, emitter: TelemetryEmitter):
        _emit_default(emitter)
        _emit_default(emitter)
        _emit_default(emitter)
        lines = emitter._file.read_text(encoding="utf-8").strip().split("\n")
        assert len(lines) == 3

    def test_records_tracked_in_memory(self, emitter: TelemetryEmitter):
        _emit_default(emitter)
        _emit_default(emitter)
        assert len(emitter._records) == 2
