"""Tests for Faustus Currency Exchange Handler & Waystone T15 Restock.
=====================================================================
Kiểm thử toàn diện:
1. Cấu hình & Định mức an toàn (Config defaults, Rule 4 trần < 500 dòng).
2. Khu vực an toàn (INV-EXCHANGE-SAFE-AREA): Chấp nhận Hideout/Town, từ chối Map.
3. Điều kiện kích hoạt Restock: Waystone stock == 0 kích hoạt, stock > 0 bỏ qua.
4. Bất biến dự trữ Chaos sinh tồn (INV-EXCHANGE-RESERVE-SAFE): Từ chối khi Chaos <= 50.
5. Bất biến thuế Vàng (INV-EXCHANGE-GOLD-TAX): Từ chối khi Vàng <= 5000.
6. Chuỗi phím SSoT (Rule 7): Phím '/' (VK_OEM_2 0xBF) mở sàn, Spacebar (VK_SPACE 0x20) đóng sàn.
7. Thực thi trọn vẹn chu trình Restock Waystone T15 (Chaos -> Waystone T15).
8. Khóa an toàn mô phỏng (INV-FOCUS-SAFETY-SEPARATION):
   - is_simulated=True không rò rỉ tín hiệu phần cứng ra desktop.
   - Từ chối mở sàn khi không có Focus trên môi trường thực tế.
"""

from unittest.mock import MagicMock, patch
import pytest

from src.assistant_tool.market.currency_exchange_handler import (
    CurrencyExchangeHandler,
    CurrencyExchangeHandlerConfig,
    ExchangeResult,
)
from src.common.win32_input import (
    VK_OEM_2,
    VK_SPACE,
    set_input_simulation_mode,
)


class TestCurrencyExchangeHandlerConfig:
    def test_default_config_values(self):
        """Xác minh các hằng số bất biến trong cấu hình mặc định."""
        cfg = CurrencyExchangeHandlerConfig()
        assert cfg.min_gold_reserve == 5000          # INV-EXCHANGE-GOLD-TAX
        assert cfg.min_chaos_reserve == 50.0        # INV-EXCHANGE-RESERVE-SAFE
        assert cfg.chaos_per_waystone_t15 == 2.0
        assert cfg.gold_tax_per_waystone == 150
        assert cfg.default_buy_count == 5
        assert cfg.waystone_restock_threshold == 0


class TestCurrencyExchangeHandlerAreaAndNeed:
    def test_safe_area_filtering(self):
        """INV-EXCHANGE-SAFE-AREA: Chỉ mở sàn tại Hideout hoặc Town."""
        handler = CurrencyExchangeHandler(is_simulated=True)
        assert handler.is_safe_area("Shoreline Hideout") is True
        assert handler.is_safe_area("Lioneye's Watch Town") is True
        assert handler.is_safe_area("Forest Encampment") is True
        assert handler.is_safe_area("Cemetery Map (Tier 15)") is False
        assert handler.is_safe_area("Deserted Arena") is False

    def test_unsafe_area_rejected(self):
        """Từ chối giao dịch nếu đang trong Map chiến đấu."""
        handler = CurrencyExchangeHandler(is_simulated=True)
        res = handler.execute_waystone_t15_restock(
            waystone_stock=0,
            available_chaos=100.0,
            available_gold=10000,
            area_name="Cemetery Map (Tier 15)",
        )
        assert res.success is False
        assert res.action_taken == "REJECTED_UNSAFE_AREA"
        assert "không an toàn" in res.reason

    def test_restock_not_needed_when_stock_positive(self):
        """Kho Stash vẫn còn Waystone (> 0) thì không cần mua thêm."""
        handler = CurrencyExchangeHandler(is_simulated=True)
        res = handler.execute_waystone_t15_restock(
            waystone_stock=3,
            available_chaos=100.0,
            available_gold=10000,
            area_name="Shoreline Hideout",
        )
        assert res.success is True
        assert res.action_taken == "NO_ACTION_REQUIRED"
        assert res.waystones_bought == 0
        assert res.chaos_spent == 0.0
        assert res.gold_spent == 0
        assert res.remaining_chaos == 100.0
        assert res.remaining_gold == 10000


