from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from datetime import datetime
from . import repository
from .schema import ReviewCreate, ReviewUpdate, ReviewResponse, ReviewDetailResponse
from booking_management import repository as booking_repo
from user_management import repository as customer_repo
from tour_catalog import repository as tour_package_repo
from user_management.models import Tourguide


def _get_or_raise(db: Session, entity_id: str, repo) -> any:
    entity = repo.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Resource not found")
    return entity


def create_review(db: Session, data: ReviewCreate) -> ReviewResponse:
    # Validate booking exists
    booking = booking_repo.get_by_id(db, data.booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    # Validate customer exists
    customer = customer_repo.get_by_id(db, data.customer_id)
    if not customer:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")
    
    # Validate tour package exists
    tour_package = tour_package_repo.get_by_id(db, data.tour_package_id)
    if not tour_package:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Tour package not found")
    
    # Validate tour guide exists if provided
    if data.tour_guide_id:
        tour_guide = db.query(Tourguide).filter(Tourguide.id == data.tour_guide_id).first()
        if not tour_guide:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Tour guide not found")
    
    # Validate booking is completed
    if booking.booking_status != "completed":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Reviews can only be submitted for completed bookings"
        )
    
    # Check if review already exists for this booking
    existing_review = db.query(repository.Review).filter(
        repository.Review.booking_id == data.booking_id
    ).first()
    if existing_review:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Review already exists for this booking"
        )
    
    try:
        review = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(review)
        return ReviewResponse.model_validate(review)
    except Exception:
        db.rollback()
        raise


def list_reviews(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    customer_id: Optional[str] = None,
    tour_package_id: Optional[str] = None,
    tour_guide_id: Optional[str] = None,
    is_published: Optional[bool] = None,
    is_verified: Optional[bool] = None
) -> List[ReviewResponse]:
    filters = {}
    if customer_id:
        filters["customer_id"] = customer_id
    if tour_package_id:
        filters["tour_package_id"] = tour_package_id
    if tour_guide_id:
        filters["tour_guide_id"] = tour_guide_id
    if is_published is not None:
        filters["is_published"] = is_published
    if is_verified is not None:
        filters["is_verified"] = is_verified
    
    reviews = repository.list_all(db, limit, offset, **filters)
    return [ReviewResponse.model_validate(review) for review in reviews]


def get_review(db: Session, review_id: str) -> ReviewResponse:
    review = _get_or_raise(db, review_id, repository)
    return ReviewResponse.model_validate(review)


def update_review(db: Session, review_id: str, data: ReviewUpdate) -> ReviewResponse:
    review = _get_or_raise(db, review_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    # Validate foreign keys if they are being updated
    if "booking_id" in update_data:
        booking = booking_repo.get_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    if "customer_id" in update_data:
        customer = customer_repo.get_by_id(db, update_data["customer_id"])
        if not customer:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Customer not found")
    
    if "tour_package_id" in update_data:
        tour_package = tour_package_repo.get_by_id(db, update_data["tour_package_id"])
        if not tour_package:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Tour package not found")
    
    if "tour_guide_id" in update_data and update_data["tour_guide_id"]:
        tour_guide = db.query(Tourguide).filter(Tourguide.id == update_data["tour_guide_id"]).first()
        if not tour_guide:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Tour guide not found")
    
    try:
        updated_review = repository.update(db, review_id, update_data)
        db.commit()
        db.refresh(updated_review)
        return ReviewResponse.model_validate(updated_review)
    except Exception:
        db.rollback()
        raise


def delete_review(db: Session, review_id: str) -> dict:
    review = _get_or_raise(db, review_id, repository)
    
    try:
        repository.delete(db, review_id)
        db.commit()
        return {"message": "Review deleted successfully"}
    except Exception:
        db.rollback()
        raise


def get_review_details(db: Session, review_id: str) -> ReviewDetailResponse:
    review = repository.get_with_details(db, review_id)
    if not review:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Review not found")
    
    return ReviewDetailResponse(
        id=review.id,
        booking_id=review.booking_id,
        customer_id=review.customer_id,
        tour_package_id=review.tour_package_id,
        tour_guide_id=review.tour_guide_id,
        overall_rating=review.overall_rating,
        guide_rating=review.guide_rating,
        accommodation_rating=review.accommodation_rating,
        transportation_rating=review.transportation_rating,
        value_for_money_rating=review.value_for_money_rating,
        title=review.title,
        comment=review.comment,
        photos=review.photos,
        is_verified=review.is_verified,
        is_published=review.is_published,
        management_response=review.management_response,
        response_date=review.response_date,
        created_at=review.created_at,
        updated_at=review.updated_at,
        customer_first_name=review.customer.user.first_name,
        customer_last_name=review.customer.user.last_name,
        tour_package_name=review.tour_package.name,
        tour_guide_first_name=review.tour_guide.user.first_name if review.tour_guide else None,
        tour_guide_last_name=review.tour_guide.user.last_name if review.tour_guide else None,
        booking_number=review.booking.booking_number
    )


def publish_review(db: Session, review_id: str) -> ReviewResponse:
    review = _get_or_raise(db, review_id, repository)
    
    if review.is_published:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Review is already published"
        )
    
    try:
        updated_review = repository.update(db, review_id, {"is_published": True})
        db.commit()
        db.refresh(updated_review)
        return ReviewResponse.model_validate(updated_review)
    except Exception:
        db.rollback()
        raise


def respond_to_review(db: Session, review_id: str, response_text: str) -> ReviewResponse:
    review = _get_or_raise(db, review_id, repository)
    
    if not review.is_published:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Cannot respond to unpublished review"
        )
    
    try:
        updated_review = repository.update(
            db,
            review_id,
            {
                "management_response": response_text,
                "response_date": datetime.utcnow()
            }
        )
        db.commit()
        db.refresh(updated_review)
        return ReviewResponse.model_validate(updated_review)
    except Exception:
        db.rollback()
        raise