import pytest
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from ..services.assistant_service import AssistantService
from ..models.event import EventBase
from ..models.service import Service
from ..models.espai import Espai
from ..models.musica import Musica
from ..models.fotografia import Fotografia
from ..models.mobiliari_decoracio import MobiliariDecoracio
from ..models.catering import Catering
from ..models.address import Address
from ..models.availability_rule import AvailabilityRule

@pytest.fixture
def mock_db(mocker):
    """Fixture para simular la base de datos"""
    return mocker.Mock(spec=Session)

@pytest.fixture
def sample_services():
    """Fixture para crear servicios de ejemplo"""
    services = []
    
    # Espai
    espai = Service(
        id=1,
        type="espai",
        name="Sala de Fiestas Moderna",
        description="Sala moderna con capacidad para eventos",
        images=["imagen1.jpg", "imagen2.jpg"],
        service_include={"limpieza": True, "seguridad": True},
        address=Address(
            direccion="Calle Principal 123",
            ciudad="Barcelona",
            codigo_postal="08001",
            provincia="Barcelona"
        ),
        espai=Espai(
            caracteristicas=["interior", "aire_acondicionado", "parking"],
            capacitat_min=50,
            capacitat_max=200,
            espai_type="sala_privada"
        )
    )
    services.append(espai)
    
    # Música
    musica = Service(
        id=2,
        type="musica",
        name="DJ Profesional",
        description="Servicio de DJ para eventos",
        images=["dj1.jpg"],
        service_include={"equipo_sonido": True},
        address=Address(
            direccion="Calle Música 45",
            ciudad="Barcelona",
            codigo_postal="08002",
            provincia="Barcelona"
        ),
        musica=Musica(
            generos=["pop", "rock", "house"],
            duracion=4,
            instrumentos=["equipo_dj"],
            caracteristicas=["profesional", "experiencia"]
        )
    )
    services.append(musica)
    
    # Fotografía
    fotografia = Service(
        id=3,
        type="fotografia",
        name="Fotógrafo de Bodas",
        description="Servicio fotográfico para bodas",
        images=["foto1.jpg"],
        service_include={"edicion": True, "album": True},
        address=Address(
            direccion="Calle Foto 67",
            ciudad="Barcelona",
            codigo_postal="08003",
            provincia="Barcelona"
        ),
        fotografia=Fotografia(
            fotografia_type="boda",
            servicio_especial_type="completo",
            duracion=8,
            caracteristicas=["profesional", "experiencia_bodas"]
        )
    )
    services.append(fotografia)
    
    # Mobiliari-Decoracio
    mobiliari = Service(
        id=4,
        type="mobiliari-decoracio",
        name="Mobiliario Moderno",
        description="Mobiliario moderno para eventos",
        images=["mob1.jpg"],
        service_include={"montaje": True, "desmontaje": True},
        address=Address(
            direccion="Calle Mobiliario 89",
            ciudad="Barcelona",
            codigo_postal="08004",
            provincia="Barcelona"
        ),
        mobiliari_decoracio=MobiliariDecoracio(
            service_type="mobiliari",
            tipologia="mesas",
            subtipologia="mesas_redondas",
            cantidad_min=10,
            cantidad_max=50,
            caracteristicas=["moderno", "elegante"],
            is_rental=True,
            modality="alquiler",
            dimensions="120cm",
            material="madera",
            color="blanco",
            style="moderno"
        )
    )
    services.append(mobiliari)
    
    # Catering
    catering = Service(
        id=5,
        type="catering",
        name="Catering Gourmet",
        description="Servicio de catering de alta calidad",
        images=["cat1.jpg"],
        service_include={"personal": True, "vajilla": True},
        address=Address(
            direccion="Calle Catering 12",
            ciudad="Barcelona",
            codigo_postal="08005",
            provincia="Barcelona"
        ),
        catering=Catering(
            catering_type="gourmet",
            menu_type="completo",
            num_platos=3,
            caracteristicas=["alta_calidad", "profesional"],
            alergenos=["gluten", "lactosa"],
            opciones_vegetarianas=True,
            opciones_veganas=True,
            servicio_bebidas=True
        )
    )
    services.append(catering)
    
    return services

