#!/usr/bin/env python3
"""Tests for the house buying/selling sub-parser."""

import sys
import os
import unittest

sys.path.insert(0, os.path.dirname(__file__))
from house_parser import HouseParser


def feed_lines(hp, lines):
    """Feed lines, return the first non-None result."""
    for sender, msg in lines:
        result = hp.feed(sender, msg)
        if result is not None:
            return result
    return None


class TestBuyBasic(unittest.TestCase):
    """Basic house buying flow."""

    def test_buy_3_properties(self):
        """Buy 1+1+2 houses on Red monopoly."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Kentucky ave. (R) (0) Indiana ave. (R) (0) Illinois ave. (R) (0) "),
            ("monop", "Houses will cost $150"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Kentucky ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Indiana ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Illinois ave. (R) (0): "),
            ("merp", ".2"),
            ("monop", "You asked for 4 houses for $600"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertIsNotNone(result)
        self.assertEqual(result["action"], "buy")
        self.assertEqual(result["changes"], {21: 1, 23: 1, 24: 2})
        self.assertEqual(result["cost"], 600)

    def test_buy_2_property_monopoly(self):
        """Buy houses on a 2-property monopoly (Purple)."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Mediterranean ave. (P) (0) Baltic ave. (P) (0) "),
            ("monop", "Houses will cost $50"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Mediterranean ave. (P) (0): "),
            ("fbs", ".2"),
            ("monop", "Baltic ave. (P) (0): "),
            ("fbs", ".2"),
            ("monop", "You asked for 4 houses for $200"),
            ("monop", "Is that ok? "),
            ("fbs", ".y"),
        ])
        self.assertEqual(result["changes"], {1: 2, 3: 2})
        self.assertEqual(result["cost"], 200)

    def test_buy_with_existing_houses(self):
        """Buy more houses on properties that already have some."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $100"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "St. James pl. (O) (2): "),
            ("merp", ".1"),
            ("monop", "Tennessee ave. (O) (1): "),
            ("merp", ".2"),
            ("monop", "New York ave. (O) (1): "),
            ("merp", ".2"),
            ("monop", "You asked for 5 houses for $500"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertEqual(result["changes"], {16: 3, 18: 3, 19: 3})

    def test_buy_rejected(self):
        """Player says no to 'Is that ok?'."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $150"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Kentucky ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Indiana ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Illinois ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "You asked for 3 houses for $450"),
            ("monop", "Is that ok? "),
            ("merp", ".n"),
        ])
        self.assertEqual(result, {"action": "cancel"})
        self.assertFalse(hp.active)

    def test_buy_to_hotel(self):
        """Buy up to hotel (5 houses)."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $200"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Park place (D) (4): "),
            ("fbs", ".1"),
            ("monop", "Boardwalk (D) (4): "),
            ("fbs", ".1"),
            ("monop", "You asked for 2 houses for $400"),
            ("monop", "Is that ok? "),
            ("fbs", ".y"),
        ])
        self.assertEqual(result["changes"], {37: 5, 39: 5})

    def test_buy_skip_hotel(self):
        """Property already has hotel — prompt shows (H), auto-skipped."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $50"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Oriental ave. (L) (3): "),
            ("fbs", ".2"),
            ("monop", "Vermont ave. (L) (H):"),  # auto-skipped
            ("monop", "Connecticut ave. (L) (4): "),
            ("fbs", ".1"),
            ("monop", "You asked for 3 houses for $150"),
            ("monop", "Is that ok? "),
            ("fbs", ".y"),
        ])
        self.assertEqual(result["changes"], {6: 5, 9: 5})
        # Vermont (8) not in changes — already at hotel, no change
        self.assertNotIn(8, result["changes"])


