"""Unit tests for Poe2VisualTool v1.2.0 features:

- Cloud Dynamic Offsets Auto-Sync
- POI Radar Shared Memory Parser & Proximity Alarms
"""

import struct
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from src.assistant.radar_overlay import (
    ENTITY_SIZE,
    HEADER_SIZE,
    POI_MAGIC,
    PoiRadarReader,
    RadarEntity,
    RadarSnapshot,
)
from src.commercial.cloud_sync import CloudOffsetsSync, SyncResult


# ==========================================================
# 1. Tests for Cloud Dynamic Offsets Auto-Sync
# ==========================================================
def test_cloud_sync_local_fingerprint(tmp_path: Path):
    offsets_file = tmp_path / "offsets.toml"
    offsets_file.write_text('version = "0.5.5-rel2"\n[Camera]\nzoom = 0x520\n', encoding="utf-8")

    sync = CloudOffsetsSync(local_config_path=offsets_file)
    fp = sync.read_local_fingerprint()
    assert fp == "0.5.5-rel2"


def test_cloud_sync_already_up_to_date(tmp_path: Path):
    offsets_file = tmp_path / "offsets.toml"
    offsets_file.write_text('version = "0.5.5-rel2"\n[Camera]\nzoom = 0x520\n', encoding="utf-8")

    sync = CloudOffsetsSync(local_config_path=offsets_file)
    with patch.object(
        sync,
        "fetch_remote_offsets",
        return_value=(True, 'version = "0.5.5-rel2"\n[Camera]\nzoom = 0x520\n', ""),
    ):
        result = sync.sync()
        assert result.success is True
        assert result.updated is False
        assert result.local_version == "0.5.5-rel2"


def test_cloud_sync_updates_and_creates_backup(tmp_path: Path):
    offsets_file = tmp_path / "offsets.toml"
    offsets_file.write_text('version = "0.5.5-rel1"\n[Camera]\nzoom = 0x520\n', encoding="utf-8")

    sync = CloudOffsetsSync(local_config_path=offsets_file)
    remote_content = 'version = "0.5.5-rel2"\n[Camera]\nzoom = 0x528\n[Minimap]\ngrid = 0x120\n'

    with patch.object(sync, "fetch_remote_offsets", return_value=(True, remote_content, "")):
        result = sync.sync()
        assert result.success is True
        assert result.updated is True
        assert result.remote_version == "0.5.5-rel2"

        # Check that file content was updated
        assert offsets_file.read_text(encoding="utf-8") == remote_content
        # Check that backup was created
        backup_file = tmp_path / "offsets.toml.bak"
        assert backup_file.exists()
        assert "0.5.5-rel1" in backup_file.read_text(encoding="utf-8")


def test_cloud_sync_rejects_invalid_remote_payload(tmp_path: Path):
    offsets_file = tmp_path / "offsets.toml"
    offsets_file.write_text('version = "0.5.5-rel1"\n', encoding="utf-8")

    sync = CloudOffsetsSync(local_config_path=offsets_file)
    with patch.object(
        sync, "fetch_remote_offsets", return_value=(True, "<html>404 Not Found</html>", "")
    ):
        result = sync.sync()
        assert result.success is False
        assert result.updated is False


