"""Test fixtures for dataset detection tests."""

import json
from pathlib import Path

import pytest


@pytest.fixture
def yolo_detection_dataset(tmp_path: Path) -> Path:
    """Create a valid YOLO detection dataset.

    Structure:
        dataset/
        ├── data.yaml
        ├── images/
        │   ├── train/
        │   │   └── img001.jpg
        │   └── val/
        │       └── img002.jpg
        └── labels/
            ├── train/
            │   └── img001.txt
            └── val/
                └── img002.txt
    """
    dataset_path = tmp_path / "yolo_detection"
    dataset_path.mkdir()

    # Create data.yaml
    yaml_content = """
path: .
train: images/train
val: images/val
names:
  0: person
  1: car
  2: bicycle
"""
    (dataset_path / "data.yaml").write_text(yaml_content)

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "images" / "val").mkdir(parents=True)
    (dataset_path / "labels" / "train").mkdir(parents=True)
    (dataset_path / "labels" / "val").mkdir(parents=True)

    # Create dummy images (just empty files for testing)
    (dataset_path / "images" / "train" / "img001.jpg").write_bytes(b"fake image")
    (dataset_path / "images" / "val" / "img002.jpg").write_bytes(b"fake image")

    # Create detection labels (5 columns: class x_center y_center width height)
    train_label = "0 0.5 0.5 0.2 0.3\n1 0.3 0.7 0.1 0.2\n"
    (dataset_path / "labels" / "train" / "img001.txt").write_text(train_label)
    (dataset_path / "labels" / "val" / "img002.txt").write_text("2 0.6 0.4 0.15 0.25\n")

    return dataset_path


@pytest.fixture
def yolo_segmentation_dataset(tmp_path: Path) -> Path:
    """Create a valid YOLO segmentation dataset.

    Labels have polygon coordinates (>5 columns).
    """
    dataset_path = tmp_path / "yolo_segmentation"
    dataset_path.mkdir()

    # Create dataset.yml (different naming)
    yaml_content = """
path: .
train: images/train
val: images/val
names:
  0: cat
  1: dog
"""
    (dataset_path / "dataset.yml").write_text(yaml_content)

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "images" / "val").mkdir(parents=True)
    (dataset_path / "labels" / "train").mkdir(parents=True)
    (dataset_path / "labels" / "val").mkdir(parents=True)

    # Create dummy images
    (dataset_path / "images" / "train" / "img001.jpg").write_bytes(b"fake image")
    (dataset_path / "images" / "val" / "img002.jpg").write_bytes(b"fake image")

    # Create segmentation labels (polygon: class x1 y1 x2 y2 x3 y3 ...)
    (dataset_path / "labels" / "train" / "img001.txt").write_text(
        "0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1\n"
    )
    (dataset_path / "labels" / "val" / "img002.txt").write_text(
        "1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9\n"
    )

    return dataset_path


@pytest.fixture
def yolo_flat_structure_dataset(tmp_path: Path) -> Path:
    """Create a YOLO dataset with flat structure (no train/val subfolders).

    Structure:
        dataset/
        ├── data.yaml
        ├── images/
        │   └── img001.jpg
        └── labels/
            └── img001.txt
    """
    dataset_path = tmp_path / "yolo_flat"
    dataset_path.mkdir()

    # Create data.yaml
    yaml_content = """
names:
  0: object1
  1: object2
"""
    (dataset_path / "data.yaml").write_text(yaml_content)

    # Create flat structure
    (dataset_path / "images").mkdir()
    (dataset_path / "labels").mkdir()

    # Add files directly in images/labels
    (dataset_path / "images" / "img001.jpg").write_bytes(b"fake image")
    (dataset_path / "images" / "img002.png").write_bytes(b"fake image")
    (dataset_path / "labels" / "img001.txt").write_text("0 0.5 0.5 0.2 0.3\n")
    (dataset_path / "labels" / "img002.txt").write_text("1 0.3 0.7 0.1 0.2\n")

    return dataset_path


