"""Tests for Image Rotation Manager (F06)."""

from datetime import datetime, timezone

import numpy as np
import pytest

from gps_denied.core.rotation import IImageMatcher, ImageRotationManager
from gps_denied.schemas import GPSPoint
from gps_denied.schemas.rotation import RotationResult
from gps_denied.schemas.satellite import TileBounds


@pytest.fixture
def rotation_manager():
    return ImageRotationManager()


def test_rotate_image_360(rotation_manager):
    img = np.zeros((100, 100, 3), dtype=np.uint8)

    # Just draw a white rectangle to test rotation
    img[10:40, 10:40] = [255, 255, 255]

    r90 = rotation_manager.rotate_image_360(img, 90.0)
    assert r90.shape == (100, 100, 3)

    # Top left corner should move
    assert not np.array_equal(img, r90)


def test_heading_management(rotation_manager):
    fid = "flight_1"
    now = datetime.now(timezone.utc)

    assert rotation_manager.get_current_heading(fid) is None

    rotation_manager.update_heading(fid, 1, 370.0, now)  # should modulo to 10
    assert rotation_manager.get_current_heading(fid) == 10.0

    rotation_manager.update_heading(fid, 2, 90.0, now)
    assert rotation_manager.get_current_heading(fid) == 90.0


def test_detect_sharp_turn(rotation_manager):
    fid = "flight_2"
    now = datetime.now(timezone.utc)

    assert rotation_manager.detect_sharp_turn(fid, 90.0) is False  # no history

    rotation_manager.update_heading(fid, 1, 90.0, now)

    assert rotation_manager.detect_sharp_turn(fid, 100.0) is False  # delta 10
    assert rotation_manager.detect_sharp_turn(fid, 180.0) is True   # delta 90
    assert rotation_manager.detect_sharp_turn(fid, 350.0) is True   # delta 100
    assert rotation_manager.detect_sharp_turn(fid, 80.0) is False   # delta 10 (wraparound)

    # Wraparound test explicitly
    rotation_manager.update_heading(fid, 2, 350.0, now)
    assert rotation_manager.detect_sharp_turn(fid, 10.0) is False   # delta 20


class MockMatcher(IImageMatcher):
    def align_to_satellite(
        self, uav_image: np.ndarray, satellite_tile: np.ndarray,
        tile_bounds: TileBounds,
    ) -> RotationResult:
        # Mock that only matches when angle was originally 90
        # By checking internal state or just returning generic true for test
        return RotationResult(matched=True, initial_angle=90.0, precise_angle=90.0, confidence=0.99)


def test_try_rotation_steps(rotation_manager):
    fid = "flight_3"
    img = np.zeros((10, 10, 3), dtype=np.uint8)
    sat = np.zeros((10, 10, 3), dtype=np.uint8)

    nw = GPSPoint(lat=10.0, lon=10.0)
    se = GPSPoint(lat=9.0, lon=11.0)
    tb = TileBounds(nw=nw, ne=nw, sw=se, se=se, center=nw, gsd=0.5)

    matcher = MockMatcher()

    # Should perform sweep and mock matcher says matched=True immediately in the loop
    res = rotation_manager.try_rotation_steps(fid, 1, img, sat, tb, datetime.now(timezone.utc), matcher)

    assert res is not None
    assert res.matched is True
    # The first step is 0 degrees, the mock matcher returns matched=True.
    # Therefore the first matched angle is 0.
    assert res.initial_angle == 0.0
