from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from . import repository
from .schema import DisputeCreate, DisputeUpdate, DisputeResponse, DisputeDetailResponse, DisputeStatus
from order_management import repository as order_repo
from user_management import repository as user_repo
from datetime import datetime

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=f"Entity with id {entity_id} not found")
    return entity

def create_dispute(db: Session, data: DisputeCreate) -> DisputeResponse:
    order = order_repo.get_by_id(db, data.order_id)
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Order with id {data.order_id} not found")
    
    complainant = user_repo.get_by_id(db, data.complainant_id)
    if not complainant:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Complainant user with id {data.complainant_id} not found")
    
    respondent = user_repo.get_by_id(db, data.respondent_id)
    if not respondent:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Respondent user with id {data.respondent_id} not found")
    
    if order.status not in ["DELIVERED", "COMPLETED"]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Disputes can only be opened for delivered or completed orders")
    
    if order.buyer_id != data.complainant_id and order.seller_id != data.complainant_id:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Complainant must be either buyer or seller of the order")
    
    existing_dispute = db.query(repository.Dispute).filter(
        repository.Dispute.order_id == data.order_id,
        repository.Dispute.status.in_(["OPEN", "UNDER_REVIEW", "ESCALATED"])
    ).first()
    if existing_dispute:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="An active dispute already exists for this order")
    
    dispute_data = data.model_dump()
    dispute_data["status"] = DisputeStatus.OPEN.value
    
    try:
        dispute = repository.create(db, dispute_data)
        db.commit()
        db.refresh(dispute)
        return DisputeResponse.model_validate(dispute)
    except Exception:
        db.rollback()
        raise

def get_dispute(db: Session, dispute_id: str) -> DisputeResponse:
    dispute = _get_or_raise(db, dispute_id, repository)
    return DisputeResponse.model_validate(dispute)

def list_disputes(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    status: Optional[str] = None,
    complainant_id: Optional[str] = None,
    respondent_id: Optional[str] = None,
    order_id: Optional[str] = None
) -> List[DisputeResponse]:
    filters = {}
    if status:
        filters["status"] = status
    if complainant_id:
        filters["complainant_id"] = complainant_id
    if respondent_id:
        filters["respondent_id"] = respondent_id
    if order_id:
        filters["order_id"] = order_id
    
    disputes = repository.list_all(db, limit, offset, **filters)
    return [DisputeResponse.model_validate(d) for d in disputes]

def update_dispute(db: Session, dispute_id: str, data: DisputeUpdate) -> DisputeResponse:
    dispute = _get_or_raise(db, dispute_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=status.HTTP_404_NOT_FOUND, detail=f"Order with id {update_data['order_id']} not found")
    
    if "complainant_id" in update_data:
        complainant = user_repo.get_by_id(db, update_data["complainant_id"])
        if not complainant:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Complainant user with id {update_data['complainant_id']} not found")
    
    if "respondent_id" in update_data:
        respondent = user_repo.get_by_id(db, update_data["respondent_id"])
        if not respondent:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Respondent user with id {update_data['respondent_id']} not found")
    
    if "resolved_by_id" in update_data and update_data["resolved_by_id"]:
        resolved_by = user_repo.get_by_id(db, update_data["resolved_by_id"])
        if not resolved_by:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Resolver user with id {update_data['resolved_by_id']} not found")
    
    try:
        updated_dispute = repository.update(db, dispute_id, update_data)
        db.commit()
        db.refresh(updated_dispute)
        return DisputeResponse.model_validate(updated_dispute)
    except Exception:
        db.rollback()
        raise

def delete_dispute(db: Session, dispute_id: str) -> dict:
    dispute = _get_or_raise(db, dispute_id, repository)
    
    try:
        repository.delete(db, dispute_id)
        db.commit()
        return {"message": "Dispute deleted successfully"}
    except Exception:
        db.rollback()
        raise

def get_dispute_details(db: Session, dispute_id: str) -> DisputeDetailResponse:
    dispute = repository.get_with_details(db, dispute_id)
    if not dispute:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Dispute with id {dispute_id} not found")
    
    response_dict = {
        "id": dispute.id,
        "order_id": dispute.order_id,
        "complainant_id": dispute.complainant_id,
        "respondent_id": dispute.respondent_id,
        "reason": dispute.reason,
        "description": dispute.description,
        "status": dispute.status,
        "resolution": dispute.resolution,
        "resolved_by_id": dispute.resolved_by_id,
        "resolved_at": dispute.resolved_at,
        "created_at": dispute.created_at,
        "updated_at": dispute.updated_at,
        "order": {
            "order_number": dispute.order.order_number,
            "order_items": [
                {"id": item.id, "card_name": item.card.name}
                for item in dispute.order.order_items
            ]
        },
        "complainant": {"email": dispute.complainant.email},
        "respondent": {"email": dispute.respondent.email},
        "resolved_by": {"email": dispute.resolved_by.email} if dispute.resolved_by else None
    }
    
    return DisputeDetailResponse.model_validate(response_dict)

def resolve_dispute_service(db: Session, dispute_id: str, resolution: str, resolved_by_id: str) -> DisputeResponse:
    dispute = _get_or_raise(db, dispute_id, repository)
    
    if dispute.status not in ["OPEN", "UNDER_REVIEW", "ESCALATED"]:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Only open, under review, or escalated disputes can be resolved")
    
    resolved_by = user_repo.get_by_id(db, resolved_by_id)
    if not resolved_by:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Resolver user with id {resolved_by_id} not found")
    
    update_data = {
        "status": DisputeStatus.RESOLVED.value,
        "resolution": resolution,
        "resolved_by_id": resolved_by_id,
        "resolved_at": datetime.utcnow()
    }
    
    try:
        updated_dispute = repository.update(db, dispute_id, update_data)
        db.commit()
        db.refresh(updated_dispute)
        return DisputeResponse.model_validate(updated_dispute)
    except Exception:
        db.rollback()
        raise