@pytest.fixture
def coco_detection_dataset(tmp_path: Path) -> Path:
    """Create a valid COCO detection dataset.

    Structure:
        dataset/
        ├── annotations/
        │   └── instances_train.json
        └── images/
            └── train/
                └── img001.jpg
    """
    dataset_path = tmp_path / "coco_detection"
    dataset_path.mkdir()

    # Create annotations directory
    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    # Create COCO annotation file
    coco_data = {
        "info": {"description": "Test dataset"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 640, "height": 480},
            {"id": 2, "file_name": "img002.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {
                "id": 1,
                "image_id": 1,
                "category_id": 1,
                "bbox": [100, 100, 200, 150],
                "area": 30000,
                "iscrowd": 0,
            },
            {
                "id": 2,
                "image_id": 2,
                "category_id": 2,
                "bbox": [50, 50, 100, 100],
                "area": 10000,
                "iscrowd": 0,
            },
        ],
        "categories": [
            {"id": 1, "name": "person", "supercategory": "human"},
            {"id": 2, "name": "car", "supercategory": "vehicle"},
        ],
    }

    (annotations_dir / "instances_train.json").write_text(json.dumps(coco_data))

    # Create images directory
    images_dir = dataset_path / "images" / "train"
    images_dir.mkdir(parents=True)
    (images_dir / "img001.jpg").write_bytes(b"fake image")
    (images_dir / "img002.jpg").write_bytes(b"fake image")

    return dataset_path


@pytest.fixture
def coco_segmentation_dataset(tmp_path: Path) -> Path:
    """Create a valid COCO segmentation dataset.

    Annotations include segmentation field.
    """
    dataset_path = tmp_path / "coco_segmentation"
    dataset_path.mkdir()

    # Create annotations directory
    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    # Create COCO annotation file with segmentation
    coco_data = {
        "info": {"description": "Test segmentation dataset"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {
                "id": 1,
                "image_id": 1,
                "category_id": 1,
                "bbox": [100, 100, 200, 150],
                "segmentation": [[100, 100, 300, 100, 300, 250, 100, 250]],
                "area": 30000,
                "iscrowd": 0,
            },
        ],
        "categories": [
            {"id": 1, "name": "cat", "supercategory": "animal"},
            {"id": 2, "name": "dog", "supercategory": "animal"},
            {"id": 3, "name": "bird", "supercategory": "animal"},
        ],
    }

    (annotations_dir / "instances_val.json").write_text(json.dumps(coco_data))

    # Create images directory
    images_dir = dataset_path / "images" / "val"
    images_dir.mkdir(parents=True)
    (images_dir / "img001.jpg").write_bytes(b"fake image")

    return dataset_path


@pytest.fixture
def coco_unsplit_dataset(tmp_path: Path) -> Path:
    """Create a COCO dataset with a single unsplit annotation file."""
    dataset_path = tmp_path / "coco_unsplit"
    dataset_path.mkdir()

    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    coco_data = {
        "info": {"description": "Unsplit dataset"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 640, "height": 480},
            {"id": 2, "file_name": "img002.jpg", "width": 640, "height": 480},
            {"id": 3, "file_name": "img003.jpg", "width": 640, "height": 480},
            {"id": 4, "file_name": "img004.jpg", "width": 640, "height": 480},
            {"id": 5, "file_name": "img005.jpg", "width": 640, "height": 480},
            {"id": 6, "file_name": "img006.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {"id": 1, "image_id": 1, "category_id": 1, "bbox": [100, 100, 50, 60]},
            {"id": 2, "image_id": 2, "category_id": 2, "bbox": [50, 50, 40, 30]},
            {"id": 3, "image_id": 3, "category_id": 1, "bbox": [10, 20, 30, 40]},
            {"id": 4, "image_id": 4, "category_id": 2, "bbox": [15, 25, 35, 45]},
            {"id": 5, "image_id": 5, "category_id": 1, "bbox": [20, 30, 10, 15]},
            {"id": 6, "image_id": 6, "category_id": 2, "bbox": [5, 10, 20, 25]},
        ],
        "categories": [
            {"id": 1, "name": "person", "supercategory": "human"},
            {"id": 2, "name": "car", "supercategory": "vehicle"},
        ],
    }

    (annotations_dir / "annotations.json").write_text(json.dumps(coco_data))

    images_dir = dataset_path / "images"
    images_dir.mkdir()
    for idx in range(1, 7):
        (images_dir / f"img{idx:03d}.jpg").write_bytes(b"fake image")

    return dataset_path


@pytest.fixture
def invalid_yaml_missing_names(tmp_path: Path) -> Path:
    """Create an invalid YOLO dataset (YAML missing 'names' key)."""
    dataset_path = tmp_path / "invalid_yaml"
    dataset_path.mkdir()

    # Create YAML without 'names' key
    yaml_content = """
path: .
train: images/train
val: images/val
"""
    (dataset_path / "data.yaml").write_text(yaml_content)

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "labels" / "train").mkdir(parents=True)

    return dataset_path


