#!/usr/bin/env python3
"""
Unit tests for feedback_vault.py

Work Order: #12
"""

import json
import os
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from helix_cdc.feedback_vault import (
    log_feedback,
    read_feedback,
    get_feedback_stats,
    clear_feedback,
    FeedbackSession,
    FEEDBACK_LOG,
)


class TestFeedbackVault:
    """Test suite for feedback vault."""

    def setup_method(self):
        """Use temp file for tests."""
        self.temp_dir = tempfile.mkdtemp()
        self.temp_log = Path(self.temp_dir) / "test_feedback.jsonl"
        os.environ["HELIX_FEEDBACK_LOG"] = str(self.temp_log)

    def teardown_method(self):
        """Clean up temp files."""
        if self.temp_log.exists():
            self.temp_log.unlink()
        if "HELIX_FEEDBACK_LOG" in os.environ:
            del os.environ["HELIX_FEEDBACK_LOG"]

    def test_log_and_read_single_entry(self):
        """Log one entry and read it back."""
        log_feedback("What is 2+2?", "4", score=1)

        entries = read_feedback()
        assert len(entries) == 1
        assert entries[0]["query"] == "What is 2+2?"
        assert entries[0]["response"] == "4"
        assert entries[0]["score"] == 1
        assert "ts" in entries[0]

    def test_log_multiple_entries(self):
        """Log multiple entries and read them back."""
        log_feedback("Q1", "R1", score=1)
        log_feedback("Q2", "R2", score=-1)
        log_feedback("Q3", "R3", score=0)

        entries = read_feedback()
        assert len(entries) == 3
        assert entries[0]["score"] == 1
        assert entries[1]["score"] == -1
        assert entries[2]["score"] == 0

    def test_metadata_storage(self):
        """Metadata should be stored correctly."""
        log_feedback("Q", "R", score=1, meta={"session_id": "abc123", "emotion": "neutral"})

        entries = read_feedback()
        assert entries[0]["meta"]["session_id"] == "abc123"
        assert entries[0]["meta"]["emotion"] == "neutral"

    def test_truncation(self):
        """Long queries/responses should be truncated to 200 chars."""
        long_text = "x" * 500
        log_feedback(long_text, long_text, score=1)

        entries = read_feedback()
        assert len(entries[0]["query"]) == 200
        assert len(entries[0]["response"]) == 200

    def test_stats_empty(self):
        """Stats on empty vault."""
        stats = get_feedback_stats()
        assert stats["total"] == 0
        assert stats["avg_score"] == 0.0

    def test_stats_with_data(self):
        """Stats calculation."""
        log_feedback("Q1", "R1", score=1)
        log_feedback("Q2", "R2", score=1)
        log_feedback("Q3", "R3", score=-1)
        log_feedback("Q4", "R4", score=0)

        stats = get_feedback_stats()
        assert stats["total"] == 4
        assert stats["positive"] == 2
        assert stats["negative"] == 1
        assert stats["neutral"] == 1
        assert stats["avg_score"] == 0.25  # (1+1-1+0)/4 = 0.25

    def test_clear_feedback(self):
        """Clear should remove all entries."""
        log_feedback("Q", "R", score=1)
        assert len(read_feedback()) == 1

        clear_feedback()
        assert len(read_feedback()) == 0

    def test_read_with_limit(self):
        """Read with limit."""
        for i in range(10):
            log_feedback(f"Q{i}", f"R{i}", score=1)

        entries = read_feedback(limit=5)
        assert len(entries) == 5

    def test_read_with_offset(self):
        """Read with offset."""
        for i in range(10):
            log_feedback(f"Q{i}", f"R{i}", score=i)

        entries = read_feedback(limit=3, offset=5)
        assert len(entries) == 3
        assert entries[0]["score"] == 5  # Skipped 0-4


class TestFeedbackSession:
    """Test session-based feedback."""

    def setup_method(self):
        """Use temp file for tests."""
        self.temp_dir = tempfile.mkdtemp()
        self.temp_log = Path(self.temp_dir) / "test_feedback.jsonl"
        os.environ["HELIX_FEEDBACK_LOG"] = str(self.temp_log)

    def teardown_method(self):
        """Clean up."""
        if self.temp_log.exists():
            self.temp_log.unlink()
        if "HELIX_FEEDBACK_LOG" in os.environ:
            del os.environ["HELIX_FEEDBACK_LOG"]

    def test_session_log(self):
        """Session should log with session_id in meta."""
        session = FeedbackSession("test-session-123")
        session.log("Q", "R", score=1)

        entries = read_feedback()
        assert len(entries) == 1
        assert entries[0]["meta"]["session_id"] == "test-session-123"

    def test_session_stats(self):
        """Session stats should reflect only session entries."""
        session = FeedbackSession("sess-1")
        session.log("Q1", "R1", score=1)
        session.log("Q2", "R2", score=-1)

        stats = session.stats()
        assert stats["session_id"] == "sess-1"
        assert stats["count"] == 2
        assert stats["positive"] == 1
        assert stats["negative"] == 1
        assert stats["avg_score"] == 0.0


def run_tests():
    """Run all tests and print results."""
    import traceback

    test_classes = [TestFeedbackVault, TestFeedbackSession]
    passed = 0
    failed = 0

    for test_class in test_classes:
        instance = test_class()
        for method_name in dir(instance):
            if method_name.startswith("test_"):
                # Setup
                if hasattr(instance, "setup_method"):
                    instance.setup_method()

                try:
                    getattr(instance, method_name)()
                    print(f"✅ {test_class.__name__}.{method_name}")
                    passed += 1
                except AssertionError as e:
                    print(f"❌ {test_class.__name__}.{method_name}: {e}")
                    failed += 1
                except Exception as e:
                    print(f"❌ {test_class.__name__}.{method_name}: {e}")
                    traceback.print_exc()
                    failed += 1
                finally:
                    # Teardown
                    if hasattr(instance, "teardown_method"):
                        instance.teardown_method()

    print(f"\n{'='*40}")
    print(f"Results: {passed} passed, {failed} failed")
    return failed == 0


if __name__ == "__main__":
    success = run_tests()
    sys.exit(0 if success else 1)
