"""Tests for reasoning chain module."""

from __future__ import annotations

import json

import pytest

from hieromem_core.reasoning.chain import ReasoningChain
from hieromem_core.symbolic import ScoredPath


class TestReasoningChain:
    """Tests for ReasoningChain dataclass."""

    @pytest.fixture
    def scored_path(self) -> ScoredPath:
        """Create a sample scored path."""
        return ScoredPath(
            path=["Alice", "knows", "Bob", "works_with", "Carol"],
            score=0.85,
            trust_score=0.9,
            semantic_score=0.8,
            length_penalty=0.95,
            explanation="Alice knows Bob who works with Carol",
        )

    def test_create_chain(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain(
            chain_id="chain-1",
            scored_path=scored_path,
            query_context="Who does Alice know?",
        )

        assert chain.chain_id == "chain-1"
        assert chain.scored_path == scored_path
        assert chain.query_context == "Who does Alice know?"
        assert chain.source_episodes == []

    def test_create_chain_with_sources(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain(
            chain_id="chain-1",
            scored_path=scored_path,
            query_context="test",
            source_episodes=["ep-1", "ep-2"],
        )

        assert chain.source_episodes == ["ep-1", "ep-2"]

    def test_from_path(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(
            scored_path=scored_path,
            query_text="Find connection",
            source_episodes=["ep-1"],
        )

        assert chain.scored_path == scored_path
        assert chain.query_context == "Find connection"
        assert chain.source_episodes == ["ep-1"]
        # Chain ID should be generated
        assert chain.chain_id is not None
        assert len(chain.chain_id) > 0

    def test_from_path_no_sources(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(
            scored_path=scored_path,
            query_text="test",
        )

        assert chain.source_episodes == []

    def test_explain_text_format(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(scored_path, "Who knows who?")
        explanation = chain.explain("text")

        assert "Reasoning Chain Explanation" in explanation
        assert "Query: Who knows who?" in explanation
        assert "Path:" in explanation
        assert "Trust Contribution:" in explanation
        assert "Semantic Contribution:" in explanation
        assert "Final Score: 0.85" in explanation

    def test_explain_mermaid_format(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(scored_path, "test")
        explanation = chain.explain("mermaid")

        assert "graph LR" in explanation
        assert "-->" in explanation
        assert "style" in explanation

    def test_explain_json_format(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(scored_path, "test query")
        explanation = chain.explain("json")

        data = json.loads(explanation)
        assert data["query"] == "test query"
        assert data["path"] == scored_path.path
        assert data["score"] == 0.85

    def test_explain_unknown_format_returns_text(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(scored_path, "test")
        explanation = chain.explain("unknown")

        assert "Reasoning Chain Explanation" in explanation

    def test_to_text(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(scored_path, "test")
        text = chain.to_text()

        assert text == chain.explain("text")

    def test_to_episode(self, scored_path: ScoredPath) -> None:
        chain = ReasoningChain.from_path(
            scored_path,
            "Who knows who?",
            source_episodes=["ep-1", "ep-2"],
        )

        episode = chain.to_episode()

        assert episode.id == chain.chain_id
        assert episode.meta["type"] == "reasoning_chain"
        assert episode.meta["episode_type"] == "derived"
        assert episode.meta["query"] == "Who knows who?"
        assert episode.meta["score"] == 0.85
        assert episode.meta["path"] == scored_path.path
        assert episode.meta["source_episodes"] == ["ep-1", "ep-2"]


class TestScoredPathIntegration:
    """Tests for ScoredPath integration with ReasoningChain."""

    def test_scored_path_to_dict(self) -> None:
        """Test that ScoredPath.to_dict is called correctly."""
        path = ScoredPath(
            path=["A", "B", "C"],
            score=0.5,
            trust_score=0.8,
            semantic_score=0.7,
            length_penalty=0.9,
            explanation="test",
        )

        # to_dict should work
        d = path.to_dict()
        assert "path" in d
        assert d["score"] == 0.5
        assert d["components"]["trust"] == 0.8
        assert d["components"]["semantic"] == 0.7
        assert d["components"]["penalty"] == 0.9
