"""Tests for screenshot comparison."""

import os
import tempfile
from unittest.mock import patch

import pytest

# Skip entire module if optional dependencies not installed
pytest.importorskip("numpy", reason="numpy required for comparison tests")
pytest.importorskip("PIL", reason="Pillow required for comparison tests")

import numpy as np
from PIL import Image

from browser_hybrid import Browser


class TestCompare:
    def test_identical_images(self):
        """Test comparison of identical images."""
        with tempfile.TemporaryDirectory() as tmpdir:
            # Create two identical images
            img = Image.new("RGB", (100, 100), color="white")
            baseline = os.path.join(tmpdir, "baseline.png")
            current = os.path.join(tmpdir, "current.png")
            img.save(baseline)
            img.save(current)

            result = Browser().compare_screenshots(baseline, current)

            assert result.match is True
            assert result.diff_pixels == 0
            assert result.diff_percentage == 0.0
            assert result.baseline_size == (100, 100)
            assert result.current_size == (100, 100)

    def test_different_images(self):
        """Test comparison of different images."""
        with tempfile.TemporaryDirectory() as tmpdir:
            # Create baseline
            baseline = Image.new("RGB", (100, 100), color="white")
            baseline_path = os.path.join(tmpdir, "baseline.png")
            baseline.save(baseline_path)

            # Create different image
            current = Image.new("RGB", (100, 100), color="black")
            current_path = os.path.join(tmpdir, "current.png")
            current.save(current_path)

            result = Browser().compare_screenshots(baseline_path, current_path)

            assert result.match is False
            assert result.diff_pixels == 100 * 100  # All pixels differ
            assert result.diff_percentage == 100.0

    def test_threshold_tolerance(self):
        """Test threshold allows small differences."""
        with tempfile.TemporaryDirectory() as tmpdir:
            # Create baseline
            baseline_arr = np.ones((100, 100, 3), dtype=np.uint8) * 255
            baseline = Image.fromarray(baseline_arr)
            baseline_path = os.path.join(tmpdir, "baseline.png")
            baseline.save(baseline_path)

            # Create slightly different image (1% noise)
            current_arr = baseline_arr.copy()
            current_arr[0:10, 0:10] = 0  # 1% of pixels differ (10x10 square in 100x100)
            current = Image.fromarray(current_arr)
            current_path = os.path.join(tmpdir, "current.png")
            current.save(current_path)

            # Compare with 2% threshold
            result = Browser().compare_screenshots(baseline_path, current_path, threshold=2.0)

            assert result.match is True  # Within 2% threshold (actual is 1%)
            assert result.diff_percentage == 1.0

    def test_diff_image_generation(self):
        """Test diff image highlights differences."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline = Image.new("RGB", (100, 100), color="white")
            baseline_path = os.path.join(tmpdir, "baseline.png")
            baseline.save(baseline_path)

            current = Image.new("RGB", (100, 100), color="white")
            # Add a black pixel
            current.putpixel((0, 0), (0, 0, 0))
            current_path = os.path.join(tmpdir, "current.png")
            current.save(current_path)

            diff_path = os.path.join(tmpdir, "diff.png")
            result = Browser().compare_screenshots(
                baseline_path, current_path, output_diff=diff_path
            )

            assert os.path.exists(diff_path)
            assert result.match is False

            # Verify diff image has red pixel at (0, 0)
            with Image.open(diff_path) as diff_img:
                assert diff_img.getpixel((0, 0)) == (255, 0, 0)

    def test_region_comparison(self):
        """Test comparing specific regions."""
        with tempfile.TemporaryDirectory() as tmpdir:
            # Baseline: white with black square in corner
            baseline_arr = np.ones((200, 200, 3), dtype=np.uint8) * 255
            baseline_arr[0:50, 0:50] = 0
            baseline = Image.fromarray(baseline_arr)
            baseline_path = os.path.join(tmpdir, "baseline.png")
            baseline.save(baseline_path)

            # Current: all white
            current = Image.new("RGB", (200, 200), color="white")
            current_path = os.path.join(tmpdir, "current.png")
            current.save(current_path)

            # Compare region without the black square (should match)
            # region is (x, y, w, h)
            result = Browser().compare_screenshots(
                baseline_path, current_path, region=(50, 50, 150, 150)
            )

            assert result.match is True
            assert result.diff_pixels == 0

    def test_dimension_mismatch(self):
        """Test error on dimension mismatch."""
        with tempfile.TemporaryDirectory() as tmpdir:
            small = Image.new("RGB", (50, 50), color="white")
            large = Image.new("RGB", (100, 100), color="white")

            small_path = os.path.join(tmpdir, "small.png")
            large_path = os.path.join(tmpdir, "large.png")
            small.save(small_path)
            large.save(large_path)

            with pytest.raises(ValueError, match="dimensions"):
                Browser().compare_screenshots(small_path, large_path)

    def test_assert_visual_match_success(self):
        """Test assert_visual_match when images match."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline_path = os.path.join(tmpdir, "baseline.png")
            Image.new("RGB", (100, 100), color="white").save(baseline_path)

            browser = Browser()

            # Mock screenshot to create the same image
            def mock_screenshot(path, **kwargs):
                Image.new("RGB", (100, 100), color="white").save(path)
                return path

            with patch.object(browser, "screenshot", side_effect=mock_screenshot):
                result = browser.assert_visual_match(baseline_path)
                assert result.match is True

    def test_assert_visual_match_failure(self):
        """Test assert_visual_match when images differ."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline_path = os.path.join(tmpdir, "baseline.png")
            Image.new("RGB", (100, 100), color="white").save(baseline_path)

            browser = Browser()

            def mock_screenshot(path, **kwargs):
                Image.new("RGB", (100, 100), color="black").save(path)
                return path

            with patch.object(browser, "screenshot", side_effect=mock_screenshot):
                with pytest.raises(AssertionError, match="Visual mismatch"):
                    browser.assert_visual_match(baseline_path)

    def test_update_baseline(self):
        """Test update_baseline parameter."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline_path = os.path.join(tmpdir, "baseline.png")
            # Start with white baseline
            Image.new("RGB", (100, 100), color="white").save(baseline_path)

            browser = Browser()

            # New screenshot is black
            def mock_screenshot(path, **kwargs):
                Image.new("RGB", (100, 100), color="black").save(path)
                return path

            with patch.object(browser, "screenshot", side_effect=mock_screenshot):
                browser.assert_visual_match(baseline_path, update_baseline=True)

                # Check that baseline is now black
                with Image.open(baseline_path) as img:
                    assert np.array(img).mean() == 0.0

    def test_threshold_negative(self):
        """Negative threshold should raise ValueError."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline = os.path.join(tmpdir, "baseline.png")
            current = os.path.join(tmpdir, "current.png")
            img = Image.new("RGB", (100, 100), color="white")
            img.save(baseline)
            img.save(current)

            browser = Browser()
            with pytest.raises(ValueError, match="threshold must be >= 0.0"):
                browser.compare_screenshots(baseline, current, threshold=-1.0)

    def test_threshold_over_100(self):
        """Threshold > 100 should raise ValueError."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline = os.path.join(tmpdir, "baseline.png")
            current = os.path.join(tmpdir, "current.png")
            img = Image.new("RGB", (100, 100), color="white")
            img.save(baseline)
            img.save(current)

            browser = Browser()
            with pytest.raises(ValueError, match="threshold must be <= 100.0"):
                browser.compare_screenshots(baseline, current, threshold=150.0)

    def test_threshold_boundary(self):
        """Threshold 0 and 100 should be valid boundary conditions."""
        with tempfile.TemporaryDirectory() as tmpdir:
            baseline = os.path.join(tmpdir, "baseline.png")
            current = os.path.join(tmpdir, "current.png")
            # Identical images
            img = Image.new("RGB", (100, 100), color="white")
            img.save(baseline)
            img.save(current)

            browser = Browser()
            # 0.0 is valid (exact match)
            res = browser.compare_screenshots(baseline, current, threshold=0.0)
            assert res.match is True

            # 100.0 is valid (always matches)
            res = browser.compare_screenshots(baseline, current, threshold=100.0)
            assert res.match is True
