""" Mobiliari i Decoracio related CRUD methods """
from typing import List, Optional, Dict, Tuple
from uuid import UUID
from datetime import time
from sqlmodel import Session, select, func, or_, and_
from fastapi import HTTPException

from app.models import (
    MobiliariDecoracio,
    MobiliariDecoracioType,
    TipoMobiliriDecoracio,
    ServiceMobiliariDecoracioOut,
    ServiceMobiliariDecoraciosOut,
    ServiceCreateMobiliariDecoracio,
    ServiceUpdateMobiliariDecoracio,
    Service,
    User,
    ServiceType,
    Dimensions,
    AvailabilityRule
)


def create_mobiliari_decoracio(
    *,
    session: Session,
    mobiliari_decoracio_in: ServiceCreateMobiliariDecoracio,
    user: User
) -> ServiceMobiliariDecoracioOut:
    """Create a new mobiliari decoracio service"""
    # Convert address to dict if it exists
    address_dict = None
    if mobiliari_decoracio_in.address:
        address_dict = mobiliari_decoracio_in.address.model_dump()

    # First create the service
    service = Service(
        type=ServiceType.MOBILIARI_DECORACIO,
        name=mobiliari_decoracio_in.name,
        description=mobiliari_decoracio_in.description,
        images=mobiliari_decoracio_in.images,
        service_include=mobiliari_decoracio_in.service_include,
        address=address_dict,
        account_id=user.id
    )
    session.add(service)
    session.flush()  # To get the service ID
    
    mobiliari_data = mobiliari_decoracio_in.mobiliari_decoracio
    # Convert dimensions to dict if exists
    dimensions_dict = None
    if "dimensions" in mobiliari_data:
        dimensions_dict = mobiliari_data["dimensions"]
        del mobiliari_data["dimensions"]
    
    # Then create the mobiliari_decoracio
    mobiliari = MobiliariDecoracio(
        **mobiliari_data,
        dimensions=dimensions_dict,
        id=service.id
    )
    session.add(mobiliari)
    service.mobiliari_decoracio_id = mobiliari.id
    session.add(service)

        # Crear reglas de disponibilidad si se proporcionan
    if mobiliari_decoracio_in.availability_rules:
        for rule_data in mobiliari_decoracio_in.availability_rules:
            hora_inicio = time.fromisoformat(rule_data.get('hora_inicio')) if rule_data.get('hora_inicio') else None
            hora_fin = time.fromisoformat(rule_data.get('hora_fin')) if rule_data.get('hora_fin') else None
            rule = AvailabilityRule(
                service_id=service.id,
                dias_semana=rule_data.get('dias_semana', []),
                hora_inicio=hora_inicio,
                hora_fin=hora_fin,
                price=rule_data.get('price', 0.0),
                min_days_before=rule_data.get('min_days_before', 1)
            )
            session.add(rule)

    session.commit()
    session.refresh(service)
    session.refresh(mobiliari)
    
    # Get availability rules
    rules = session.exec(
        select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
    ).all()
    availability_rules_dict = [rule.dict() for rule in rules]
    
    # Construct the response
    mobiliari_dict = mobiliari.dict()
    return ServiceMobiliariDecoracioOut(
        id=service.id,
        type=service.type,
        name=service.name,
        description=service.description,
        images=service.images,
        service_include=service.service_include,
        address=service.address,
        account_id=service.account_id,
        availability_rules=availability_rules_dict,
        mobiliari_decoracio_id=mobiliari.id,
        mobiliari_decoracio=mobiliari_dict
    )