class TestCurrencyExchangeHandlerReserves:
    def test_reject_low_chaos_reserve(self):
        """INV-EXCHANGE-RESERVE-SAFE: Từ chối khi Chaos <= min_chaos_reserve."""
        cfg = CurrencyExchangeHandlerConfig(min_chaos_reserve=50.0, chaos_per_waystone_t15=2.0)
        handler = CurrencyExchangeHandler(config=cfg, is_simulated=True)

        # Có 51 Chaos -> sau dự trữ còn 1 Chaos (< 2.0 đơn giá)
        res = handler.execute_waystone_t15_restock(
            waystone_stock=0,
            available_chaos=51.0,
            available_gold=10000,
            area_name="Shoreline Hideout",
        )
        assert res.success is False
        assert res.action_taken == "REJECTED_LOW_CHAOS"
        assert "Bảo vệ Chaos Orb" in res.reason

    def test_reject_low_gold_tax(self):
        """INV-EXCHANGE-GOLD-TAX: Từ chối khi Vàng <= min_gold_reserve."""
        cfg = CurrencyExchangeHandlerConfig(min_gold_reserve=5000, gold_tax_per_waystone=150)
        handler = CurrencyExchangeHandler(config=cfg, is_simulated=True)

        # Có 5100 Vàng -> sau dự trữ còn 100 Vàng (< 150 phí thuế)
        res = handler.execute_waystone_t15_restock(
            waystone_stock=0,
            available_chaos=100.0,
            available_gold=5100,
            area_name="Shoreline Hideout",
        )
        assert res.success is False
        assert res.action_taken == "REJECTED_LOW_GOLD"
        assert "Bảo vệ Gold Tax" in res.reason

    def test_partial_restock_budget_clamping(self):
        """Mua số lượng giới hạn theo ngân sách khả dụng thay vì mua full 5."""
        cfg = CurrencyExchangeHandlerConfig(
            min_chaos_reserve=50.0,
            chaos_per_waystone_t15=2.0,
            min_gold_reserve=5000,
            gold_tax_per_waystone=150,
            default_buy_count=5,
        )
        handler = CurrencyExchangeHandler(config=cfg, is_simulated=True)

        # Có 54.0 Chaos -> usable chaos = 4.0 -> mua được 2 Waystones (đơn giá 2.0)
        # Vàng có 10000 -> dư dả
        res = handler.execute_waystone_t15_restock(
            waystone_stock=0,
            available_chaos=54.0,
            available_gold=10000,
            area_name="Shoreline Hideout",
        )
        assert res.success is True
        assert res.action_taken == "RESTOCKED_WAYSTONES"
        assert res.waystones_bought == 2
        assert res.chaos_spent == 4.0
        assert res.gold_spent == 300
        assert res.remaining_chaos == 50.0
        assert res.remaining_gold == 9700


class TestCurrencyExchangeHandlerExecutionAndKeys:
    def test_key_sequence_ssot_slash_and_space(self):
        """Rule 7 SSoT: Mở sàn bằng '/' (0xBF) và đóng an toàn bằng Spacebar (0x20)."""
        captured_keys = []

        def mock_press(vk: int):
            captured_keys.append(vk)

        handler = CurrencyExchangeHandler(
            is_simulated=True,
            press_key_func=mock_press,
        )

        # Mở sàn
        opened = handler.open_currency_exchange()
        assert opened is True
        assert handler.is_ui_open is True
        assert captured_keys == [VK_OEM_2]  # 0xBF

        # Đóng sàn
        closed = handler.close_currency_exchange()
        assert closed is True
        assert handler.is_ui_open is False
        assert captured_keys == [VK_OEM_2, VK_SPACE]  # 0xBF, 0x20

    def test_full_successful_restock_lifecycle(self):
        """Toàn bộ quy trình restock Waystone T15 thành công khi stock == 0."""
        captured_keys = []
        captured_clicks = []

        def mock_press(vk: int):
            captured_keys.append(vk)

        def mock_click(x: int, y: int):
            captured_clicks.append((x, y))
            return True

        handler = CurrencyExchangeHandler(
            screen_w=2560,
            screen_h=1440,
            is_simulated=True,
            press_key_func=mock_press,
            click_func=mock_click,
        )

        res = handler.execute_waystone_t15_restock(
            waystone_stock=0,
            available_chaos=120.0,
            available_gold=15000,
            buy_count=5,
            area_name="Shoreline Hideout",
        )

        assert res.success is True
        assert res.action_taken == "RESTOCKED_WAYSTONES"
        assert res.waystones_bought == 5
        assert res.chaos_spent == 10.0      # 5 * 2.0
        assert res.gold_spent == 750        # 5 * 150
        assert res.remaining_chaos == 110.0 # 120.0 - 10.0
        assert res.remaining_gold == 14250  # 15000 - 750
        assert handler.total_restock_operations == 1
        assert handler.is_ui_open is False

        # Kiểm tra chuỗi phím: Mở sàn bằng '/' (0xBF) rồi đóng bằng Spacebar (0x20)
        assert captured_keys == [VK_OEM_2, VK_SPACE]
        # Kiểm tra thao tác click chuột trên UI Faustus
        assert len(captured_clicks) == 3


class TestCurrencyExchangeFocusSafetySeparation:
    def test_unfocused_real_mode_blocks_open(self):
        """INV-FOCUS-SAFETY-SEPARATION: Khi is_simulated=False và mất focus -> CHẶN mở sàn."""
        handler = CurrencyExchangeHandler(is_simulated=False)

        with patch("src.assistant_tool.market.currency_exchange_handler.is_poe2_window_focused", return_value=False):
            with patch("src.assistant_tool.market.currency_exchange_handler.ensure_window_focus"):
                opened = handler.open_currency_exchange()
                assert opened is False
                assert handler.is_ui_open is False

    def test_simulation_mode_zero_hardware_leakage(self):
        """INV-FOCUS-SAFETY-SEPARATION: is_simulated=True không phát sinh phần cứng."""
        set_input_simulation_mode(True)
        try:
            handler = CurrencyExchangeHandler(is_simulated=True)
            # Chạy open và close mà không mock callback -> dùng hàm win32_input nội bộ
            # Hàm nội bộ kiểm tra _is_physical_allowed(is_simulated=True) trả về False -> 0 leakage!
            opened = handler.open_currency_exchange()
            assert opened is True
            assert handler.is_ui_open is True

            closed = handler.close_currency_exchange()
            assert closed is True
            assert handler.is_ui_open is False
        finally:
            set_input_simulation_mode(None)
