from unittest.mock import AsyncMock, patch
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

# Bytes JPEG minimaux valides (header JFIF)
FAKE_IMAGE = (
    b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"
    b"\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t"
    b"\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a"
    b"\x1f\x1e\x1d\x1a\x1c\x1c $.' \",#\x1c\x1c(7),01444\x1f'9=82<.342\x1e"
    b"\xff\xc0\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11\x00"
    b"\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00"
    b"\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b"
    b"\xff\xda\x00\x08\x01\x01\x00\x00?\x00\xf5\x00\xff\xd9"
)


def test_identify_returns_plantnet_results():
    """PlantNet disponible → retourne ses résultats avec source=plantnet."""
    fake_results = [
        {
            "species": "Solanum lycopersicum",
            "common_name": "Tomate",
            "confidence": 0.95,
            "image_url": "",
        }
    ]
    with (
        patch("app.services.redis_cache.get", return_value=None),
        patch("app.services.redis_cache.set"),
        patch(
            "app.services.plantnet.identify",
            new_callable=AsyncMock,
            return_value=fake_results,
        ),
    ):
        resp = client.post(
            "/api/identify",
            files={"file": ("test.jpg", FAKE_IMAGE, "image/jpeg")},
        )
    assert resp.status_code == 200
    data = resp.json()
    assert data["source"] == "plantnet"
    assert len(data["results"]) == 1
    assert data["results"][0]["species"] == "Solanum lycopersicum"
    assert data["results"][0]["confidence"] == 0.95


def test_identify_falls_back_to_yolo_when_plantnet_fails():
    """PlantNet indisponible (retourne []) → fallback YOLO avec source=yolo."""
    fake_yolo = [
        {
            "species": "Tomato healthy",
            "common_name": "Tomate",
            "confidence": 0.80,
            "image_url": "",
        }
    ]
    with (
        patch("app.services.redis_cache.get", return_value=None),
        patch("app.services.redis_cache.set"),
        patch(
            "app.services.plantnet.identify",
            new_callable=AsyncMock,
            return_value=[],
        ),
        patch(
            "app.services.yolo_service.identify",
            new_callable=AsyncMock,
            return_value=fake_yolo,
        ),
    ):
        resp = client.post(
            "/api/identify",
            files={"file": ("test.jpg", FAKE_IMAGE, "image/jpeg")},
        )
    assert resp.status_code == 200
    data = resp.json()
    assert data["source"] == "yolo"
    assert len(data["results"]) == 1
    assert data["results"][0]["common_name"] == "Tomate"


def test_identify_uses_cache():
    """Cache Redis hit → retourne les données en cache sans appeler PlantNet."""
    cached = [
        {
            "species": "Rosa canina",
            "common_name": "Églantier",
            "confidence": 0.9,
            "image_url": "",
        }
    ]
    with patch("app.services.redis_cache.get", return_value=cached):
        resp = client.post(
            "/api/identify",
            files={"file": ("test.jpg", FAKE_IMAGE, "image/jpeg")},
        )
    assert resp.status_code == 200
    data = resp.json()
    assert data["source"] == "cache"
    assert data["results"][0]["species"] == "Rosa canina"


def test_identify_returns_empty_when_both_fail():
    """PlantNet et YOLO indisponibles → retourne résultats vides."""
    with (
        patch("app.services.redis_cache.get", return_value=None),
        patch("app.services.redis_cache.set"),
        patch(
            "app.services.plantnet.identify",
            new_callable=AsyncMock,
            return_value=[],
        ),
        patch(
            "app.services.yolo_service.identify",
            new_callable=AsyncMock,
            return_value=[],
        ),
    ):
        resp = client.post(
            "/api/identify",
            files={"file": ("test.jpg", FAKE_IMAGE, "image/jpeg")},
        )
    assert resp.status_code == 200
    data = resp.json()
    assert data["source"] == "yolo"
    assert data["results"] == []