def update_mobiliari_decoracio(
    *,
    session: Session,
    service: Service,
    mobiliari: MobiliariDecoracio,
    mobiliari_decoracio_in: ServiceUpdateMobiliariDecoracio
) -> ServiceMobiliariDecoracioOut:
    """Update a mobiliari decoracio service"""
    # Update service fields
    service_data = mobiliari_decoracio_in.dict(exclude={"mobiliari_decoracio", "availability_rules"}, exclude_unset=True)
    address_dict = None
    if service_data.get("address"):
        address = service_data["address"]
        address_dict = address.dict() if hasattr(address, 'dict') else address
        service_data["address"] = address_dict
    for key, value in service_data.items():
        setattr(service, key, value)
    
    # Actualizar reglas de disponibilidad si se proporcionan
    if mobiliari_decoracio_in.availability_rules is not None:
        # Eliminar reglas existentes
        existing_rules = session.exec(
            select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
        ).all()
        for rule in existing_rules:
            session.delete(rule)
        
        # Crear nuevas reglas
        for rule_data in mobiliari_decoracio_in.availability_rules:
            hora_inicio = time.fromisoformat(rule_data.get('hora_inicio')) if rule_data.get('hora_inicio') else None
            hora_fin = time.fromisoformat(rule_data.get('hora_fin')) if rule_data.get('hora_fin') else None
            rule = AvailabilityRule(
                service_id=service.id,
                dias_semana=rule_data.get('dias_semana', []),
                hora_inicio=hora_inicio,
                hora_fin=hora_fin,
                price=rule_data.get('price', 0.0),
                min_days_before=rule_data.get('min_days_before', 1)
            )
            session.add(rule)
    
    # Update mobiliari fields if provided
    if mobiliari_decoracio_in.mobiliari_decoracio and isinstance(mobiliari_decoracio_in.mobiliari_decoracio, dict):
        mobiliari_data = mobiliari_decoracio_in.mobiliari_decoracio.copy()
        for key, value in mobiliari_data.items():
            setattr(mobiliari, key, value)
    
    session.add(service)
    session.add(mobiliari)
    session.commit()
    session.refresh(service)
    session.refresh(mobiliari)
    
    # Get availability rules
    rules = session.exec(
        select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
    ).all()
    availability_rules_dict = [rule.dict() for rule in rules]
    
    # Construct the response
    mobiliari_dict = mobiliari.dict()
    return ServiceMobiliariDecoracioOut(
        id=service.id,
        type=service.type,
        name=service.name,
        description=service.description,
        images=service.images,
        service_include=service.service_include,
        address=service.address,
        account_id=service.account_id,
        availability_rules=availability_rules_dict,
        mobiliari_decoracio_id=mobiliari.id,
        mobiliari_decoracio=mobiliari_dict
    )


def get_mobiliari_decoracio_by_id(
    *,
    session: Session,
    id: UUID
) -> ServiceMobiliariDecoracioOut:
    """Get mobiliari decoracio by ID"""
    # Join query to get service with related mobiliari
    result = session.exec(
        select(Service, MobiliariDecoracio)
        .join(MobiliariDecoracio, Service.id == MobiliariDecoracio.id)
        .where(Service.id == id)
    ).first()
    
    if not result:
        raise HTTPException(status_code=404, detail="Mobiliari decoracio not found")
    
    service, mobiliari = result
    
    # Get availability rules
    rules = session.exec(
        select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
    ).all()
    availability_rules_dict = [rule.dict() for rule in rules]
    
    # Construct the response
    mobiliari_dict = mobiliari.dict()
    return ServiceMobiliariDecoracioOut(
        id=service.id,
        type=service.type,
        name=service.name,
        description=service.description,
        images=service.images,
        service_include=service.service_include,
        address=service.address,
        account_id=service.account_id,
        availability_rules=availability_rules_dict,
        mobiliari_decoracio_id=mobiliari.id,
        mobiliari_decoracio=mobiliari_dict
    )


def get_mobiliari_decoracios(
    *,
    session: Session,
    skip: int = 0,
    limit: int = 100,
    service_type: Optional[MobiliariDecoracioType] = None,
    tipologia: Optional[List[TipoMobiliriDecoracio]] = None,
    is_rental: Optional[bool] = None,
    cantidad: Optional[int] = None,
    material: Optional[List[str]] = None,
    color: Optional[List[str]] = None,
    caracteristicas: Optional[List[str]] = None,
    forma: Optional[List[str]] = None,
    user_id: Optional[UUID] = None
) -> ServiceMobiliariDecoraciosOut:
    """Get list of mobiliari decoracio services with filters"""
    # Build the base query
    query = select(Service, MobiliariDecoracio).join(
        MobiliariDecoracio, Service.id == MobiliariDecoracio.id
    )
    
    # Apply filters
    if user_id:
        query = query.where(Service.account_id == user_id)
    if service_type:
        query = query.where(MobiliariDecoracio.service_type == service_type)
    if tipologia:
        # Buscar elementos que contengan al menos uno de los materiales especificados
        tipologia_conditions = [MobiliariDecoracio.tipologia.contains(t) for t in tipologia]
        query = query.where(or_(*tipologia_conditions))
    if is_rental is not None:
        query = query.where(MobiliariDecoracio.is_rental == is_rental)
    if cantidad:
        query = query.where(
            and_(
                MobiliariDecoracio.cantidad_min <= cantidad,
                MobiliariDecoracio.cantidad_max >= cantidad
            )
        )
    if material:
        # Buscar elementos que contengan al menos uno de los materiales especificados
        material_conditions = [MobiliariDecoracio.material.contains(m) for m in material]
        query = query.where(or_(*material_conditions))
    if color:
        # Buscar elementos que contengan al menos uno de los colores especificados
        color_conditions = [MobiliariDecoracio.color.contains(c) for c in color]
        query = query.where(or_(*color_conditions))
    if caracteristicas:
        # Buscar elementos que contengan al menos una de las características especificadas
        caracteristicas_conditions = [MobiliariDecoracio.caracteristicas.contains(c) for c in caracteristicas]
        query = query.where(or_(*caracteristicas_conditions))
    if forma:
        forma_conditions = [MobiliariDecoracio.forma.contains(f) for f in forma]
        query = query.where(or_(*forma_conditions))
    
    # Get total count
    count = session.exec(select(func.count()).select_from(query.subquery())).one()
    
    # Get paginated results
    results = session.exec(query.offset(skip).limit(limit)).all()
    
    # Construct the response
    mobiliaris = []
    for service, mobiliari in results:
        # Get availability rules
        rules = session.exec(
            select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
        ).all()
        availability_rules_dict = [rule.dict() for rule in rules]
        
        mobiliari_dict = mobiliari.dict()
        mobiliaris.append(ServiceMobiliariDecoracioOut(
            id=service.id,
            type=service.type,
            name=service.name,
            description=service.description,
            images=service.images,
            service_include=service.service_include,
            address=service.address,
            account_id=service.account_id,
            availability_rules=availability_rules_dict,
            mobiliari_decoracio_id=mobiliari.id,
            mobiliari_decoracio=mobiliari_dict
        ))
    
    return ServiceMobiliariDecoraciosOut(data=mobiliaris, count=count)