# ==========================================================
# 2. Tests for POI Radar Shared State Parsing
# ==========================================================
def create_mock_poi_state_bytes(
    seqlock: int = 2,
    entities_data: list = None,
) -> bytes:
    if entities_data is None:
        entities_data = [
            (
                2001,
                1,  # Boss
                100.0,
                200.0,
                50.0,
                960.0,
                540.0,
                1,
                b"\x00\x00\x00",
                75.0,
                0.92,
                b"Crucible Boss\x00\x00\x00",
            ),
            (
                2002,
                3,  # Strongbox
                120.0,
                180.0,
                50.0,
                1050.0,
                600.0,
                1,
                b"\x00\x00\x00",
                45.0,
                1.0,
                b"Arcanist's Strongbox\x00",
            ),
        ]

    # Header: magic, seqlock, count, reserved, player_x, player_y, player_z, camera_yaw
    header = struct.pack(
        "<IIII4f",
        POI_MAGIC,
        seqlock,
        len(entities_data),
        0,
        100.0,
        150.0,
        50.0,
        0.785,
    )

    body = bytearray()
    for ent in entities_data:
        eid, etype, wx, wy, wz, sx, sy, on_scr, pad, dist, hp, name_bytes = ent
        padded_name = name_bytes.ljust(32, b"\x00")[:32]
        body.extend(
            struct.pack(
                "<QI5fB3sff32s",
                eid,
                etype,
                wx,
                wy,
                wz,
                sx,
                sy,
                on_scr,
                pad,
                dist,
                hp,
                padded_name,
            )
        )

    # Pad remaining entries to 64 total
    empty_entry = b"\x00" * ENTITY_SIZE
    body.extend(empty_entry * (64 - len(entities_data)))

    return header + bytes(body)


def test_radar_reader_parse_snapshot():
    raw_data = create_mock_poi_state_bytes(seqlock=4)

    reader = PoiRadarReader()
    # Mock mmap with byte buffer
    mock_mmap = MagicMock()
    mock_mmap.seek = MagicMock()

    # Configure read calls: header (32B), entity 1 (76B), entity 2 (76B), seqlock check (4B)
    read_chunks = [
        raw_data[:HEADER_SIZE],
        raw_data[HEADER_SIZE : HEADER_SIZE + ENTITY_SIZE],
        raw_data[HEADER_SIZE + ENTITY_SIZE : HEADER_SIZE + 2 * ENTITY_SIZE],
        struct.pack("<I", 4),  # seq2 == 4
    ]
    mock_mmap.read.side_effect = read_chunks
    reader._mmap = mock_mmap

    snapshot = reader.read_snapshot()
    assert snapshot is not None
    assert snapshot.count == 2
    assert snapshot.player_x == 100.0

    boss = snapshot.entities[0]
    assert boss.id == 2001
    assert boss.is_boss is True
    assert boss.name == "Crucible Boss"
    assert boss.distance == 75.0

    box = snapshot.entities[1]
    assert box.id == 2002
    assert box.is_strongbox is True
    assert box.name == "Arcanist's Strongbox"


def test_radar_threat_alert_callback():
    reader = PoiRadarReader()

    entities = [
        RadarEntity(
            id=3001,
            poi_type=1,  # Boss
            world_x=10.0,
            world_y=10.0,
            world_z=0.0,
            screen_x=500.0,
            screen_y=500.0,
            on_screen=True,
            distance=65.0,  # inside 180.0
            health_pct=1.0,
            name="Map Boss",
        ),
        RadarEntity(
            id=3002,
            poi_type=2,  # Rare monster (not boss/box)
            world_x=20.0,
            world_y=20.0,
            world_z=0.0,
            screen_x=600.0,
            screen_y=600.0,
            on_screen=True,
            distance=50.0,
            health_pct=1.0,
            name="Rare Bear",
        ),
    ]

    snapshot = RadarSnapshot(
        count=2,
        player_x=0.0,
        player_y=0.0,
        player_z=0.0,
        camera_yaw=0.0,
        entities=entities,
    )

    alerted_targets = []

    def on_alert(ent: RadarEntity):
        alerted_targets.append(ent.name)

    threats = reader.check_high_value_threats(
        snapshot, distance_threshold=180.0, on_new_target=on_alert
    )

    assert len(threats) == 1
    assert threats[0].name == "Map Boss"
    assert alerted_targets == ["Map Boss"]

    # Subsequent frame should NOT trigger duplicate alarm for the same ID
    alerted_targets.clear()
    threats2 = reader.check_high_value_threats(
        snapshot, distance_threshold=180.0, on_new_target=on_alert
    )
    assert len(threats2) == 0
    assert len(alerted_targets) == 0
