#!/usr/bin/env python3
"""Tests for engine_lib/validation.py — validate_turn only."""

import sys
import os
import json

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from unittest.mock import patch, MagicMock


def test_turn_empty_inputs():
    """No action and no narrative should return (True, '', 'ok')."""
    from engine_lib.validation import validate_turn
    valid, reason, action = validate_turn("")
    assert valid is True
    assert reason == ""
    assert action == "ok"
    print("✓ empty inputs returns (True, '', 'ok')")


def _mock_read(p: str) -> str:
    low = str(p).lower()
    if "character" in low:
        return "HP: 10\nGold: 5\nInventory:\n- Healing Salve"
    if "journal" in low:
        return "# Journal\n\n## TODO\n- Find the relic\n\n## DONE\n"
    return "## Location\nTavern"


def _mock_read_no_gold(p: str) -> str:
    low = str(p).lower()
    if "character" in low:
        return "HP: 10\nGold: 0"
    if "journal" in low:
        return "# Journal\n\n## TODO\n\n## DONE\n"
    return "## Location\nTavern"


def _mock_read_basic(p: str) -> str:
    low = str(p).lower()
    if "character" in low:
        return "HP: 10"
    if "journal" in low:
        return "# Journal\n\n## TODO\n\n## DONE\n"
    return "## Location\nTavern"


def _tool_response(valid: bool, reason: str, action: str) -> str:
    return f'```tool\n{{"tool": "validate", "args": {{"valid": {str(valid).lower()}, "reason": "{reason}", "action": "{action}"}}}}\n```'


@patch("engine_lib.validation.state.read_file")
@patch("engine_lib.validation.state.truncate_world")
@patch("engine_lib.validation.call_llm")
def test_turn_valid(mock_call_llm, mock_truncate_world, mock_read_file):
    from engine_lib.validation import validate_turn

    mock_read_file.side_effect = _mock_read
    mock_truncate_world.return_value = "## Location\nTavern"
    mock_call_llm.return_value = _tool_response(True, "ok", "ok")

    valid, reason, action = validate_turn(
        "I use my healing salve",
        narrative="Kael applies the salve to his wound.",
        log_entry="Kael used his healing salve to restore 2 HP.",
        changes=[{"tool": "modify_inventory", "args": {"operation": "remove", "item": "Healing Salve"}},
                  {"tool": "modify_vitals", "args": {"stat": "HP", "operation": "set", "value": 8}}],
        story="At the tavern",
        log="- Entered the tavern",
    )

    assert valid is True
    assert reason == "ok"
    assert action == "ok"
    print("✓ turn validation returns (True, 'ok', 'ok')")


@patch("engine_lib.validation.state.read_file")
@patch("engine_lib.validation.state.truncate_world")
@patch("engine_lib.validation.call_llm")
def test_turn_reject(mock_call_llm, mock_truncate_world, mock_read_file):
    from engine_lib.validation import validate_turn

    mock_read_file.side_effect = _mock_read_no_gold
    mock_truncate_world.return_value = "## Location\nTavern"
    mock_call_llm.return_value = _tool_response(False, "Player has no gold", "reject")

    valid, reason, action = validate_turn(
        "I buy a round for the house",
        narrative="Kael orders drinks for everyone.",
        log_entry="Kael bought a round at the tavern.",
        changes=[{"tool": "modify_cash", "args": {"cash": 0}}],
        story="At the tavern",
        log="- Entered the tavern",
    )

    assert valid is False
    assert reason == "Player has no gold"
    assert action == "reject"
    print("✓ turn validation returns (False, reason, 'reject')")


@patch("engine_lib.validation.state.read_file")
@patch("engine_lib.validation.state.truncate_world")
@patch("engine_lib.validation.call_llm")
def test_turn_regenerate(mock_call_llm, mock_truncate_world, mock_read_file):
    from engine_lib.validation import validate_turn

    mock_read_file.side_effect = _mock_read
    mock_truncate_world.return_value = "## Location\nTavern"
    mock_call_llm.return_value = _tool_response(False, "Narrative says salve used but no modify_inventory with operation remove", "regenerate")

    valid, reason, action = validate_turn(
        "I use my healing salve",
        narrative="Kael applies the salve to his wound.",
        log_entry="Kael used his healing salve.",
        changes=[{"tool": "modify_vitals", "args": {"stat": "HP", "operation": "set", "value": 8}}],
        story="At the tavern",
        log="- Entered the tavern",
    )

    assert valid is False
    assert reason == "Narrative says salve used but no modify_inventory with operation remove"
    assert action == "regenerate"
    print("✓ turn validation returns (False, reason, 'regenerate')")


@patch("engine_lib.validation.state.read_file")
@patch("engine_lib.validation.state.truncate_world")
@patch("engine_lib.validation.call_llm")
def test_turn_bad_json(mock_call_llm, mock_truncate_world, mock_read_file):
    from engine_lib.validation import validate_turn

    mock_read_file.side_effect = _mock_read_basic
    mock_truncate_world.return_value = "## Location\nTavern"
    mock_call_llm.return_value = "not valid json"

    valid, reason, action = validate_turn(
        "I attack the dragon",
        narrative="Kael swings his sword.",
        log_entry="Kael attacked the dragon.",
        changes=[{"tool": "modify_vitals", "args": {"stat": "HP", "operation": "set", "value": 10}}],
        story="A dragon appears!",
        log="- Dragon spotted",
    )

    assert valid is False
    assert reason == "Unrecognized"
    assert action == "reject"
    print("✓ turn validation bad JSON gives (False, 'Unrecognized', 'reject')")


if __name__ == "__main__":
    test_turn_empty_inputs()
    test_turn_valid()
    test_turn_reject()
    test_turn_regenerate()
    test_turn_bad_json()
    print("\n✓ All validation tests passed")
