"""Tests for ietf_analyzer.models and ietf_analyzer.config."""

from __future__ import annotations

import json

import pytest

from ietf_analyzer.models import Draft, Rating, normalize_category
from ietf_analyzer.config import Config, DEFAULT_KEYWORDS


# ---- Rating ----


def test_rating_composite_score():
    """Composite score should use weighted average formula."""
    r = Rating(
        draft_name="test", novelty=4, maturity=3, overlap=2,
        momentum=3, relevance=5, summary="test",
    )
    # Expected: 4*0.30 + 5*0.25 + 3*0.20 + 3*0.15 + (6-2)*0.10
    expected = 4 * 0.30 + 5 * 0.25 + 3 * 0.20 + 3 * 0.15 + (6 - 2) * 0.10
    assert abs(r.composite_score - expected) < 0.001


def test_rating_composite_score_all_ones():
    """Composite score with all 1s should be the minimum."""
    r = Rating(
        draft_name="test", novelty=1, maturity=1, overlap=5,
        momentum=1, relevance=1, summary="test",
    )
    expected = 1 * 0.30 + 1 * 0.25 + 1 * 0.20 + 1 * 0.15 + (6 - 5) * 0.10
    assert abs(r.composite_score - expected) < 0.001


def test_rating_composite_score_all_fives():
    """Composite score with all 5s (except overlap=1 for best)."""
    r = Rating(
        draft_name="test", novelty=5, maturity=5, overlap=1,
        momentum=5, relevance=5, summary="test",
    )
    expected = 5 * 0.30 + 5 * 0.25 + 5 * 0.20 + 5 * 0.15 + (6 - 1) * 0.10
    assert abs(r.composite_score - expected) < 0.001
    assert r.composite_score == 5.0


# ---- Draft ----


def test_draft_datatracker_url():
    """datatracker_url should construct the correct URL."""
    d = Draft(name="draft-example-test", rev="00", title="Test", abstract="", time="2025-01-01")
    assert d.datatracker_url == "https://datatracker.ietf.org/doc/draft-example-test/"


def test_draft_text_url():
    """text_url should construct the correct URL with revision."""
    d = Draft(name="draft-example-test", rev="03", title="Test", abstract="", time="2025-01-01")
    assert d.text_url == "https://www.ietf.org/archive/id/draft-example-test-03.txt"


def test_draft_defaults():
    """Draft should have sensible defaults for optional fields."""
    d = Draft(name="draft-minimal", rev="00", title="Min", abstract="", time="2025-01-01")
    assert d.dt_id is None
    assert d.pages is None
    assert d.words is None
    assert d.group is None
    assert d.full_text is None
    assert d.categories == []
    assert d.tags == []
    assert d.states == []
    assert d.source == "ietf"


def test_draft_date_property():
    """Draft.date should return just the date portion of time."""
    d = Draft(name="test", rev="00", title="T", abstract="", time="2025-06-15T12:00:00+00:00")
    assert d.date == "2025-06-15"


def test_draft_date_empty():
    """Draft.date should return empty string if time is None."""
    d = Draft(name="test", rev="00", title="T", abstract="", time=None)
    assert d.date == ""


# ---- normalize_category ----


def test_normalize_category():
    """Known verbose category names should be normalized to short forms."""
    assert normalize_category("Agent-to-agent communication protocols") == "A2A protocols"
    assert normalize_category("AI safety / guardrails / alignment") == "AI safety/alignment"


def test_normalize_category_passthrough():
    """Unknown category names should pass through unchanged."""
    assert normalize_category("A2A protocols") == "A2A protocols"
    assert normalize_category("Some Unknown Category") == "Some Unknown Category"


# ---- Config ----


def test_config_load_defaults():
    """Config without a file should use defaults."""
    cfg = Config()
    assert cfg.ollama_url == "http://localhost:11434"
    assert cfg.claude_model != ""
    assert cfg.fetch_delay == 0.5


def test_config_save_and_load(tmp_path):
    """Config should roundtrip through save/load."""
    cfg = Config(
        data_dir=str(tmp_path),
        db_path=str(tmp_path / "test.db"),
        claude_model="claude-test-model",
    )
    # Save to the default config path (override it)
    config_file = tmp_path / "config.json"
    config_file.write_text(json.dumps({
        "data_dir": str(tmp_path),
        "db_path": str(tmp_path / "test.db"),
        "claude_model": "claude-test-model",
        "ollama_url": "http://localhost:11434",
        "search_keywords": ["agent", "ai-agent"],
    }))

    # Verify roundtrip by reading back
    data = json.loads(config_file.read_text())
    loaded = Config(**{k: v for k, v in data.items() if k in Config.__dataclass_fields__})
    assert loaded.claude_model == "claude-test-model"
    assert loaded.db_path == str(tmp_path / "test.db")


def test_config_search_keywords():
    """Default config should have the expected search keywords."""
    cfg = Config()
    assert "agent" in cfg.search_keywords
    assert "mcp" in cfg.search_keywords
    assert "agentic" in cfg.search_keywords
    assert len(cfg.search_keywords) == len(DEFAULT_KEYWORDS)


def _patch_config_file(monkeypatch, tmp_path):
    """Point CONFIG_FILE to a non-existent path so tests use defaults."""
    import ietf_analyzer.config as config_mod
    monkeypatch.setattr(config_mod, "CONFIG_FILE", tmp_path / "config.json")


def test_config_env_var_override(tmp_path, monkeypatch):
    """Environment variables should override config file values."""
    _patch_config_file(monkeypatch, tmp_path)
    monkeypatch.setenv("IETF_ANALYZER_DB_PATH", str(tmp_path / "env.db"))
    monkeypatch.setenv("IETF_ANALYZER_CLAUDE_MODEL", "claude-from-env")
    monkeypatch.setenv("IETF_ANALYZER_OLLAMA_URL", "http://remote:11434")

    cfg = Config.load()
    assert cfg.db_path == str(tmp_path / "env.db")
    assert cfg.claude_model == "claude-from-env"
    assert cfg.ollama_url == "http://remote:11434"


def test_config_validation_bad_model(tmp_path, monkeypatch):
    """Empty claude_model should raise ValueError."""
    _patch_config_file(monkeypatch, tmp_path)
    monkeypatch.setenv("IETF_ANALYZER_CLAUDE_MODEL", "")
    with pytest.raises(ValueError, match="claude_model"):
        Config.load()


def test_config_validation_bad_url(tmp_path, monkeypatch):
    """Non-URL ollama_url should raise ValueError."""
    _patch_config_file(monkeypatch, tmp_path)
    monkeypatch.setenv("IETF_ANALYZER_OLLAMA_URL", "not-a-url")
    with pytest.raises(ValueError, match="ollama_url"):
        Config.load()


def test_config_validation_bad_db_path(tmp_path, monkeypatch):
    """db_path with non-existent parent directory should raise ValueError."""
    _patch_config_file(monkeypatch, tmp_path)
    monkeypatch.setenv("IETF_ANALYZER_DB_PATH", "/nonexistent/dir/test.db")
    with pytest.raises(ValueError, match="db_path"):
        Config.load()
