"""
Unit tests for PageRenderer coordinate transformations
"""

import pytest
from pyPhotoAlbum.page_renderer import PageRenderer


class TestPageRendererCoordinates:
    """Test coordinate transformation methods"""

    def test_page_to_screen_no_zoom_no_pan(self):
        """Test page_to_screen conversion with zoom=1.0 and no pan"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # Element at page origin should map to screen_x, screen_y
        screen_x, screen_y = renderer.page_to_screen(0, 0)
        assert screen_x == 100.0
        assert screen_y == 200.0

        # Element at (50, 75) should be offset by that amount
        screen_x, screen_y = renderer.page_to_screen(50, 75)
        assert screen_x == 150.0
        assert screen_y == 275.0

    def test_page_to_screen_with_zoom(self):
        """Test page_to_screen conversion with zoom applied"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=2.0
        )

        # With zoom=2.0, distances should be doubled
        screen_x, screen_y = renderer.page_to_screen(50, 75)
        assert screen_x == 200.0  # 100 + 50*2
        assert screen_y == 350.0  # 200 + 75*2

    def test_page_to_screen_with_fractional_zoom(self):
        """Test page_to_screen conversion with fractional zoom"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=0.5
        )

        # With zoom=0.5, distances should be halved
        screen_x, screen_y = renderer.page_to_screen(100, 150)
        assert screen_x == 150.0  # 100 + 100*0.5
        assert screen_y == 275.0  # 200 + 150*0.5

    def test_screen_to_page_no_zoom_no_pan(self):
        """Test screen_to_page conversion with zoom=1.0 and no pan"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # Screen position at screen_x, screen_y should map to page origin
        page_x, page_y = renderer.screen_to_page(100.0, 200.0)
        assert page_x == 0.0
        assert page_y == 0.0

        # Screen position offset should map to same offset in page coords
        page_x, page_y = renderer.screen_to_page(150.0, 275.0)
        assert page_x == 50.0
        assert page_y == 75.0

    def test_screen_to_page_with_zoom(self):
        """Test screen_to_page conversion with zoom applied"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=2.0
        )

        # With zoom=2.0, screen distances should be divided by 2 to get page coords
        page_x, page_y = renderer.screen_to_page(200.0, 350.0)
        assert page_x == 50.0  # (200-100)/2
        assert page_y == 75.0  # (350-200)/2

    def test_roundtrip_conversion_no_zoom(self):
        """Test that page->screen->page conversion is accurate with no zoom"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # Start with page coordinates
        orig_page_x, orig_page_y = 123.45, 678.90

        # Convert to screen and back
        screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
        page_x, page_y = renderer.screen_to_page(screen_x, screen_y)

        # Should get back the original values
        assert abs(page_x - orig_page_x) < 0.001
        assert abs(page_y - orig_page_y) < 0.001

    def test_roundtrip_conversion_with_zoom(self):
        """Test that page->screen->page conversion is accurate with zoom"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5
        )

        # Start with page coordinates
        orig_page_x, orig_page_y = 123.45, 678.90

        # Convert to screen and back
        screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
        page_x, page_y = renderer.screen_to_page(screen_x, screen_y)

        # Should get back the original values (with floating point tolerance)
        assert abs(page_x - orig_page_x) < 0.001
        assert abs(page_y - orig_page_y) < 0.001

    def test_roundtrip_conversion_extreme_zoom(self):
        """Test coordinate conversion with extreme zoom levels"""
        for zoom in [0.1, 0.5, 1.0, 2.0, 5.0]:
            renderer = PageRenderer(
                page_width_mm=210.0, page_height_mm=297.0, screen_x=50.0, screen_y=100.0, dpi=96, zoom=zoom
            )

            orig_page_x, orig_page_y = 250.0, 400.0
            screen_x, screen_y = renderer.page_to_screen(orig_page_x, orig_page_y)
            page_x, page_y = renderer.screen_to_page(screen_x, screen_y)

            assert abs(page_x - orig_page_x) < 0.001
            assert abs(page_y - orig_page_y) < 0.001


class TestPageRendererBounds:
    """Test page bounds and point detection"""

    def test_is_point_in_page_inside(self):
        """Test is_point_in_page for points inside the page"""
        renderer = PageRenderer(
            page_width_mm=210.0,  # A4 width
            page_height_mm=297.0,  # A4 height
            screen_x=100.0,
            screen_y=200.0,
            dpi=96,
            zoom=1.0,
        )

        # Calculate page dimensions in pixels
        page_width_px = 210.0 * 96 / 25.4  # ~794 pixels
        page_height_px = 297.0 * 96 / 25.4  # ~1123 pixels

        # Point in center should be inside
        center_x = 100.0 + page_width_px / 2
        center_y = 200.0 + page_height_px / 2
        assert renderer.is_point_in_page(center_x, center_y)

        # Point at origin should be inside
        assert renderer.is_point_in_page(100.0, 200.0)

        # Point at bottom-right corner should be inside
        assert renderer.is_point_in_page(100.0 + page_width_px, 200.0 + page_height_px)

    def test_is_point_in_page_outside(self):
        """Test is_point_in_page for points outside the page"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # Point before page start
        assert not renderer.is_point_in_page(50.0, 150.0)

        # Point way beyond page
        assert not renderer.is_point_in_page(2000.0, 2000.0)

        # Point to the left of page
        assert not renderer.is_point_in_page(50.0, 500.0)

        # Point above page
        assert not renderer.is_point_in_page(500.0, 150.0)

    def test_is_point_in_page_with_zoom(self):
        """Test is_point_in_page with different zoom levels"""
        for zoom in [0.5, 1.0, 2.0]:
            renderer = PageRenderer(
                page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=zoom
            )

            # Center of page should always be inside regardless of zoom
            page_width_px = 210.0 * 96 / 25.4
            page_height_px = 297.0 * 96 / 25.4
            center_x = 100.0 + (page_width_px * zoom) / 2
            center_y = 200.0 + (page_height_px * zoom) / 2
            assert renderer.is_point_in_page(center_x, center_y)

    def test_get_page_bounds_screen(self):
        """Test get_page_bounds_screen returns correct screen coordinates"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5
        )

        x, y, w, h = renderer.get_page_bounds_screen()

        assert x == 100.0
        assert y == 200.0

        # Width and height should be scaled by zoom
        page_width_px = 210.0 * 96 / 25.4
        page_height_px = 297.0 * 96 / 25.4
        assert abs(w - page_width_px * 1.5) < 0.1
        assert abs(h - page_height_px * 1.5) < 0.1

    def test_get_page_bounds_page(self):
        """Test get_page_bounds_page returns correct page-local coordinates"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.5
        )

        x, y, w, h = renderer.get_page_bounds_page()

        # Origin should be at 0,0 in page-local coordinates
        assert x == 0.0
        assert y == 0.0

        # Width and height should NOT be affected by zoom (page-local coords)
        page_width_px = 210.0 * 96 / 25.4
        page_height_px = 297.0 * 96 / 25.4
        assert abs(w - page_width_px) < 0.1
        assert abs(h - page_height_px) < 0.1