@pytest.fixture
def invalid_coco_missing_categories(tmp_path: Path) -> Path:
    """Create an invalid COCO dataset (JSON missing 'categories' key)."""
    dataset_path = tmp_path / "invalid_coco"
    dataset_path.mkdir()

    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    # Create JSON without 'categories' key
    coco_data = {
        "images": [{"id": 1, "file_name": "img001.jpg"}],
        "annotations": [{"id": 1, "image_id": 1, "bbox": [100, 100, 200, 150]}],
    }

    (annotations_dir / "instances_train.json").write_text(json.dumps(coco_data))

    return dataset_path


@pytest.fixture
def empty_directory(tmp_path: Path) -> Path:
    """Create an empty directory."""
    dataset_path = tmp_path / "empty"
    dataset_path.mkdir()
    return dataset_path


@pytest.fixture
def random_files_directory(tmp_path: Path) -> Path:
    """Create a directory with random non-dataset files."""
    dataset_path = tmp_path / "random"
    dataset_path.mkdir()

    # Create various non-dataset files
    (dataset_path / "readme.txt").write_text("This is a readme")
    (dataset_path / "config.json").write_text('{"key": "value"}')
    (dataset_path / "script.py").write_text("print('hello')")
    (dataset_path / "notes.yaml").write_text("notes:\n  - item1\n  - item2")

    return dataset_path


@pytest.fixture
def nested_datasets(tmp_path: Path) -> Path:
    """Create a directory structure with multiple nested datasets."""
    root_path = tmp_path / "nested"
    root_path.mkdir()

    # Create YOLO dataset at depth 1
    yolo_path = root_path / "yolo_data"
    yolo_path.mkdir()
    yaml_content = """
names:
  0: class1
  1: class2
"""
    (yolo_path / "data.yaml").write_text(yaml_content)
    (yolo_path / "images").mkdir()
    (yolo_path / "labels").mkdir()
    (yolo_path / "images" / "img.jpg").write_bytes(b"fake")
    (yolo_path / "labels" / "img.txt").write_text("0 0.5 0.5 0.2 0.3\n")

    # Create COCO dataset at depth 2
    coco_path = root_path / "subdir" / "coco_data"
    coco_path.mkdir(parents=True)
    annotations_dir = coco_path / "annotations"
    annotations_dir.mkdir()
    coco_data = {
        "images": [{"id": 1, "file_name": "img.jpg", "width": 100, "height": 100}],
        "annotations": [
            {"id": 1, "image_id": 1, "category_id": 1, "bbox": [0, 0, 50, 50]}
        ],
        "categories": [{"id": 1, "name": "obj"}],
    }
    (annotations_dir / "annotations.json").write_text(json.dumps(coco_data))

    return root_path


@pytest.fixture
def yolo_classification_dataset(tmp_path: Path) -> Path:
    """Create a valid YOLO classification dataset.

    Structure:
        dataset/
        └── images/
            ├── train/
            │   ├── cat/
            │   │   ├── img001.jpg
            │   │   └── img002.jpg
            │   └── dog/
            │       └── img001.jpg
            └── val/
                ├── cat/
                │   └── img001.jpg
                └── dog/
                    └── img001.jpg
    """
    dataset_path = tmp_path / "yolo_classification"
    dataset_path.mkdir()

    # Create directory structure with class subdirectories
    (dataset_path / "images" / "train" / "cat").mkdir(parents=True)
    (dataset_path / "images" / "train" / "dog").mkdir(parents=True)
    (dataset_path / "images" / "val" / "cat").mkdir(parents=True)
    (dataset_path / "images" / "val" / "dog").mkdir(parents=True)

    # Create dummy images in each class directory
    # Train split
    (dataset_path / "images" / "train" / "cat" / "img001.jpg").write_bytes(b"fake cat")
    (dataset_path / "images" / "train" / "cat" / "img002.jpg").write_bytes(b"fake cat")
    (dataset_path / "images" / "train" / "dog" / "img001.jpg").write_bytes(b"fake dog")

    # Val split
    (dataset_path / "images" / "val" / "cat" / "img001.jpg").write_bytes(b"fake cat")
    (dataset_path / "images" / "val" / "dog" / "img001.jpg").write_bytes(b"fake dog")

    return dataset_path


@pytest.fixture
def yolo_classification_multiclass_dataset(tmp_path: Path) -> Path:
    """Create a YOLO classification dataset with more classes.

    Structure:
        dataset/
        └── images/
            └── train/
                ├── class1/
                ├── class2/
                ├── class3/
                └── class4/
    """
    dataset_path = tmp_path / "yolo_cls_multiclass"
    dataset_path.mkdir()

    classes = ["apple", "banana", "cherry", "date"]

    for cls in classes:
        (dataset_path / "images" / "train" / cls).mkdir(parents=True)
        # Add varying number of images per class
        num_images = classes.index(cls) + 1
        for i in range(num_images):
            (dataset_path / "images" / "train" / cls / f"img{i:03d}.jpg").write_bytes(
                b"fake image"
            )

    return dataset_path


