"""Unit tests for inference module."""

import pytest
import torch
import json
import tempfile
import os
from PIL import Image
from src.inference import preprocess_image, predict, load_inference_model
from src.model import ResNetPhenologyClassifier


@pytest.fixture
def mock_model():
    """Create a small model for testing."""
    return ResNetPhenologyClassifier(num_classes=3, pretrained=False)


@pytest.fixture
def temp_image():
    """Create a temporary test image."""
    temp_file = tempfile.NamedTemporaryFile(suffix='.jpg', delete=False)
    img = Image.new('RGB', (224, 224), color='red')
    img.save(temp_file.name)
    yield temp_file.name
    os.unlink(temp_file.name)


@pytest.fixture
def temp_class_mapping():
    """Create a temporary class mapping file."""
    temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False)
    mapping = {
        'classes': ['vegetative', 'flowering', 'fruiting'],
        'class_to_idx': {'vegetative': 0, 'flowering': 1, 'fruiting': 2}
    }
    json.dump(mapping, temp_file)
    temp_file.flush()
    yield temp_file.name
    os.unlink(temp_file.name)


@pytest.mark.unit
def test_preprocess_image(temp_image):
    """Test image preprocessing."""
    image_tensor, original_image = preprocess_image(temp_image)
    
    assert image_tensor.shape == (1, 3, 224, 224)  # Batch size 1, RGB, 224x224
    assert isinstance(original_image, Image.Image)
    assert original_image.size == (224, 224)


@pytest.mark.unit
def test_predict(mock_model, temp_image):
    """Test prediction."""
    device = 'cpu'
    model = mock_model.to(device)
    model.eval()
    
    classes = ['vegetative', 'flowering', 'fruiting']
    image_tensor, _ = preprocess_image(temp_image)
    
    result = predict(model, image_tensor, classes, device)
    
    # Check result structure
    assert 'phase' in result
    assert 'confidence' in result
    assert 'probabilities' in result
    
    # Check values
    assert result['phase'] in classes
    assert 0 <= result['confidence'] <= 1
    assert len(result['probabilities']) == len(classes)
    
    # Check probabilities sum to ~1
    prob_sum = sum(result['probabilities'].values())
    assert abs(prob_sum - 1.0) < 1e-5


@pytest.mark.unit
def test_predict_confidence(mock_model, temp_image):
    """Test that prediction confidence matches max probability."""
    device = 'cpu'
    model = mock_model.to(device)
    model.eval()
    
    classes = ['vegetative', 'flowering', 'fruiting']
    image_tensor, _ = preprocess_image(temp_image)
    
    result = predict(model, image_tensor, classes, device)
    
    # Confidence should match the probability of the predicted class
    predicted_prob = result['probabilities'][result['phase']]
    assert abs(result['confidence'] - predicted_prob) < 1e-6


@pytest.mark.unit
def test_predict_consistency(mock_model, temp_image):
    """Test that predictions are consistent."""
    device = 'cpu'
    model = mock_model.to(device)
    model.eval()
    
    classes = ['vegetative', 'flowering', 'fruiting']
    image_tensor, _ = preprocess_image(temp_image)
    
    # Run prediction twice
    result1 = predict(model, image_tensor, classes, device)
    result2 = predict(model, image_tensor, classes, device)
    
    # Should get same results
    assert result1['phase'] == result2['phase']
    assert abs(result1['confidence'] - result2['confidence']) < 1e-6