def delete_mobiliari_decoracio(
    *,
    session: Session,
    service: Service,
    mobiliari: MobiliariDecoracio
) -> None:
    """Delete a mobiliari decoracio service"""
    session.delete(mobiliari)
    session.delete(service)
    session.commit()


def get_mobiliaris_by_account(
    *,
    session: Session,
    account_id: UUID,
    skip: int = 0,
    limit: int = 100
) -> ServiceMobiliariDecoraciosOut:
    """Get mobiliaris by account id with pagination"""
    # Count query
    count_statement = select(func.count(Service.id)).join(MobiliariDecoracio).where(
        Service.account_id == account_id
    )
    count = session.exec(count_statement).one()
    
    # Results query
    statement = select(Service, MobiliariDecoracio).join(
        MobiliariDecoracio, Service.id == MobiliariDecoracio.id
    ).where(
        Service.account_id == account_id
    ).offset(skip).limit(limit)
    
    results = session.exec(statement).all()
    
    # Construct the response
    mobiliaris = []
    for service, mobiliari in results:
        # Get availability rules
        rules = session.exec(
            select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
        ).all()
        availability_rules_dict = [rule.dict() for rule in rules]
        
        mobiliari_dict = mobiliari.dict()
        mobiliaris.append(ServiceMobiliariDecoracioOut(
            id=service.id,
            type=service.type,
            name=service.name,
            description=service.description,
            images=service.images,
            service_include=service.service_include,
            address=service.address,
            account_id=service.account_id,
            availability_rules=availability_rules_dict,
            mobiliari_decoracio_id=mobiliari.id,
            mobiliari_decoracio=mobiliari_dict
        ))
    
    return ServiceMobiliariDecoraciosOut(data=mobiliaris, count=count)


def get_all_mobiliaris(
    *,
    session: Session,
    skip: int = 0,
    limit: int = 100
) -> ServiceMobiliariDecoraciosOut:
    """Get all mobiliari decoracio services"""
    # Count query
    count_statement = select(func.count(Service.id)).where(
        Service.type == ServiceType.MOBILIARI_DECORACIO
    )
    count = session.exec(count_statement).one()
    
    # Results query
    results = session.exec(
        select(Service, MobiliariDecoracio)
        .join(MobiliariDecoracio, Service.id == MobiliariDecoracio.id)
        .where(Service.type == ServiceType.MOBILIARI_DECORACIO)
        .offset(skip)
        .limit(limit)
    ).all()
    
    # Construct the response
    mobiliaris = []
    for service, mobiliari in results:
        # Get availability rules
        rules = session.exec(
            select(AvailabilityRule).where(AvailabilityRule.service_id == service.id)
        ).all()
        availability_rules_dict = [rule.dict() for rule in rules]
        
        mobiliari_dict = mobiliari.dict()
        mobiliaris.append(ServiceMobiliariDecoracioOut(
            id=service.id, 
            type=service.type,
            name=service.name,
            images=service.images,
            description=service.description,
            service_include=service.service_include,
            address=service.address,
            account_id=service.account_id,
            mobiliari_decoracio_id=mobiliari.id,
            mobiliari_decoracio=mobiliari_dict,
            availability_rules=availability_rules_dict
        ))
    
    return ServiceMobiliariDecoraciosOut(data=mobiliaris, count=count) 