class TestPageRendererSubPages:
    """Test sub-page detection for facing pages"""

    def test_get_sub_page_at_single_page(self):
        """Test that get_sub_page_at returns None for single pages"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # For non-facing pages, should return None
        result = renderer.get_sub_page_at(500.0, is_facing_page=False)
        assert result is None

    def test_get_sub_page_at_facing_page_left(self):
        """Test get_sub_page_at for left side of facing page"""
        renderer = PageRenderer(
            page_width_mm=420.0,  # Double width for facing page
            page_height_mm=297.0,
            screen_x=100.0,
            screen_y=200.0,
            dpi=96,
            zoom=1.0,
        )

        # Calculate center line
        page_width_px = 420.0 * 96 / 25.4
        center_x = 100.0 + page_width_px / 2

        # Point before center should be 'left'
        result = renderer.get_sub_page_at(center_x - 10, is_facing_page=True)
        assert result == "left"

    def test_get_sub_page_at_facing_page_right(self):
        """Test get_sub_page_at for right side of facing page"""
        renderer = PageRenderer(
            page_width_mm=420.0,  # Double width for facing page
            page_height_mm=297.0,
            screen_x=100.0,
            screen_y=200.0,
            dpi=96,
            zoom=1.0,
        )

        # Calculate center line
        page_width_px = 420.0 * 96 / 25.4
        center_x = 100.0 + page_width_px / 2

        # Point after center should be 'right'
        result = renderer.get_sub_page_at(center_x + 10, is_facing_page=True)
        assert result == "right"


class TestPageRendererDimensions:
    """Test page dimension calculations"""

    def test_page_dimensions_calculated_correctly(self):
        """Test that page dimensions are calculated correctly from mm to pixels"""
        renderer = PageRenderer(
            page_width_mm=210.0,  # A4 width
            page_height_mm=297.0,  # A4 height
            screen_x=0.0,
            screen_y=0.0,
            dpi=96,
            zoom=1.0,
        )

        # A4 at 96 DPI
        expected_width = 210.0 * 96 / 25.4  # ~794 pixels
        expected_height = 297.0 * 96 / 25.4  # ~1123 pixels

        assert abs(renderer.page_width_px - expected_width) < 0.1
        assert abs(renderer.page_height_px - expected_height) < 0.1

    def test_screen_dimensions_with_zoom(self):
        """Test that screen dimensions account for zoom"""
        renderer = PageRenderer(page_width_mm=210.0, page_height_mm=297.0, screen_x=0.0, screen_y=0.0, dpi=96, zoom=2.0)

        # Screen dimensions should be doubled due to zoom
        expected_width = (210.0 * 96 / 25.4) * 2.0
        expected_height = (297.0 * 96 / 25.4) * 2.0

        assert abs(renderer.screen_width - expected_width) < 0.1
        assert abs(renderer.screen_height - expected_height) < 0.1

    def test_different_dpi_values(self):
        """Test page dimensions with different DPI values"""
        dpi_values = [72, 96, 150, 300]

        for dpi in dpi_values:
            renderer = PageRenderer(
                page_width_mm=210.0, page_height_mm=297.0, screen_x=0.0, screen_y=0.0, dpi=dpi, zoom=1.0
            )

            expected_width = 210.0 * dpi / 25.4
            expected_height = 297.0 * dpi / 25.4

            assert abs(renderer.page_width_px - expected_width) < 0.1
            assert abs(renderer.page_height_px - expected_height) < 0.1


class TestPageRendererEdgeCases:
    """Test edge cases and boundary conditions"""

    def test_zero_coordinates(self):
        """Test handling of zero coordinates"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        screen_x, screen_y = renderer.page_to_screen(0, 0)
        assert screen_x == 100.0
        assert screen_y == 200.0

        page_x, page_y = renderer.screen_to_page(100.0, 200.0)
        assert page_x == 0.0
        assert page_y == 0.0

    def test_negative_page_coordinates(self):
        """Test handling of negative page coordinates"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        # Negative page coordinates should still convert correctly
        screen_x, screen_y = renderer.page_to_screen(-50, -75)
        assert screen_x == 50.0
        assert screen_y == 125.0

        # And back again
        page_x, page_y = renderer.screen_to_page(50.0, 125.0)
        assert page_x == -50.0
        assert page_y == -75.0

    def test_very_large_coordinates(self):
        """Test handling of very large coordinates"""
        renderer = PageRenderer(
            page_width_mm=210.0, page_height_mm=297.0, screen_x=100.0, screen_y=200.0, dpi=96, zoom=1.0
        )

        large_x, large_y = 10000.0, 20000.0

        screen_x, screen_y = renderer.page_to_screen(large_x, large_y)
        page_x, page_y = renderer.screen_to_page(screen_x, screen_y)

        assert abs(page_x - large_x) < 0.001
        assert abs(page_y - large_y) < 0.001