class TestSellBasic(unittest.TestCase):
    """Basic house selling flow."""

    def test_sell_houses(self):
        """Sell houses from Red monopoly."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will get you $75 apiece"),
            ("monop", "Kentucky ave. (R) (1) Indiana ave. (R) (1) Illinois ave. (R) (2) "),
            ("monop", "How many houses do you wish to sell from"),
            ("monop", "Kentucky ave. (R) (1): "),
            ("merp", ".1"),
            ("monop", "Indiana ave. (R) (1): "),
            ("merp", ".1"),
            ("monop", "Illinois ave. (R) (2): "),
            ("merp", ".2"),
            ("monop", "You asked to sell 4 houses for $300"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertEqual(result["action"], "sell")
        self.assertEqual(result["changes"], {21: 0, 23: 0, 24: 0})
        self.assertEqual(result["cost"], 300)

    def test_sell_from_hotel(self):
        """Sell 1 house from a hotel."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will get you $25 apiece"),
            ("monop", "How many houses do you wish to sell from"),
            ("monop", "Oriental ave. (L) (H): "),
            ("fbs", ".1"),
            ("monop", "Vermont ave. (L) (H): "),
            ("fbs", ".0"),
            ("monop", "Connecticut ave. (L) (H): "),
            ("fbs", ".0"),
            ("monop", "You asked to sell 1 houses for $25"),
            ("monop", "Is that ok? "),
            ("fbs", ".y"),
        ])
        self.assertEqual(result["changes"], {6: 4})  # hotel(5) - 1 = 4

    def test_sell_skip_zero(self):
        """Property with 0 houses — auto-skipped during sell."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will get you $100 apiece"),
            ("monop", "How many houses do you wish to sell from"),
            ("monop", "St. James pl. (O) (0):"),  # auto-skipped
            ("monop", "Tennessee ave. (O) (2): "),
            ("merp", ".1"),
            ("monop", "New York ave. (O) (3): "),
            ("merp", ".2"),
            ("monop", "You asked to sell 3 houses for $300"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertEqual(result["changes"], {18: 1, 19: 1})
        self.assertNotIn(16, result["changes"])  # St. James had 0


class TestErrorHandling(unittest.TestCase):
    """Error cases and retries."""

    def test_spread_too_wide_retry(self):
        """'Spread too wide' resets all prompts."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $150"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Kentucky ave. (R) (0): "),
            ("merp", ".3"),
            ("monop", "Indiana ave. (R) (0): "),
            ("merp", ".0"),
            ("monop", "Illinois ave. (R) (0): "),
            ("merp", ".0"),
            ("monop", "That makes the spread too wide.  Try again"),
            # Retry
            ("monop", "Kentucky ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Indiana ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "Illinois ave. (R) (0): "),
            ("merp", ".1"),
            ("monop", "You asked for 3 houses for $450"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertIsNotNone(result)
        self.assertEqual(result["changes"], {21: 1, 23: 1, 24: 1})

    def test_too_many_retry(self):
        """'Too many' re-prompts same property."""
        hp = HouseParser()
        result = feed_lines(hp, [
            ("monop", "Houses will cost $150"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Kentucky ave. (R) (3): "),
            ("merp", ".5"),
            ("monop", "That's too many.  The most you can buy is 2"),
            ("monop", "Kentucky ave. (R) (3): "),
            ("merp", ".2"),
            ("monop", "Indiana ave. (R) (3): "),
            ("merp", ".2"),
            ("monop", "Illinois ave. (R) (3): "),
            ("merp", ".2"),
            ("monop", "You asked for 6 houses for $900"),
            ("monop", "Is that ok? "),
            ("merp", ".y"),
        ])
        self.assertEqual(result["changes"], {21: 5, 23: 5, 24: 5})

    def test_resets_after_completion(self):
        """Parser resets to idle after completion."""
        hp = HouseParser()
        feed_lines(hp, [
            ("monop", "Houses will cost $50"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Mediterranean ave. (P) (0): "),
            ("fbs", ".1"),
            ("monop", "Baltic ave. (P) (0): "),
            ("fbs", ".1"),
            ("monop", "You asked for 2 houses for $100"),
            ("monop", "Is that ok? "),
            ("fbs", ".y"),
        ])
        self.assertFalse(hp.active)
        self.assertEqual(hp.state, "idle")


class TestRealLog(unittest.TestCase):
    """Replay real log segments through the sub-parser."""

    def _replay_segment(self, start, end):
        """Replay lines start..end from test log through HouseParser."""
        hp = HouseParser()
        results = []
        with open("test_data/monop.log") as f:
            for i, line in enumerate(f, 1):
                if i < start:
                    continue
                if i > end:
                    break
                parts = line.rstrip('\n').split('\t', 2)
                if len(parts) < 3:
                    continue
                sender = parts[1].strip().lstrip('@')
                msg = parts[2]
                r = hp.feed(sender, msg)
                if r is not None:
                    results.append(r)
        return results, hp

    def test_real_buy_line_59(self):
        """Real log: merp buys 1+1+2 houses on Red (lines 59-73)."""
        results, hp = self._replay_segment(59, 73)
        self.assertEqual(len(results), 1)
        r = results[0]
        self.assertEqual(r["action"], "buy")
        # Kentucky=21, Indiana=23, Illinois=24
        self.assertEqual(r["changes"], {21: 1, 23: 1, 24: 2})
        self.assertEqual(r["cost"], 600)

    def test_real_sell_line_354(self):
        """Real log: merp sells 1+1+2 houses from Red (lines 354-366)."""
        results, hp = self._replay_segment(354, 366)
        self.assertEqual(len(results), 1)
        r = results[0]
        self.assertEqual(r["action"], "sell")
        self.assertEqual(r["changes"], {21: 0, 23: 0, 24: 0})

    def test_real_sell_hotel_line_4386(self):
        """Real log: Derecho sells 1 hotel from Oriental (lines 4386-4399)."""
        results, hp = self._replay_segment(4386, 4399)
        self.assertEqual(len(results), 1)
        r = results[0]
        self.assertEqual(r["action"], "sell")
        # Oriental (6): H(5) - 1 = 4
        self.assertEqual(r["changes"][6], 4)


class TestActiveState(unittest.TestCase):
    """Test the active property."""

    def test_not_active_initially(self):
        hp = HouseParser()
        self.assertFalse(hp.active)

    def test_active_during_prompts(self):
        hp = HouseParser()
        hp.feed("monop", "Houses will cost $150")
        hp.feed("monop", "How many houses do you wish to buy for")
        self.assertTrue(hp.active)

    def test_active_during_confirm(self):
        hp = HouseParser()
        feed_lines(hp, [
            ("monop", "Houses will cost $50"),
            ("monop", "How many houses do you wish to buy for"),
            ("monop", "Mediterranean ave. (P) (0): "),
            ("fbs", ".1"),
            ("monop", "Baltic ave. (P) (0): "),
            ("fbs", ".1"),
            ("monop", "You asked for 2 houses for $100"),
        ])
        self.assertTrue(hp.active)


if __name__ == "__main__":
    unittest.main(verbosity=2)
