from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from . import repository
from .schema import (
    UserCreate, UserUpdate, UserResponse,
    CustomerCreate, CustomerUpdate, CustomerResponse,
    TourguideCreate, TourguideUpdate, TourguideResponse,
    ServiceproviderCreate, ServiceproviderUpdate, ServiceproviderResponse
)
from typing import List, Optional
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def hash_password(password: str) -> str:
    return pwd_context.hash(password)


def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)


# User handlers
def create_user(db: Session, data: UserCreate) -> UserResponse:
    existing_user = repository.get_user_by_email(db, data.email)
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Email must be unique"
        )
    
    user_data = data.model_dump(exclude={"password"})
    user_data["password_hash"] = hash_password(data.password)
    
    try:
        user = repository.create(db, user_data)
        db.commit()
        db.refresh(user)
        return UserResponse.model_validate(user)
    except Exception:
        db.rollback()
        raise


def get_user(db: Session, user_id: str) -> UserResponse:
    user = repository.get_by_id(db, user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    return UserResponse.model_validate(user)


def list_users(
    db: Session,
    limit: int,
    offset: int,
    role: Optional[str] = None,
    is_active: Optional[bool] = None,
    search: Optional[str] = None
) -> List[UserResponse]:
    filters = {}
    if role:
        filters["role"] = role
    if is_active is not None:
        filters["is_active"] = is_active
    if search:
        filters["search"] = search
    
    users = repository.list_all(db, limit, offset, **filters)
    return [UserResponse.model_validate(user) for user in users]


def update_user(db: Session, user_id: str, data: UserUpdate) -> UserResponse:
    user = repository.get_by_id(db, user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "email" in update_data and update_data["email"] != user.email:
        existing_user = repository.get_user_by_email(db, update_data["email"])
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Email must be unique"
            )
    
    if "password" in update_data:
        update_data["password_hash"] = hash_password(update_data.pop("password"))
    
    try:
        updated_user = repository.update(db, user_id, update_data)
        db.commit()
        db.refresh(updated_user)
        return UserResponse.model_validate(updated_user)
    except Exception:
        db.rollback()
        raise


def delete_user(db: Session, user_id: str) -> dict:
    user = repository.get_by_id(db, user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    try:
        repository.delete(db, user_id)
        db.commit()
        return {"message": "User deleted successfully"}
    except Exception:
        db.rollback()
        raise


# Customer handlers
def create_customer(db: Session, data: CustomerCreate) -> CustomerResponse:
    user = repository.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    existing_customer = repository.get_customer_by_user_id(db, data.user_id)
    if existing_customer:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Customer profile already exists for this user"
        )
    
    try:
        customer = repository.create_customer(db, data.model_dump())
        db.commit()
        db.refresh(customer)
        return CustomerResponse.model_validate(customer)
    except Exception:
        db.rollback()
        raise


def get_customer(db: Session, customer_id: str) -> CustomerResponse:
    customer = repository.get_customer_by_id(db, customer_id)
    if not customer:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Customer not found"
        )
    return CustomerResponse.model_validate(customer)


def list_customers(
    db: Session,
    limit: int,
    offset: int,
    loyalty_status: Optional[str] = None,
    country: Optional[str] = None
) -> List[CustomerResponse]:
    filters = {}
    if loyalty_status:
        filters["loyalty_status"] = loyalty_status
    if country:
        filters["country"] = country
    
    customers = repository.list_all_customers(db, limit, offset, **filters)
    return [CustomerResponse.model_validate(customer) for customer in customers]


def update_customer(db: Session, customer_id: str, data: CustomerUpdate) -> CustomerResponse:
    customer = repository.get_customer_by_id(db, customer_id)
    if not customer:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Customer not found"
        )
    
    update_data = data.model_dump(exclude_unset=True)
    
    try:
        updated_customer = repository.update_customer(db, customer_id, update_data)
        db.commit()
        db.refresh(updated_customer)
        return CustomerResponse.model_validate(updated_customer)
    except Exception:
        db.rollback()
        raise


def delete_customer(db: Session, customer_id: str) -> dict:
    customer = repository.get_customer_by_id(db, customer_id)
    if not customer:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Customer not found"
        )
    
    try:
        repository.delete_customer(db, customer_id)
        db.commit()
        return {"message": "Customer deleted successfully"}
    except Exception:
        db.rollback()
        raise


