import numpy as np
import pytest

from uniface import (
    create_detector,
    create_landmarker,
    create_recognizer,
    detect_faces,
    list_available_detectors,
)
from uniface.constants import RetinaFaceWeights, SCRFDWeights


# create_detector tests
def test_create_detector_retinaface():
    """
    Test creating a RetinaFace detector using factory function.
    """
    detector = create_detector("retinaface")
    assert detector is not None, "Failed to create RetinaFace detector"


def test_create_detector_scrfd():
    """
    Test creating a SCRFD detector using factory function.
    """
    detector = create_detector("scrfd")
    assert detector is not None, "Failed to create SCRFD detector"


def test_create_detector_with_config():
    """
    Test creating detector with custom configuration.
    """
    detector = create_detector(
        "retinaface",
        model_name=RetinaFaceWeights.MNET_V2,
        conf_thresh=0.8,
        nms_thresh=0.3,
    )
    assert detector is not None, "Failed to create detector with custom config"


def test_create_detector_invalid_method():
    """
    Test that invalid detector method raises an error.
    """
    with pytest.raises((ValueError, KeyError)):
        create_detector("invalid_method")


def test_create_detector_scrfd_with_model():
    """
    Test creating SCRFD detector with specific model.
    """
    detector = create_detector("scrfd", model_name=SCRFDWeights.SCRFD_10G_KPS, conf_thresh=0.5)
    assert detector is not None, "Failed to create SCRFD with specific model"


# create_recognizer tests
def test_create_recognizer_arcface():
    """
    Test creating an ArcFace recognizer using factory function.
    """
    recognizer = create_recognizer("arcface")
    assert recognizer is not None, "Failed to create ArcFace recognizer"


def test_create_recognizer_mobileface():
    """
    Test creating a MobileFace recognizer using factory function.
    """
    recognizer = create_recognizer("mobileface")
    assert recognizer is not None, "Failed to create MobileFace recognizer"


def test_create_recognizer_sphereface():
    """
    Test creating a SphereFace recognizer using factory function.
    """
    recognizer = create_recognizer("sphereface")
    assert recognizer is not None, "Failed to create SphereFace recognizer"


def test_create_recognizer_invalid_method():
    """
    Test that invalid recognizer method raises an error.
    """
    with pytest.raises((ValueError, KeyError)):
        create_recognizer("invalid_method")


# create_landmarker tests
def test_create_landmarker():
    """
    Test creating a Landmark106 detector using factory function.
    """
    landmarker = create_landmarker("2d106det")
    assert landmarker is not None, "Failed to create Landmark106 detector"


def test_create_landmarker_default():
    """
    Test creating landmarker with default parameters.
    """
    landmarker = create_landmarker()
    assert landmarker is not None, "Failed to create default landmarker"


def test_create_landmarker_invalid_method():
    """
    Test that invalid landmarker method raises an error.
    """
    with pytest.raises((ValueError, KeyError)):
        create_landmarker("invalid_method")


# detect_faces tests
def test_detect_faces_retinaface():
    """
    Test high-level detect_faces function with RetinaFace.
    """
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
    faces = detect_faces(mock_image, method="retinaface")

    assert isinstance(faces, list), "detect_faces should return a list"


def test_detect_faces_scrfd():
    """
    Test high-level detect_faces function with SCRFD.
    """
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
    faces = detect_faces(mock_image, method="scrfd")

    assert isinstance(faces, list), "detect_faces should return a list"


def test_detect_faces_with_threshold():
    """
    Test detect_faces with custom confidence threshold.
    """
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
    faces = detect_faces(mock_image, method="retinaface", conf_thresh=0.8)

    assert isinstance(faces, list), "detect_faces should return a list"

    # All detections should respect threshold
    for face in faces:
        assert face["confidence"] >= 0.8, "All detections should meet confidence threshold"


def test_detect_faces_default_method():
    """
    Test detect_faces with default method (should use retinaface).
    """
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
    faces = detect_faces(mock_image)  # No method specified

    assert isinstance(faces, list), "detect_faces should return a list with default method"


def test_detect_faces_empty_image():
    """
    Test detect_faces on a blank image.
    """
    empty_image = np.zeros((640, 640, 3), dtype=np.uint8)
    faces = detect_faces(empty_image, method="retinaface")

    assert isinstance(faces, list), "Should return a list even for empty image"
    assert len(faces) == 0, "Should detect no faces in blank image"


# list_available_detectors tests
def test_list_available_detectors():
    """
    Test that list_available_detectors returns a dictionary.
    """
    detectors = list_available_detectors()

    assert isinstance(detectors, dict), "Should return a dictionary of detectors"
    assert len(detectors) > 0, "Should have at least one detector available"


def test_list_available_detectors_contents():
    """
    Test that list includes known detectors.
    """
    detectors = list_available_detectors()

    # Should include at least these detectors
    assert "retinaface" in detectors, "Should include 'retinaface'"
    assert "scrfd" in detectors, "Should include 'scrfd'"


# Integration tests
def test_detector_inference_from_factory():
    """
    Test that detector created from factory can perform inference.
    """
    detector = create_detector("retinaface")
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)

    faces = detector.detect(mock_image)
    assert isinstance(faces, list), "Detector should return list of faces"


def test_recognizer_inference_from_factory():
    """
    Test that recognizer created from factory can perform inference.
    """
    recognizer = create_recognizer("arcface")
    mock_image = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)

    embedding = recognizer.get_embedding(mock_image)
    assert embedding is not None, "Recognizer should return embedding"
    assert embedding.shape[1] == 512, "Should return 512-dimensional embedding"


def test_landmarker_inference_from_factory():
    """
    Test that landmarker created from factory can perform inference.
    """
    landmarker = create_landmarker("2d106det")
    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
    mock_bbox = [100, 100, 300, 300]

    landmarks = landmarker.get_landmarks(mock_image, mock_bbox)
    assert landmarks is not None, "Landmarker should return landmarks"
    assert landmarks.shape == (106, 2), "Should return 106 landmarks"


def test_multiple_detector_creation():
    """
    Test that multiple detectors can be created independently.
    """
    detector1 = create_detector("retinaface")
    detector2 = create_detector("scrfd")

    assert detector1 is not None
    assert detector2 is not None
    assert detector1 is not detector2, "Should create separate instances"


def test_detector_with_different_configs():
    """
    Test creating multiple detectors with different configurations.
    """
    detector_high_thresh = create_detector("retinaface", conf_thresh=0.9)
    detector_low_thresh = create_detector("retinaface", conf_thresh=0.3)

    mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)

    faces_high = detector_high_thresh.detect(mock_image)
    faces_low = detector_low_thresh.detect(mock_image)

    # Both should work
    assert isinstance(faces_high, list)
    assert isinstance(faces_low, list)


def test_factory_returns_correct_types():
    """
    Test that factory functions return instances of the correct types.
    """
    from uniface import RetinaFace, ArcFace, Landmark106

    detector = create_detector("retinaface")
    recognizer = create_recognizer("arcface")
    landmarker = create_landmarker("2d106det")

    assert isinstance(detector, RetinaFace), "Should return RetinaFace instance"
    assert isinstance(recognizer, ArcFace), "Should return ArcFace instance"
    assert isinstance(landmarker, Landmark106), "Should return Landmark106 instance"
