# ------------------------------------------------------------------------
# RF-DETR
# Copyright (c) 2025 Roboflow. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
# ------------------------------------------------------------------------
import shutil
from pathlib import Path
from typing import Any, Generator

import pytest

# NOTE: Model weights (rf-detr-*.pth) download to the CWD (typically tests/ when running pytest).
# This is a known limitation. Route: change pretrain_weights default to
# platformdirs.user_cache_dir("rfdetr") in a future PR.
from rfdetr.datasets.synthetic import DatasetSplitRatios, generate_coco_dataset
from rfdetr.utilities.reproducibility import seed_all


@pytest.fixture(scope="session", autouse=True)
def _prewarm_dinov2_cache() -> None:
    """Download DINOv2 backbone weights once per test session.

    HuggingFace hub uses file-level locking internally, so concurrent xdist
    workers block on each other rather than issuing duplicate network requests.
    After the first worker finishes, all others read from the local disk cache.

    Examples:
        This fixture is autouse — no explicit reference needed in tests.
    """
    import os

    if os.environ.get("RFDETR_SKIP_DINOV2_PREWARM") == "1":
        return

    from huggingface_hub import snapshot_download

    try:
        snapshot_download(
            "facebook/dinov2-with-registers-base",
            ignore_patterns=["*.msgpack", "flax_model*", "tf_model*", "rust_model*"],
        )
    except Exception as exc:
        pytest.skip(f"Skipping DINOv2 cache prewarm (snapshot_download failed): {exc}")


@pytest.fixture(autouse=True)
def reset_random_seeds() -> None:
    """Reset all RNG sources before every test for reproducibility."""
    seed_all()


@pytest.fixture(scope="session")
def synthetic_shape_dataset_dir(tmp_path_factory: pytest.TempPathFactory) -> Generator[Path, Any, None]:
    """Build a synthetic COCO-style dataset on disk and clean it up after tests.

    Args:
        tmp_path_factory: Pytest factory for temporary directories.

    Yields:
        Path to the synthetic dataset directory.
    """
    seed_all()
    dataset_dir = tmp_path_factory.mktemp("synthetic_dataset")
    generate_coco_dataset(
        output_dir=str(dataset_dir),
        num_images=100,
        img_size=224,
        class_mode="shape",
        min_objects=3,
        max_objects=7,
        split_ratios=DatasetSplitRatios(train=0.8, val=0.2, test=0.0),
    )
    val_dir = dataset_dir / "val"
    valid_dir = dataset_dir / "valid"
    if val_dir.exists() and not valid_dir.exists():
        val_dir.rename(valid_dir)
    test_dir = dataset_dir / "test"
    if not test_dir.exists():
        test_dir.mkdir(parents=True, exist_ok=True)
        (test_dir / "_annotations.coco.json").write_text((valid_dir / "_annotations.coco.json").read_text())
        # Ensure test split has corresponding images referenced by the annotations
        for item in valid_dir.iterdir():
            if item.is_file() and item.name != "_annotations.coco.json":
                shutil.copy2(item, test_dir / item.name)
    yield dataset_dir
    shutil.rmtree(dataset_dir)


@pytest.fixture(scope="session")
def synthetic_shape_segmentation_dataset_dir(
    tmp_path_factory: pytest.TempPathFactory,
) -> Generator[Path, Any, None]:
    """Build a synthetic COCO-style dataset with polygon segmentation annotations.

    Same layout as :func:`synthetic_shape_dataset_dir` but every annotation includes a ``segmentation`` polygon field so
    the dataset can be used to train or evaluate segmentation models.

    Args:
        tmp_path_factory: Pytest factory for temporary directories.

    Yields:
        Path to the synthetic segmentation dataset directory.
    """
    seed_all()
    dataset_dir = tmp_path_factory.mktemp("synthetic_seg_dataset")
    generate_coco_dataset(
        output_dir=str(dataset_dir),
        num_images=100,
        img_size=224,
        class_mode="shape",
        min_objects=3,
        max_objects=7,
        split_ratios=DatasetSplitRatios(train=0.8, val=0.2, test=0.0),
        with_segmentation=True,
    )
    val_dir = dataset_dir / "val"
    valid_dir = dataset_dir / "valid"
    if val_dir.exists() and not valid_dir.exists():
        val_dir.rename(valid_dir)
    test_dir = dataset_dir / "test"
    if not test_dir.exists():
        test_dir.mkdir(parents=True, exist_ok=True)
        (test_dir / "_annotations.coco.json").write_text((valid_dir / "_annotations.coco.json").read_text())
        for item in valid_dir.iterdir():
            if item.is_file() and item.name != "_annotations.coco.json":
                shutil.copy2(item, test_dir / item.name)
    yield dataset_dir
    shutil.rmtree(dataset_dir)
