# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import random
import sys

import numpy as np
import pytest

pytestmark = pytest.mark.py38_incompatible
# py3.8 lmdb wheel exposes an undefined Py_SET_REFCNT symbol and fails to
# load. Skip collection only on py3.8; on py3.9+ a lmdb import failure is
# a real regression and must surface rather than be silently skipped.
if sys.version_info < (3, 9):
    pytest.importorskip("lmdb")

from ppocr.data.imaug.iaa_augment import IaaAugment

# Set a fixed random seed to ensure test reproducibility
np.random.seed(42)
random.seed(42)


# Fixture to provide a sample image for tests
@pytest.fixture
def sample_image():
    # Create a 100x100 pixel dummy image with 3 color channels (RGB)
    return np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)


# Fixture to provide sample polygons for tests
@pytest.fixture
def sample_polys():
    # Create dummy polygons as sample data
    polys = [
        np.array([[10, 10], [20, 10], [20, 20], [10, 20]], dtype=np.float32),
        np.array([[30, 30], [40, 30], [40, 40], [30, 40]], dtype=np.float32),
    ]
    return polys


# Helper function to create a data dictionary for testing
def create_data(sample_image, sample_polys):
    return {
        "image": sample_image.copy(),
        "polys": [poly.copy() for poly in sample_polys],
    }


# Test the default behavior of the augmenter (without specified arguments)
def test_iaa_augment_default(sample_image, sample_polys):
    data = create_data(sample_image, sample_polys)
    augmenter = IaaAugment()
    transformed_data = augmenter(data)

    # Check the data types and structure of the transformed image and polygons
    assert isinstance(
        transformed_data["image"], np.ndarray
    ), "Image should be a numpy array"
    assert isinstance(
        transformed_data["polys"], np.ndarray
    ), "Polys should be a numpy array"
    assert transformed_data["image"].ndim == 3, "Image should be 3-dimensional"

    # Verify that the polygons have been transformed
    polys_changed = any(
        not np.allclose(orig_poly, trans_poly)
        for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
    )
    assert polys_changed, "Polygons should have been transformed"


# Test the augmenter with empty arguments, meaning no transformations should occur
def test_iaa_augment_none(sample_image, sample_polys):
    data = create_data(sample_image, sample_polys)
    augmenter = IaaAugment(augmenter_args=[])
    transformed_data = augmenter(data)

    # Check that the image and polygons remain unchanged
    assert np.array_equal(
        data["image"], transformed_data["image"]
    ), "Image should be unchanged"
    for orig_poly, transformed_poly in zip(data["polys"], transformed_data["polys"]):
        assert np.array_equal(
            orig_poly, transformed_poly
        ), "Polygons should be unchanged"


# Parameterized test to check various augmenter arguments and expected image shapes
@pytest.mark.parametrize(
    "augmenter_args, expected_shape",
    [
        ([], (100, 100, 3)),
        ([{"type": "Resize", "args": {"size": [0.5, 0.5]}}], (50, 50, 3)),
        ([{"type": "Resize", "args": {"size": [2.0, 2.0]}}], (200, 200, 3)),
    ],
)
def test_iaa_augment_resize(sample_image, sample_polys, augmenter_args, expected_shape):
    data = create_data(sample_image, sample_polys)
    augmenter = IaaAugment(augmenter_args=augmenter_args)
    transformed_data = augmenter(data)

    # Verify that the transformed image has the expected shape
    assert (
        transformed_data["image"].shape == expected_shape
    ), f"Expected image shape {expected_shape}, got {transformed_data['image'].shape}"


# Test custom augmenter arguments with specific transformations
def test_iaa_augment_custom(sample_image, sample_polys):
    data = create_data(sample_image, sample_polys)
    augmenter_args = [
        {"type": "Affine", "args": {"rotate": [45, 45]}},  # Apply 45-degree rotation
        {"type": "Resize", "args": {"size": [0.5, 0.5]}},
    ]
    augmenter = IaaAugment(augmenter_args=augmenter_args)
    transformed_data = augmenter(data)

    # Check the expected image dimensions after resizing
    expected_height = int(sample_image.shape[0] * 0.5)
    expected_width = int(sample_image.shape[1] * 0.5)
    assert (
        transformed_data["image"].shape[0] == expected_height
    ), "Image height should be scaled by 0.5"
    assert (
        transformed_data["image"].shape[1] == expected_width
    ), "Image width should be scaled by 0.5"

    # Verify that the polygons have been transformed
    polys_changed = any(
        not np.allclose(orig_poly, trans_poly)
        for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
    )
    assert polys_changed, "Polygons should have been transformed"


def test_iaa_augment_affine_fit_output(sample_image, sample_polys):
    data = create_data(sample_image, sample_polys)
    augmenter_args = [
        {"type": "Affine", "args": {"rotate": [45, 45], "fit_output": True}},
    ]
    augmenter = IaaAugment(augmenter_args=augmenter_args)
    transformed_data = augmenter(data)

    assert transformed_data["image"].shape[0] > sample_image.shape[0]
    assert transformed_data["image"].shape[1] > sample_image.shape[1]


# Test that an unknown transformation type raises an AttributeError
def test_iaa_augment_unknown_transform():
    augmenter_args = [{"type": "UnknownTransform", "args": {}}]
    with pytest.raises(AttributeError):
        IaaAugment(augmenter_args=augmenter_args)


# Test that an invalid resize size parameter raises a ValueError
def test_iaa_augment_invalid_resize_size(sample_image, sample_polys):
    augmenter_args = [{"type": "Resize", "args": {"size": "invalid_size"}}]
    with pytest.raises(ValueError) as exc_info:
        IaaAugment(augmenter_args=augmenter_args)
    assert "'size' must be a list or tuple of two numbers" in str(exc_info.value)


# Test that polygons are transformed as expected
def test_iaa_augment_polys_transformation(sample_image, sample_polys):
    data = create_data(sample_image, sample_polys)
    augmenter_args = [
        {"type": "Affine", "args": {"rotate": [90, 90]}},  # Apply 90-degree rotation
    ]
    augmenter = IaaAugment(augmenter_args=augmenter_args)
    transformed_data = augmenter(data)

    # Verify that the polygons have been transformed
    polys_changed = any(
        not np.allclose(orig_poly, trans_poly)
        for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
    )
    assert polys_changed, "Polygons should have been transformed"


# Test multiple transformations applied to the augmenter
def test_iaa_augment_multiple_transforms(sample_image, sample_polys):
    augmenter_args = [
        {"type": "Fliplr", "args": {"p": 1.0}},  # Always apply horizontal flip
        {"type": "Affine", "args": {"shear": 10}},
    ]
    data = create_data(sample_image, sample_polys)
    augmenter = IaaAugment(augmenter_args=augmenter_args)
    transformed_data = augmenter(data)

    # Ensure the image has been transformed
    images_different = not np.array_equal(transformed_data["image"], sample_image)
    assert images_different, "Image should be transformed"

    # Ensure the polygons have been transformed
    polys_changed = any(
        not np.allclose(orig_poly, trans_poly)
        for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
    )
    assert polys_changed, "Polygons should have been transformed"
