# 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 importlib.util
import io
import pickle
import sys
import types
from pathlib import Path

import numpy as np
import pytest


def _load_latexocr_module(monkeypatch):
    module_name = "ppocr.data.latexocr_dataset"
    module_path = (
        Path(__file__).resolve().parents[2] / "ppocr" / "data" / "latexocr_dataset.py"
    )

    ppocr_pkg = types.ModuleType("ppocr")
    ppocr_pkg.__path__ = []
    data_pkg = types.ModuleType("ppocr.data")
    data_pkg.__path__ = []
    imaug_pkg = types.ModuleType("ppocr.data.imaug")
    imaug_pkg.__path__ = []
    label_ops_mod = types.ModuleType("ppocr.data.imaug.label_ops")

    class _Tokenizer:
        def __init__(self, *_args, **_kwargs):
            pass

    label_ops_mod.LatexOCRLabelEncode = _Tokenizer
    imaug_pkg.transform = lambda data, ops: data
    imaug_pkg.create_operators = lambda transforms, global_config: []

    paddle_mod = types.ModuleType("paddle")
    paddle_io_mod = types.ModuleType("paddle.io")

    class _Dataset:
        pass

    paddle_io_mod.Dataset = _Dataset
    paddle_mod.io = paddle_io_mod
    paddle_mod.arange = lambda n: list(range(n))
    paddle_mod.randperm = lambda n: list(range(n))

    for name, module in {
        "ppocr": ppocr_pkg,
        "ppocr.data": data_pkg,
        "ppocr.data.imaug": imaug_pkg,
        "ppocr.data.imaug.label_ops": label_ops_mod,
        "paddle": paddle_mod,
        "paddle.io": paddle_io_mod,
        "cv2": types.ModuleType("cv2"),
    }.items():
        monkeypatch.setitem(sys.modules, name, module)

    spec = importlib.util.spec_from_file_location(module_name, module_path)
    module = importlib.util.module_from_spec(spec)
    assert spec.loader is not None
    monkeypatch.setitem(sys.modules, module_name, module)
    spec.loader.exec_module(module)
    return module


class _Payload:
    def __init__(self, marker_path: Path):
        self.marker_path = marker_path

    def __reduce__(self):
        expression = (
            f"(__import__('pathlib').Path({self.marker_path.as_posix()!r}).write_text('pwned'), "
            "{(32, 32): [('eq', 'img.png')]})[1]"
        )
        return (eval, (expression,))


@pytest.fixture
def latexocr_config(tmp_path):
    return {
        "Global": {
            "max_seq_len": 16,
            "rec_char_dict_path": str(tmp_path / "dict.txt"),
        },
        "Train": {
            "dataset": {
                "data": str(tmp_path / "dataset.pkl"),
                "data_dir": str(tmp_path),
                "min_dimensions": [1, 1],
                "max_dimensions": [128, 128],
                "batch_size_per_pair": 1,
                "keep_smaller_batches": True,
                "transforms": [],
            },
            "loader": {"shuffle": False},
        },
    }


def test_latexocr_dataset_rejects_executable_pickle_payload(
    tmp_path, latexocr_config, monkeypatch
):
    module = _load_latexocr_module(monkeypatch)
    marker_path = tmp_path / "marker.txt"
    payload_path = Path(latexocr_config["Train"]["dataset"]["data"])
    payload_path.write_bytes(pickle.dumps(_Payload(marker_path)))

    with pytest.raises(pickle.UnpicklingError):
        module.LaTeXOCRDataSet(latexocr_config, "Train", logger=types.SimpleNamespace())

    assert not marker_path.exists()


def test_latexocr_dataset_accepts_basic_dict_payload(
    tmp_path, latexocr_config, monkeypatch
):
    module = _load_latexocr_module(monkeypatch)
    payload_path = Path(latexocr_config["Train"]["dataset"]["data"])
    payload_path.write_bytes(pickle.dumps({(32, 32): [("eq", "img.png")]}))

    dataset = module.LaTeXOCRDataSet(
        latexocr_config, "Train", logger=types.SimpleNamespace()
    )

    assert dataset.size == 1
    assert dataset.data == {(32, 32): [("eq", "img.png")]}
