"""
Comprehensive tests for ChatContinue functionality.

Tests are written based on the public interface, not implementation details.
Tests challenge the business logic and verify correct behavior.
"""

from unittest.mock import Mock, patch

import pytest

from lexilux import Chat, ChatContinue, ChatHistory, ChatResult
from lexilux.usage import Usage


class TestChatContinueContinueRequest:
    """Test ChatContinue.continue_request method"""

    @patch("lexilux.chat.client.requests.post")
    def test_continue_request_with_prompt(self, mock_post):
        """Test continue_request with add_continue_prompt=True"""
        chat = Chat(
            base_url="https://api.example.com/v1",
            api_key="test-key",
            model="gpt-4",
        )

        result1 = ChatResult(
            text="This is part 1",
            usage=Usage(input_tokens=10, output_tokens=50, total_tokens=60),
            finish_reason="length",
        )

        history = ChatHistory.from_messages("Write a story")
        history.append_result(result1)

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "choices": [{"message": {"content": " and part 2"}, "finish_reason": "stop"}],
            "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
        }
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        continue_result = ChatContinue.continue_request(
            chat, result1, history=history, add_continue_prompt=True, continue_prompt="continue"
        )

        # New API with auto_merge=True (default) returns merged result
        assert continue_result.text == "This is part 1 and part 2"
        assert " and part 2" in continue_result.text
        # Verify that history was updated with continue prompt
        # The history should have the continue prompt as a user message
        assert len(history.messages) >= 2  # Original + continue prompt

    @patch("lexilux.chat.client.requests.post")
    def test_continue_request_without_prompt(self, mock_post):
        """Test continue_request with add_continue_prompt=False"""
        chat = Chat(
            base_url="https://api.example.com/v1",
            api_key="test-key",
            model="gpt-4",
        )

        result1 = ChatResult(
            text="This is part 1",
            usage=Usage(input_tokens=10, output_tokens=50, total_tokens=60),
            finish_reason="length",
        )

        history = ChatHistory.from_messages("Write a story")
        history.append_result(result1)

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "choices": [{"message": {"content": " and part 2"}, "finish_reason": "stop"}],
            "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
        }
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        continue_result = ChatContinue.continue_request(
            chat, result1, history=history, add_continue_prompt=False
        )

        # New API with auto_merge=True (default) returns merged result
        assert continue_result.text == "This is part 1 and part 2"
        assert " and part 2" in continue_result.text
        # History should not have additional user message when add_continue_prompt=False
        # Note: This tests the interface contract

    @patch("lexilux.chat.client.requests.post")
    def test_continue_request_custom_prompt(self, mock_post):
        """Test continue_request with custom continue_prompt"""
        chat = Chat(
            base_url="https://api.example.com/v1",
            api_key="test-key",
            model="gpt-4",
        )

        result1 = ChatResult(
            text="This is part 1",
            usage=Usage(input_tokens=10, output_tokens=50, total_tokens=60),
            finish_reason="length",
        )

        history = ChatHistory.from_messages("Write a story")
        history.append_result(result1)

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "choices": [{"message": {"content": " continuation"}, "finish_reason": "stop"}],
            "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
        }
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        continue_result = ChatContinue.continue_request(
            chat,
            result1,
            history=history,
            add_continue_prompt=True,
            continue_prompt="Please continue",
        )

        # New API with auto_merge=True (default) returns merged result
        assert continue_result.text == "This is part 1 continuation"
        assert " continuation" in continue_result.text
        # Verify custom prompt was used
        # The last user message before assistant should be the custom prompt
        user_messages = [msg for msg in history.messages if msg.get("role") == "user"]
        assert any("Please continue" in msg.get("content", "") for msg in user_messages)


