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, ReviewDetailsResponse
from order_management import repository as order_repo
from user_management import repository as user_repo


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=404, detail="Review not found")
    return entity


def create_review(db: Session, data: ReviewCreate) -> ReviewResponse:
    order = order_repo.get_by_id(db, data.order_id)
    if not order:
        raise HTTPException(status_code=404, detail="Order not found")
    
    reviewer = user_repo.get_by_id(db, data.reviewer_id)
    if not reviewer:
        raise HTTPException(status_code=404, detail="Reviewer user not found")
    
    seller = user_repo.get_by_id(db, data.seller_id)
    if not seller:
        raise HTTPException(status_code=404, detail="Seller user not found")
    
    existing_review = (
        db.query(repository.Review)
        .filter(repository.Review.order_id == data.order_id)
        .first()
    )
    if existing_review:
        raise HTTPException(status_code=409, detail="One review per order")
    
    try:
        review_data = data.model_dump()
        review = repository.create(db, review_data)
        db.commit()
        db.refresh(review)
        return ReviewResponse.model_validate(review)
    except Exception:
        db.rollback()
        raise


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


def list_reviews(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    order_id: Optional[str] = None,
    reviewer_id: Optional[str] = None,
    seller_id: Optional[str] = None,
    rating: Optional[int] = None,
    is_verified_purchase: Optional[bool] = None
) -> List[ReviewResponse]:
    filters = {}
    if order_id:
        filters["order_id"] = order_id
    if reviewer_id:
        filters["reviewer_id"] = reviewer_id
    if seller_id:
        filters["seller_id"] = seller_id
    if rating is not None:
        filters["rating"] = rating
    if is_verified_purchase is not None:
        filters["is_verified_purchase"] = is_verified_purchase
    
    reviews = repository.list_all(db, limit, offset, **filters)
    return [ReviewResponse.model_validate(r) for r in reviews]


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)
    
    if "order_id" in update_data:
        order = order_repo.get_by_id(db, update_data["order_id"])
        if not order:
            raise HTTPException(status_code=404, detail="Order not found")
    
    if "reviewer_id" in update_data:
        reviewer = user_repo.get_by_id(db, update_data["reviewer_id"])
        if not reviewer:
            raise HTTPException(status_code=404, detail="Reviewer user not found")
    
    if "seller_id" in update_data:
        seller = user_repo.get_by_id(db, update_data["seller_id"])
        if not seller:
            raise HTTPException(status_code=404, detail="Seller user 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) -> ReviewDetailsResponse:
    review = repository.get_with_details(db, review_id)
    if not review:
        raise HTTPException(status_code=404, detail="Review not found")
    
    return ReviewDetailsResponse.model_validate(review)


def respond_to_review(db: Session, review_id: str, response_text: str, seller_id: str) -> ReviewResponse:
    review = _get_or_raise(db, review_id, repository)
    
    if review.seller_id != seller_id:
        raise HTTPException(status_code=403, detail="Only the seller can respond to this review")
    
    try:
        update_data = {
            "seller_response": response_text,
            "seller_response_at": datetime.utcnow()
        }
        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