"""Tests for the Obsidian vault export.

If this test breaks, the export is out of sync with the data model.
Fix obsidian_export.py to match whatever changed.
"""

from __future__ import annotations

import io
import sys
import zipfile
from pathlib import Path


_project_root = Path(__file__).resolve().parent.parent
if str(_project_root / "src") not in sys.path:
    sys.path.insert(0, str(_project_root / "src"))

from webui.obsidian_export import build_obsidian_vault


def test_vault_structure(seeded_db):
    """Vault ZIP should contain expected folders and key files."""
    data = build_obsidian_vault(seeded_db)
    assert len(data) > 0

    z = zipfile.ZipFile(io.BytesIO(data))
    names = z.namelist()

    # Key structural files must exist
    assert "IETF-AI-Agent-Drafts/Dashboard.md" in names
    assert "IETF-AI-Agent-Drafts/Authors/index.md" in names
    assert "IETF-AI-Agent-Drafts/Categories/index.md" in names
    assert "IETF-AI-Agent-Drafts/.obsidian/graph.json" in names

    # Should have analysis notes
    analysis = [n for n in names if "/Analysis/" in n]
    assert len(analysis) >= 3  # Score Distribution, Top Rated, Ideas Overview


def test_vault_has_all_drafts(seeded_db):
    """Every draft in the DB should have a corresponding note in the vault."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))
    draft_files = [n for n in z.namelist() if "/Drafts/" in n]

    # seeded_db has 5 drafts
    assert len(draft_files) == 5

    # Check each draft name appears
    draft_names = {Path(f).stem for f in draft_files}
    assert "draft-alpha-agent-comm" in draft_names
    assert "draft-gamma-agent-id" in draft_names


def test_draft_note_has_frontmatter(seeded_db):
    """Draft notes must have YAML frontmatter with score and categories."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Drafts/draft-alpha-agent-comm.md").decode()

    # YAML frontmatter
    assert content.startswith("---")
    assert "score:" in content
    assert "novelty:" in content
    assert "maturity:" in content
    assert "categories:" in content
    assert "tags:" in content

    # No floating-point noise (e.g., 3.4000000000000004)
    import re
    long_floats = re.findall(r"\d+\.\d{4,}", content)
    assert len(long_floats) == 0, f"Unformatted floats found: {long_floats}"


def test_draft_note_has_wikilinks(seeded_db):
    """Draft notes should link to authors and categories with [[wikilinks]]."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Drafts/draft-alpha-agent-comm.md").decode()

    # Should link to authors
    assert "[[Alice Researcher]]" in content
    assert "[[Bob Engineer]]" in content

    # Should link to categories
    assert "[[A2A protocols]]" in content


def test_draft_note_has_ideas(seeded_db):
    """Draft notes should include extracted ideas."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Drafts/draft-alpha-agent-comm.md").decode()

    assert "Extracted Ideas" in content
    assert "Agent Handshake" in content
    assert "Capability Negotiation" in content


def test_draft_note_has_rating_bars(seeded_db):
    """Draft notes should include visual score bars."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Drafts/draft-alpha-agent-comm.md").decode()

    # Score bars use block chars
    assert "\u2588" in content  # filled block
    assert "\u2591" in content  # empty block
    assert "/5.0" in content


def test_author_notes(seeded_db):
    """Author notes should list their drafts with wikilinks."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Authors/Alice Researcher.md").decode()

    assert content.startswith("---")
    assert "affiliation:" in content
    assert "ExampleCorp" in content
    assert "[[draft-alpha-agent-comm" in content
    assert "[[draft-gamma-agent-id" in content


def test_category_notes(seeded_db):
    """Category notes should list drafts with scores."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))
    cat_files = [n for n in z.namelist() if "/Categories/" in n and "index" not in n]

    # seeded_db has 5 distinct categories
    assert len(cat_files) >= 4

    # Check one category note
    content = z.read("IETF-AI-Agent-Drafts/Categories/A2A protocols.md").decode()
    assert "[[draft-alpha-agent-comm" in content
    assert "draft_count:" in content


def test_dashboard_has_mermaid(seeded_db):
    """Dashboard should contain Mermaid chart blocks."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Dashboard.md").decode()

    assert "```mermaid" in content
    assert "pie title" in content
    assert "Key Stats" in content
    assert "Total Drafts" in content


def test_vault_has_glossary(seeded_db):
    """Vault should contain a Glossary with scoring dimensions explained."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    assert "IETF-AI-Agent-Drafts/Analysis/Glossary.md" in z.namelist()
    content = z.read("IETF-AI-Agent-Drafts/Analysis/Glossary.md").decode()

    # All five dimensions must be explained
    for dim in ("Novelty", "Maturity", "Overlap", "Momentum", "Relevance"):
        assert dim in content, f"Glossary missing dimension: {dim}"

    assert "Composite Score" in content
    assert "Internet-Draft" in content


def test_top_rated_uses_full_names(seeded_db):
    """Top Rated table should use full dimension names, not abbreviations."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    content = z.read("IETF-AI-Agent-Drafts/Analysis/Top Rated.md").decode()

    assert "Novelty" in content
    assert "Maturity" in content
    assert "| Nov |" not in content  # no abbreviations


def test_vault_is_valid_zip(seeded_db):
    """The output should be a valid ZIP that can be extracted."""
    data = build_obsidian_vault(seeded_db)
    z = zipfile.ZipFile(io.BytesIO(data))

    # Should not raise
    bad = z.testzip()
    assert bad is None, f"Corrupt file in ZIP: {bad}"

    # All files should be decodable as UTF-8
    for name in z.namelist():
        if name.endswith(".md"):
            z.read(name).decode("utf-8")