@pytest.fixture
def mask_dataset_grayscale(tmp_path: Path) -> Path:
    """Create a mask dataset with grayscale masks (no config file).

    Structure:
        dataset/
        ├── images/
        │   ├── train/
        │   │   └── img001.jpg
        │   └── val/
        │       └── img002.jpg
        └── masks/
            ├── train/
            │   └── img001.png
            └── val/
                └── img002.png
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_grayscale"
    dataset_path.mkdir()

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "images" / "val").mkdir(parents=True)
    (dataset_path / "masks" / "train").mkdir(parents=True)
    (dataset_path / "masks" / "val").mkdir(parents=True)

    # Create dummy images (100x100 RGB)
    img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    cv2.imwrite(str(dataset_path / "images" / "train" / "img001.jpg"), img)
    cv2.imwrite(str(dataset_path / "images" / "val" / "img002.jpg"), img)

    # Create grayscale masks (100x100)
    # Train mask: background (0), class 1, class 2
    train_mask = np.zeros((100, 100), dtype=np.uint8)
    train_mask[20:40, 20:40] = 1  # Class 1 region
    train_mask[60:80, 60:80] = 2  # Class 2 region
    cv2.imwrite(str(dataset_path / "masks" / "train" / "img001.png"), train_mask)

    # Val mask: background (0), class 1, some ignored (255)
    val_mask = np.zeros((100, 100), dtype=np.uint8)
    val_mask[30:50, 30:50] = 1  # Class 1 region
    val_mask[70:90, 70:90] = 255  # Ignored region
    cv2.imwrite(str(dataset_path / "masks" / "val" / "img002.png"), val_mask)

    return dataset_path


@pytest.fixture
def mask_dataset_rgb(tmp_path: Path) -> Path:
    """Create a mask dataset with RGB palette masks and config file.

    Structure:
        dataset/
        ├── classes.yaml
        ├── images/
        │   └── train/
        │       └── img001.jpg
        └── masks/
            └── train/
                └── img001.png
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_rgb"
    dataset_path.mkdir()

    # Create classes.yaml with RGB palette
    yaml_content = """
names:
  0: background
  1: person
  2: car

ignore_index: 255

palette:
  - color: [0, 0, 0]
    name: background
    id: 0
  - color: [220, 20, 60]
    name: person
    id: 1
  - color: [0, 0, 142]
    name: car
    id: 2
"""
    (dataset_path / "classes.yaml").write_text(yaml_content)

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "masks" / "train").mkdir(parents=True)

    # Create dummy image
    img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    cv2.imwrite(str(dataset_path / "images" / "train" / "img001.jpg"), img)

    # Create RGB mask (actually using grayscale encoding for simplicity)
    # The class config provides the mapping
    mask = np.zeros((100, 100), dtype=np.uint8)
    mask[20:40, 20:40] = 1  # Person region
    mask[60:80, 60:80] = 2  # Car region
    cv2.imwrite(str(dataset_path / "masks" / "train" / "img001.png"), mask)

    return dataset_path


@pytest.fixture
def mask_dataset_cityscapes(tmp_path: Path) -> Path:
    """Create a Cityscapes-style mask dataset.

    Structure:
        dataset/
        ├── classes.yaml
        ├── leftImg8bit/
        │   └── train/
        │       └── city001_000000_000000_leftImg8bit.png
        └── gtFine/
            └── train/
                └── city001_000000_000000_leftImg8bit.png
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_cityscapes"
    dataset_path.mkdir()

    # Create classes.yaml
    yaml_content = """
names:
  0: road
  1: sidewalk
  2: building