class TestChatContinueMergeResults:
    """Test ChatContinue.merge_results method"""

    def test_merge_results_two_results(self):
        """Test merging two results"""
        result1 = ChatResult(
            text="Part 1",
            usage=Usage(input_tokens=10, output_tokens=20, total_tokens=30),
            finish_reason="length",
        )

        result2 = ChatResult(
            text=" Part 2",
            usage=Usage(input_tokens=15, output_tokens=25, total_tokens=40),
            finish_reason="stop",
        )

        merged = ChatContinue.merge_results(result1, result2)

        assert merged.text == "Part 1 Part 2"
        assert merged.usage.total_tokens == 70  # 30 + 40
        assert merged.usage.input_tokens == 25  # 10 + 15
        assert merged.usage.output_tokens == 45  # 20 + 25
        assert merged.finish_reason == "stop"  # Should use last result's finish_reason

    def test_merge_results_single_result(self):
        """Test merging single result (edge case)"""
        result1 = ChatResult(
            text="Part 1",
            usage=Usage(input_tokens=10, output_tokens=20, total_tokens=30),
            finish_reason="stop",
        )

        merged = ChatContinue.merge_results(result1)

        assert merged.text == "Part 1"
        assert merged.usage.total_tokens == 30
        assert merged.finish_reason == "stop"

    def test_merge_results_multiple_results(self):
        """Test merging multiple results"""
        results = [
            ChatResult(
                text=f"Part {i}",
                usage=Usage(input_tokens=10, output_tokens=20, total_tokens=30),
                finish_reason="length" if i < 3 else "stop",
            )
            for i in range(1, 4)
        ]

        merged = ChatContinue.merge_results(*results)

        assert merged.text == "Part 1Part 2Part 3"
        assert merged.usage.total_tokens == 90  # 30 * 3
        assert merged.finish_reason == "stop"  # Last result's finish_reason

    def test_merge_results_with_none_usage(self):
        """Test merging results with None usage values"""
        result1 = ChatResult(
            text="Part 1",
            usage=Usage(input_tokens=None, output_tokens=20, total_tokens=30),
            finish_reason="length",
        )

        result2 = ChatResult(
            text=" Part 2",
            usage=Usage(input_tokens=15, output_tokens=None, total_tokens=40),
            finish_reason="stop",
        )

        merged = ChatContinue.merge_results(result1, result2)

        assert merged.text == "Part 1 Part 2"
        # None values should be handled gracefully
        assert merged.usage.input_tokens == 15  # Only result2 has input_tokens
        assert merged.usage.output_tokens == 20  # Only result1 has output_tokens
        assert merged.usage.total_tokens == 70  # Both have total_tokens

    def test_merge_results_empty_text(self):
        """Test merging results with empty text"""
        result1 = ChatResult(
            text="",
            usage=Usage(input_tokens=10, output_tokens=0, total_tokens=10),
            finish_reason="length",
        )

        result2 = ChatResult(
            text="Part 2",
            usage=Usage(input_tokens=15, output_tokens=25, total_tokens=40),
            finish_reason="stop",
        )

        merged = ChatContinue.merge_results(result1, result2)

        assert merged.text == "Part 2"  # Empty + "Part 2"
        assert merged.usage.total_tokens == 50

    def test_merge_results_no_results_raises_error(self):
        """Test that merge_results raises error with no results"""
        with pytest.raises(ValueError, match="At least one result is required"):
            ChatContinue.merge_results()


class TestChatContinueIntegration:
    """Test ChatContinue integration scenarios"""

    @patch("lexilux.chat.client.requests.post")
    def test_continue_workflow(self, mock_post):
        """Test complete continue workflow"""
        chat = Chat(
            base_url="https://api.example.com/v1",
            api_key="test-key",
            model="gpt-4",
        )

        # Initial request that gets cut off
        result1 = ChatResult(
            text="This is a long story that was cut off",
            usage=Usage(input_tokens=10, output_tokens=50, total_tokens=60),
            finish_reason="length",
        )

        history = ChatHistory.from_messages("Write a long story")
        history.append_result(result1)

        # Continue request
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "choices": [
                {"message": {"content": " and here is the continuation"}, "finish_reason": "stop"}
            ],
            "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
        }
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        # New API with auto_merge=True (default) returns merged result directly
        full_result = ChatContinue.continue_request(
            chat, result1, history=history, add_continue_prompt=True
        )

        assert "This is a long story that was cut off" in full_result.text
        assert " and here is the continuation" in full_result.text
        assert full_result.usage.total_tokens == 90  # 60 + 30
        assert full_result.finish_reason == "stop"