# Tourguide handlers
def create_tourguide(db: Session, data: TourguideCreate) -> TourguideResponse:
    user = repository.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    existing_tourguide = repository.get_tourguide_by_user_id(db, data.user_id)
    if existing_tourguide:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Tour guide profile already exists for this user"
        )
    
    try:
        tourguide = repository.create_tourguide(db, data.model_dump())
        db.commit()
        db.refresh(tourguide)
        return TourguideResponse.model_validate(tourguide)
    except Exception:
        db.rollback()
        raise


def get_tourguide(db: Session, tourguide_id: str) -> TourguideResponse:
    tourguide = repository.get_tourguide_by_id(db, tourguide_id)
    if not tourguide:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Tour guide not found"
        )
    return TourguideResponse.model_validate(tourguide)


def list_tourguides(
    db: Session,
    limit: int,
    offset: int,
    is_available: Optional[bool] = None,
    language: Optional[str] = None
) -> List[TourguideResponse]:
    filters = {}
    if is_available is not None:
        filters["is_available"] = is_available
    if language:
        filters["language"] = language
    
    tourguides = repository.list_all_tourguides(db, limit, offset, **filters)
    return [TourguideResponse.model_validate(tourguide) for tourguide in tourguides]


def update_tourguide(db: Session, tourguide_id: str, data: TourguideUpdate) -> TourguideResponse:
    tourguide = repository.get_tourguide_by_id(db, tourguide_id)
    if not tourguide:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Tour guide not found"
        )
    
    update_data = data.model_dump(exclude_unset=True)
    
    try:
        updated_tourguide = repository.update_tourguide(db, tourguide_id, update_data)
        db.commit()
        db.refresh(updated_tourguide)
        return TourguideResponse.model_validate(updated_tourguide)
    except Exception:
        db.rollback()
        raise


def delete_tourguide(db: Session, tourguide_id: str) -> dict:
    tourguide = repository.get_tourguide_by_id(db, tourguide_id)
    if not tourguide:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Tour guide not found"
        )
    
    try:
        repository.delete_tourguide(db, tourguide_id)
        db.commit()
        return {"message": "Tour guide deleted successfully"}
    except Exception:
        db.rollback()
        raise


# Serviceprovider handlers
def create_serviceprovider(db: Session, data: ServiceproviderCreate) -> ServiceproviderResponse:
    user = repository.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    existing_serviceprovider = repository.get_serviceprovider_by_user_id(db, data.user_id)
    if existing_serviceprovider:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Service provider profile already exists for this user"
        )
    
    try:
        serviceprovider = repository.create_serviceprovider(db, data.model_dump())
        db.commit()
        db.refresh(serviceprovider)
        return ServiceproviderResponse.model_validate(serviceprovider)
    except Exception:
        db.rollback()
        raise


def get_serviceprovider(db: Session, serviceprovider_id: str) -> ServiceproviderResponse:
    serviceprovider = repository.get_serviceprovider_by_id(db, serviceprovider_id)
    if not serviceprovider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Service provider not found"
        )
    return ServiceproviderResponse.model_validate(serviceprovider)


def list_serviceproviders(
    db: Session,
    limit: int,
    offset: int,
    service_type: Optional[str] = None,
    is_verified: Optional[bool] = None,
    country: Optional[str] = None
) -> List[ServiceproviderResponse]:
    filters = {}
    if service_type:
        filters["service_type"] = service_type
    if is_verified is not None:
        filters["is_verified"] = is_verified
    if country:
        filters["country"] = country
    
    serviceproviders = repository.list_all_serviceproviders(db, limit, offset, **filters)
    return [ServiceproviderResponse.model_validate(sp) for sp in serviceproviders]


def update_serviceprovider(db: Session, serviceprovider_id: str, data: ServiceproviderUpdate) -> ServiceproviderResponse:
    serviceprovider = repository.get_serviceprovider_by_id(db, serviceprovider_id)
    if not serviceprovider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Service provider not found"
        )
    
    update_data = data.model_dump(exclude_unset=True)
    
    try:
        updated_serviceprovider = repository.update_serviceprovider(db, serviceprovider_id, update_data)
        db.commit()
        db.refresh(updated_serviceprovider)
        return ServiceproviderResponse.model_validate(updated_serviceprovider)
    except Exception:
        db.rollback()
        raise


def delete_serviceprovider(db: Session, serviceprovider_id: str) -> dict:
    serviceprovider = repository.get_serviceprovider_by_id(db, serviceprovider_id)
    if not serviceprovider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Service provider not found"
        )
    
    try:
        repository.delete_serviceprovider(db, serviceprovider_id)
        db.commit()
        return {"message": "Service provider deleted successfully"}
    except Exception:
        db.rollback()
        raise