ignore_index: 255
"""
    (dataset_path / "classes.yaml").write_text(yaml_content)

    # Create Cityscapes-style directory structure
    (dataset_path / "leftImg8bit" / "train").mkdir(parents=True)
    (dataset_path / "gtFine" / "train").mkdir(parents=True)

    # Create image
    img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    cv2.imwrite(
        str(dataset_path / "leftImg8bit" / "train" / "city_000000_leftImg8bit.png"), img
    )

    # Create mask
    mask = np.zeros((100, 100), dtype=np.uint8)
    mask[0:30, :] = 2  # Building (top)
    mask[30:60, :] = 1  # Sidewalk (middle)
    mask[60:100, :] = 0  # Road (bottom)
    cv2.imwrite(
        str(dataset_path / "gtFine" / "train" / "city_000000_leftImg8bit.png"), mask
    )

    return dataset_path


@pytest.fixture
def mask_dataset_unsplit(tmp_path: Path) -> Path:
    """Create a mask dataset without train/val splits.

    Structure:
        dataset/
        ├── images/
        │   ├── img001.jpg
        │   └── img002.jpg
        └── masks/
            ├── img001.png
            └── img002.png
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_unsplit"
    dataset_path.mkdir()

    # Create flat directory structure
    (dataset_path / "images").mkdir()
    (dataset_path / "masks").mkdir()

    # Create images and masks
    for i in range(1, 3):
        img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
        cv2.imwrite(str(dataset_path / "images" / f"img{i:03d}.jpg"), img)

        mask = np.zeros((100, 100), dtype=np.uint8)
        mask[20:60, 20:60] = i  # Different class per image
        cv2.imwrite(str(dataset_path / "masks" / f"img{i:03d}.png"), mask)

    return dataset_path