@pytest.fixture
def sample_event():
    """Fixture para crear un evento de ejemplo"""
    return EventBase(
        name="Boda María y Juan",
        type="boda",
        fechahora_inicio=datetime.now() + timedelta(days=30),
        fechahora_final=datetime.now() + timedelta(days=30, hours=6),
        num_invitats=100,
        pressupost=15000,
        has_espai_propi=False,
        selectedServices=["espai", "musica", "fotografia", "mobiliari-decoracio", "catering"],
        espai_requirements=["interior", "aire_acondicionado"],
        music_preferences=["pop", "rock"],
        photo_requirements="boda",
        mobiliari_requirements=["moderno", "elegante"],
        catering_requirements=["gourmet", "completo"],
        preferred_location="Barcelona"
    )

@pytest.mark.asyncio
async def test_find_matching_services(mock_db, sample_services, sample_event):
    """Test para verificar la búsqueda de servicios"""
    # Configurar el mock de la base de datos
    mock_db.query.return_value.all.return_value = sample_services
    
    # Crear instancia del servicio
    assistant_service = AssistantService()
    
    # Llamar al método de búsqueda
    result = await assistant_service._find_matching_services(sample_event, mock_db)
    
    # Verificar resultados
    assert len(result) > 0, "Debería encontrar al menos un servicio"
    
    # Verificar que los servicios están ordenados por puntuación
    scores = [service["match_score"] for service in result]
    assert scores == sorted(scores, reverse=True), "Los servicios deberían estar ordenados por puntuación"
    
    # Verificar que cada servicio tiene la información correcta
    for service in result:
        assert "service_id" in service
        assert "type" in service
        assert "name" in service
        assert "description" in service
        assert "match_score" in service
        
        # Verificar información específica según el tipo
        if service["type"] == "espai":
            assert "espai" in service
            assert "caracteristicas" in service["espai"]
            assert "capacitat_min" in service["espai"]
            assert "capacitat_max" in service["espai"]
            
        elif service["type"] == "musica":
            assert "musica" in service
            assert "generos" in service["musica"]
            assert "duracion" in service["musica"]
            
        elif service["type"] == "fotografia":
            assert "fotografia" in service
            assert "fotografia_type" in service["fotografia"]
            assert "duracion" in service["fotografia"]
            
        elif service["type"] == "mobiliari-decoracio":
            assert "mobiliari_decoracio" in service
            assert "service_type" in service["mobiliari_decoracio"]
            assert "tipologia" in service["mobiliari_decoracio"]
            
        elif service["type"] == "catering":
            assert "catering" in service
            assert "catering_type" in service["catering"]
            assert "menu_type" in service["catering"]
            assert "num_platos" in service["catering"]

@pytest.mark.asyncio
async def test_calculate_service_match_score(mock_db, sample_services, sample_event):
    """Test para verificar el cálculo de puntuación de coincidencia"""
    assistant_service = AssistantService()
    
    for service in sample_services:
        score = assistant_service._calculate_service_match_score(service, sample_event)
        assert score >= 0, "La puntuación debe ser positiva"
        assert score <= 100, "La puntuación máxima debe ser 100"
        
        # Verificar que los servicios relevantes tienen una puntuación más alta
        if service.type in sample_event.selectedServices:
            assert score >= 30, "Los servicios seleccionados deben tener al menos 30 puntos"

@pytest.mark.asyncio
async def test_calculate_estimated_cost(mock_db, sample_services, sample_event):
    """Test para verificar el cálculo de costes estimados"""
    assistant_service = AssistantService()
    
    for service in sample_services:
        cost = assistant_service._calculate_estimated_cost(service, sample_event)
        assert "price" in cost
        assert "currency" in cost
        assert cost["currency"] == "EUR"
        
        if "includes" in cost:
            assert isinstance(cost["includes"], dict) 