"""Tests for Image Input Pipeline (F05)."""


import cv2
import numpy as np
import pytest

from gps_denied.core.pipeline import ImageInputPipeline, QueueFullError
from gps_denied.schemas.image import ImageBatch


@pytest.fixture
def pipeline(tmp_path):
    storage = str(tmp_path / "images")
    return ImageInputPipeline(storage_dir=storage, max_queue_size=2)


def test_batch_validation(pipeline):
    # VO-05: minimum batch size is now 1 (not 10)
    # Zero images is still invalid
    b0 = ImageBatch(images=[], filenames=[], start_sequence=1, end_sequence=0, batch_number=1)
    val0 = pipeline.validate_batch(b0)
    assert not val0.valid
    assert "Batch is empty" in val0.errors

    # Single image is now valid
    b1 = ImageBatch(images=[b"fake"], filenames=["AD000001.jpg"], start_sequence=1, end_sequence=1, batch_number=1)
    val1 = pipeline.validate_batch(b1)
    assert val1.valid, f"Single-image batch should be valid; errors: {val1.errors}"

    # 2-image batch — also valid under new rule
    b2 = ImageBatch(
        images=[b"1", b"2"], filenames=["AD000001.jpg", "AD000002.jpg"],
        start_sequence=1, end_sequence=2, batch_number=1,
    )
    val2 = pipeline.validate_batch(b2)
    assert val2.valid

    # Large valid batch
    fake_imgs = [b"fake"] * 10
    fake_names = [f"AD{i:06d}.jpg" for i in range(1, 11)]
    b10 = ImageBatch(images=fake_imgs, filenames=fake_names, start_sequence=1, end_sequence=10, batch_number=1)
    val10 = pipeline.validate_batch(b10)
    assert val10.valid


@pytest.mark.asyncio
async def test_queue_and_process(pipeline):
    flight_id = "test_f1"

    # Create valid fake images
    fake_img_np = np.zeros((10, 10, 3), dtype=np.uint8)
    _, encoded = cv2.imencode(".jpg", fake_img_np)
    fake_bytes = encoded.tobytes()

    fake_imgs = [fake_bytes] * 10
    fake_names = [f"AD{i:06d}.jpg" for i in range(1, 11)]
    b = ImageBatch(images=fake_imgs, filenames=fake_names, start_sequence=1, end_sequence=10, batch_number=1)

    pipeline.queue_batch(flight_id, b)

    # Process
    processed = await pipeline.process_next_batch(flight_id)
    assert processed is not None
    assert len(processed.images) == 10
    assert processed.images[0].sequence == 1
    assert processed.images[-1].sequence == 10

    # Status
    st = pipeline.get_processing_status(flight_id)
    assert st.total_images == 10
    assert st.processed_images == 10

    # Sequential get
    next_img = pipeline.get_next_image(flight_id)
    assert next_img is not None
    assert next_img.sequence == 1

    # Second get
    next_img2 = pipeline.get_next_image(flight_id)
    assert next_img2 is not None
    assert next_img2.sequence == 2


@pytest.mark.asyncio
async def test_exact_sequence_lookup_no_collision(pipeline, tmp_path):
    """VO-05: sequence 1 must NOT match AD000011.jpg or AD000010.jpg."""
    flight_id = "exact_test"
    fake_img_np = np.zeros((10, 10, 3), dtype=np.uint8)
    _, encoded = cv2.imencode(".jpg", fake_img_np)
    fake_bytes = encoded.tobytes()

    # Sequences 1 and 11 stored in the same flight
    names = ["AD000001.jpg", "AD000011.jpg"]
    imgs = [fake_bytes, fake_bytes]
    b = ImageBatch(images=imgs, filenames=names, start_sequence=1, end_sequence=11, batch_number=1)
    pipeline.queue_batch(flight_id, b)
    await pipeline.process_next_batch(flight_id)

    img1 = pipeline.get_image_by_sequence(flight_id, 1)
    img11 = pipeline.get_image_by_sequence(flight_id, 11)

    assert img1 is not None
    assert img1.filename == "AD000001.jpg", f"Expected AD000001.jpg, got {img1.filename}"
    assert img11 is not None
    assert img11.filename == "AD000011.jpg", f"Expected AD000011.jpg, got {img11.filename}"


def test_queue_full(pipeline):
    flight_id = "test_full"
    fake_imgs = [b"fake"] * 10
    fake_names = [f"AD{i:06d}.jpg" for i in range(1, 11)]
    b = ImageBatch(images=fake_imgs, filenames=fake_names, start_sequence=1, end_sequence=10, batch_number=1)

    pipeline.queue_batch(flight_id, b)
    pipeline.queue_batch(flight_id, b)

    with pytest.raises(QueueFullError):
        pipeline.queue_batch(flight_id, b)