@pytest.fixture
def mask_dataset_missing_mask(tmp_path: Path) -> Path:
    """Create a mask dataset where some images have no corresponding mask.

    Structure:
        dataset/
        ├── images/
        │   └── train/
        │       ├── img001.jpg (has mask)
        │       └── img002.jpg (no mask)
        └── masks/
            └── train/
                └── img001.png
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_missing"
    dataset_path.mkdir()

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "masks" / "train").mkdir(parents=True)

    # Create two images
    img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    cv2.imwrite(str(dataset_path / "images" / "train" / "img001.jpg"), img)
    cv2.imwrite(str(dataset_path / "images" / "train" / "img002.jpg"), img)

    # Only create mask for first image
    mask = np.zeros((100, 100), dtype=np.uint8)
    mask[20:60, 20:60] = 1
    cv2.imwrite(str(dataset_path / "masks" / "train" / "img001.png"), mask)

    return dataset_path


@pytest.fixture
def roboflow_coco_dataset(tmp_path: Path) -> Path:
    """Create a Roboflow COCO dataset with annotations in split directories.

    Structure:
        dataset/
        ├── train/
        │   ├── _annotations.coco.json
        │   ├── img001.jpg
        │   └── img002.jpg
        ├── valid/
        │   ├── _annotations.coco.json
        │   └── img003.jpg
        └── test/
            ├── _annotations.coco.json
            └── img004.jpg
    """
    dataset_path = tmp_path / "roboflow_coco"
    dataset_path.mkdir()

    # Create train split
    train_dir = dataset_path / "train"
    train_dir.mkdir()
    train_coco = {
        "info": {"description": "Roboflow train"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 640, "height": 480},
            {"id": 2, "file_name": "img002.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {"id": 1, "image_id": 1, "category_id": 1, "bbox": [100, 100, 50, 50]},
            {"id": 2, "image_id": 2, "category_id": 2, "bbox": [200, 200, 60, 60]},
        ],
        "categories": [
            {"id": 1, "name": "person", "supercategory": "human"},
            {"id": 2, "name": "car", "supercategory": "vehicle"},
        ],
    }
    (train_dir / "_annotations.coco.json").write_text(json.dumps(train_coco))
    (train_dir / "img001.jpg").write_bytes(b"fake train image 1")
    (train_dir / "img002.jpg").write_bytes(b"fake train image 2")

    # Create valid split
    valid_dir = dataset_path / "valid"
    valid_dir.mkdir()
    valid_coco = {
        "info": {"description": "Roboflow valid"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img003.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {"id": 1, "image_id": 1, "category_id": 1, "bbox": [150, 150, 40, 40]},
        ],
        "categories": [
            {"id": 1, "name": "person", "supercategory": "human"},
            {"id": 2, "name": "car", "supercategory": "vehicle"},
        ],
    }
    (valid_dir / "_annotations.coco.json").write_text(json.dumps(valid_coco))
    (valid_dir / "img003.jpg").write_bytes(b"fake valid image")

    # Create test split
    test_dir = dataset_path / "test"
    test_dir.mkdir()
    test_coco = {
        "info": {"description": "Roboflow test"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img004.jpg", "width": 640, "height": 480},
        ],
        "annotations": [
            {"id": 1, "image_id": 1, "category_id": 2, "bbox": [50, 50, 30, 30]},
        ],
        "categories": [
            {"id": 1, "name": "person", "supercategory": "human"},
            {"id": 2, "name": "car", "supercategory": "vehicle"},
        ],
    }
    (test_dir / "_annotations.coco.json").write_text(json.dumps(test_coco))
    (test_dir / "img004.jpg").write_bytes(b"fake test image")

    return dataset_path


@pytest.fixture
def roboflow_yolo_dataset(tmp_path: Path) -> Path:
    """Create a Roboflow YOLO dataset with {split}/images/ layout.

    Structure:
        dataset/
        ├── data.yaml
        ├── train/
        │   ├── images/
        │   │   ├── img001.jpg
        │   │   └── img002.jpg
        │   └── labels/
        │       ├── img001.txt
        │       └── img002.txt
        ├── valid/
        │   ├── images/
        │   │   └── img003.jpg
        │   └── labels/
        │       └── img003.txt
        └── test/
            ├── images/
            │   └── img004.jpg
            └── labels/
                └── img004.txt
    """
    dataset_path = tmp_path / "roboflow_yolo"
    dataset_path.mkdir()

    # Create data.yaml with Roboflow-style paths
    yaml_content = (
        "names:\n"
        "- ball\n"
        "- player\n"
        "- referee\n"
        "nc: 3\n"
        "train: ../train/images\n"
        "val: ../valid/images\n"
        "test: ../test/images\n"
    )
    (dataset_path / "data.yaml").write_text(yaml_content)

    # Train split
    (dataset_path / "train" / "images").mkdir(parents=True)
    (dataset_path / "train" / "labels").mkdir(parents=True)
    (dataset_path / "train" / "images" / "img001.jpg").write_bytes(b"fake image")
    (dataset_path / "train" / "images" / "img002.jpg").write_bytes(b"fake image")
    (dataset_path / "train" / "labels" / "img001.txt").write_text(
        "0 0.5 0.5 0.1 0.1\n1 0.3 0.3 0.2 0.4\n"
    )
    (dataset_path / "train" / "labels" / "img002.txt").write_text(
        "0 0.6 0.6 0.15 0.15\n"
    )

    # Valid split (Roboflow uses "valid" instead of "val")
    (dataset_path / "valid" / "images").mkdir(parents=True)
    (dataset_path / "valid" / "labels").mkdir(parents=True)
    (dataset_path / "valid" / "images" / "img003.jpg").write_bytes(b"fake image")
    (dataset_path / "valid" / "labels" / "img003.txt").write_text(
        "1 0.4 0.4 0.2 0.3\n2 0.7 0.7 0.1 0.1\n"
    )

    # Test split
    (dataset_path / "test" / "images").mkdir(parents=True)
    (dataset_path / "test" / "labels").mkdir(parents=True)
    (dataset_path / "test" / "images" / "img004.jpg").write_bytes(b"fake image")
    (dataset_path / "test" / "labels" / "img004.txt").write_text("")

    return dataset_path


@pytest.fixture
def coco_rle_dataset(tmp_path: Path) -> Path:
    """Create a COCO dataset with RLE segmentation annotations.

    Contains a 100x100 image with one RLE annotation (10x10 block
    at top-left in column-major order).

    Structure:
        dataset/
        ├── annotations/
        │   └── instances_train.json
        └── images/
            └── train/
                └── img001.jpg
    """
    import cv2 as _cv2
    import numpy as _np

    dataset_path = tmp_path / "coco_rle"
    dataset_path.mkdir()

    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    # RLE counts: column-major, alternating bg/fg runs
    # 10x10 block at rows 0-9, cols 0-9 on a 100x100 image
    # Column-major: first 10 pixels of each of the first 10 columns are fg
    counts = []
    for col in range(10):
        if col == 0:
            counts.append(0)  # 0 background pixels before first fg
            counts.append(10)  # 10 foreground pixels
        else:
            counts.append(90)  # 90 background pixels (rest of previous col)
            counts.append(10)  # 10 foreground pixels
    # Remaining pixels after the last fg run
    counts.append(100 * 100 - 10 * 100 + 90)  # rest of col 9 + cols 10-99

    coco_data = {
        "info": {"description": "RLE test dataset"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 100, "height": 100},
        ],
        "annotations": [
            {
                "id": 1,
                "image_id": 1,
                "category_id": 1,
                "bbox": [0, 0, 10, 10],
                "segmentation": {
                    "counts": counts,
                    "size": [100, 100],
                },
                "area": 100,
                "iscrowd": 0,
            },
        ],
        "categories": [
            {"id": 1, "name": "object", "supercategory": "thing"},
        ],
    }

    (annotations_dir / "instances_train.json").write_text(json.dumps(coco_data))

    images_dir = dataset_path / "images" / "train"
    images_dir.mkdir(parents=True)
    img = _np.random.randint(0, 255, (100, 100, 3), dtype=_np.uint8)
    _cv2.imwrite(str(images_dir / "img001.jpg"), img)

    return dataset_path


@pytest.fixture
def coco_mixed_rle_polygon_dataset(tmp_path: Path) -> Path:
    """Create a COCO dataset with both RLE and polygon annotations.

    Contains a 100x100 image with:
    - One RLE annotation (category 1): 10x10 block top-left
    - One polygon annotation (category 2): rectangle at rows 50-69, cols 50-69

    Structure:
        dataset/
        ├── annotations/
        │   └── instances_train.json
        └── images/
            └── train/
                └── img001.jpg
    """
    import cv2 as _cv2
    import numpy as _np

    dataset_path = tmp_path / "coco_mixed"
    dataset_path.mkdir()

    annotations_dir = dataset_path / "annotations"
    annotations_dir.mkdir()

    # RLE for 10x10 block at top-left (same as coco_rle_dataset)
    counts = []
    for col in range(10):
        if col == 0:
            counts.append(0)
            counts.append(10)
        else:
            counts.append(90)
            counts.append(10)
    counts.append(100 * 100 - 10 * 100 + 90)

    coco_data = {
        "info": {"description": "Mixed RLE+polygon test dataset"},
        "licenses": [],
        "images": [
            {"id": 1, "file_name": "img001.jpg", "width": 100, "height": 100},
        ],
        "annotations": [
            {
                "id": 1,
                "image_id": 1,
                "category_id": 1,
                "bbox": [0, 0, 10, 10],
                "segmentation": {
                    "counts": counts,
                    "size": [100, 100],
                },
                "area": 100,
                "iscrowd": 0,
            },
            {
                "id": 2,
                "image_id": 1,
                "category_id": 2,
                "bbox": [50, 50, 20, 20],
                "segmentation": [[50, 50, 70, 50, 70, 70, 50, 70]],
                "area": 400,
                "iscrowd": 0,
            },
        ],
        "categories": [
            {"id": 1, "name": "cat", "supercategory": "animal"},
            {"id": 2, "name": "dog", "supercategory": "animal"},
        ],
    }

    (annotations_dir / "instances_train.json").write_text(json.dumps(coco_data))

    images_dir = dataset_path / "images" / "train"
    images_dir.mkdir(parents=True)
    img = _np.random.randint(0, 255, (100, 100, 3), dtype=_np.uint8)
    _cv2.imwrite(str(images_dir / "img001.jpg"), img)

    return dataset_path


@pytest.fixture
def coco_relative_path_rle_dataset(tmp_path: Path) -> Path:
    """Create a COCO RLE dataset where file_name contains a relative path prefix.

    The COCO spec allows ``file_name`` to be a relative path like
    ``"images/img001.jpg"`` rather than a bare filename. This fixture
    verifies that ``load_mask`` and ``get_annotations_for_image`` still
    match correctly.

    Structure:
        dataset/
        └── train/
            ├── _annotations.coco.json   (file_name: "images/img001.jpg")
            └── images/
                └── img001.jpg
    """
    import cv2 as _cv2
    import numpy as _np

    dataset_path = tmp_path / "coco_roboflow_rle"
    dataset_path.mkdir()

    train_dir = dataset_path / "train"
    train_dir.mkdir()

    counts = []
    for col in range(10):
        if col == 0:
            counts.append(0)
            counts.append(10)
        else:
            counts.append(90)
            counts.append(10)
    counts.append(100 * 100 - 10 * 100 + 90)

    coco_data = {
        "info": {"description": "Roboflow RLE test dataset"},
        "licenses": [],
        "images": [
            {
                "id": 1,
                "file_name": "images/img001.jpg",
                "width": 100,
                "height": 100,
            },
        ],
        "annotations": [
            {
                "id": 1,
                "image_id": 1,
                "category_id": 1,
                "bbox": [0, 0, 10, 10],
                "segmentation": {"counts": counts, "size": [100, 100]},
                "area": 100,
                "iscrowd": 0,
            },
        ],
        "categories": [
            {"id": 1, "name": "object", "supercategory": "thing"},
        ],
    }

    (train_dir / "_annotations.coco.json").write_text(json.dumps(coco_data))

    images_dir = train_dir / "images"
    images_dir.mkdir()
    img = _np.random.randint(0, 255, (100, 100, 3), dtype=_np.uint8)
    _cv2.imwrite(str(images_dir / "img001.jpg"), img)

    return dataset_path


@pytest.fixture
def mask_dataset_dimension_mismatch(tmp_path: Path) -> Path:
    """Create a mask dataset where image and mask have different dimensions.

    Structure:
        dataset/
        ├── images/
        │   └── train/
        │       └── img001.jpg (100x100)
        └── masks/
            └── train/
                └── img001.png (50x50)
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "mask_mismatch"
    dataset_path.mkdir()

    # Create directory structure
    (dataset_path / "images" / "train").mkdir(parents=True)
    (dataset_path / "masks" / "train").mkdir(parents=True)

    # Create 100x100 image
    img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
    cv2.imwrite(str(dataset_path / "images" / "train" / "img001.jpg"), img)

    # Create 50x50 mask (mismatch)
    mask = np.zeros((50, 50), dtype=np.uint8)
    mask[10:30, 10:30] = 1
    cv2.imwrite(str(dataset_path / "masks" / "train" / "img001.png"), mask)

    return dataset_path


# ---------------------------------------------------------------------------
# Real-image fixtures for ultralytics cross-validation tests
# ---------------------------------------------------------------------------


@pytest.fixture
def yolo_real_detection_dataset(tmp_path: Path) -> Path:
    """Create a YOLO detection dataset with real images for ultralytics.

    Includes:
        - train/img001: 2 annotations (class 0, class 1)
        - train/img002: 1 annotation  (class 0)
        - train/img003: empty label file (background)
        - train/img004: no label file at all (missing label)
        - val/img005: 1 annotation  (class 2)
        - val/img006: empty label file (background)
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "yolo_real_det"
    dataset_path.mkdir()

    # data.yaml with absolute path (required by ultralytics)
    yaml_content = (
        f"path: {dataset_path}\n"
        "train: images/train\n"
        "val: images/val\n"
        "names:\n"
        "  0: person\n"
        "  1: car\n"
        "  2: bicycle\n"
    )
    (dataset_path / "data.yaml").write_text(yaml_content)

    for split in ("train", "val"):
        (dataset_path / "images" / split).mkdir(parents=True)
        (dataset_path / "labels" / split).mkdir(parents=True)

    # Helper to write a 100x100 image
    def _write_img(rel: str) -> None:
        img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
        cv2.imwrite(str(dataset_path / rel), img)

    # --- train split ---
    _write_img("images/train/img001.jpg")
    (dataset_path / "labels/train/img001.txt").write_text(
        "0 0.5 0.5 0.2 0.3\n1 0.3 0.7 0.1 0.2\n"
    )

    _write_img("images/train/img002.jpg")
    (dataset_path / "labels/train/img002.txt").write_text("0 0.6 0.4 0.15 0.25\n")

    _write_img("images/train/img003.jpg")
    (dataset_path / "labels/train/img003.txt").write_text("")  # background

    _write_img("images/train/img004.jpg")
    # No label file for img004 — ultralytics sees this as background

    # --- val split ---
    _write_img("images/val/img005.jpg")
    (dataset_path / "labels/val/img005.txt").write_text("2 0.5 0.5 0.2 0.3\n")

    _write_img("images/val/img006.jpg")
    (dataset_path / "labels/val/img006.txt").write_text("")  # background

    return dataset_path


@pytest.fixture
def yolo_real_segmentation_dataset(tmp_path: Path) -> Path:
    """Create a YOLO segmentation dataset with real images for ultralytics.

    Includes polygon labels (>5 columns per line).
    """
    import cv2
    import numpy as np

    dataset_path = tmp_path / "yolo_real_seg"
    dataset_path.mkdir()

    yaml_content = (
        f"path: {dataset_path}\n"
        "train: images/train\n"
        "val: images/val\n"
        "names:\n"
        "  0: cat\n"
        "  1: dog\n"
    )
    (dataset_path / "data.yaml").write_text(yaml_content)

    for split in ("train", "val"):
        (dataset_path / "images" / split).mkdir(parents=True)
        (dataset_path / "labels" / split).mkdir(parents=True)

    def _write_img(rel: str) -> None:
        img = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
        cv2.imwrite(str(dataset_path / rel), img)

    # train
    _write_img("images/train/img001.jpg")
    (dataset_path / "labels/train/img001.txt").write_text(
        "0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1\n"
    )

    _write_img("images/train/img002.jpg")
    (dataset_path / "labels/train/img002.txt").write_text(
        "1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9\n"
    )

    # val
    _write_img("images/val/img003.jpg")
    (dataset_path / "labels/val/img003.txt").write_text(
        "0 0.15 0.25 0.35 0.45 0.55 0.65\n1 0.2 0.3 0.4 0.5 0.6 0.7\n"
    )

    return dataset_path
