from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from datetime import datetime
from . import repository
from .schema import ReportCreate, ReportUpdate, ReportResponse, ReportDetailResponse, ReportStatus
from user_management import repository as user_repo
from listing_management import repository as listing_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="Report not found")
    return entity


def create_report(db: Session, data: ReportCreate) -> ReportResponse:
    reporter = user_repo.get_by_id(db, data.reporter_id)
    if not reporter:
        raise HTTPException(status_code=404, detail="Reporter user not found")
    
    if data.reported_user_id:
        reported_user = user_repo.get_by_id(db, data.reported_user_id)
        if not reported_user:
            raise HTTPException(status_code=404, detail="Reported user not found")
    
    if data.reported_listing_id:
        reported_listing = listing_repo.get_by_id(db, data.reported_listing_id)
        if not reported_listing:
            raise HTTPException(status_code=404, detail="Reported listing not found")
    
    if not data.reported_user_id and not data.reported_listing_id:
        raise HTTPException(
            status_code=400,
            detail="Either reported_user_id or reported_listing_id must be provided"
        )
    
    try:
        report_data = data.model_dump()
        report_data["status"] = ReportStatus.PENDING
        report = repository.create(db, report_data)
        db.commit()
        db.refresh(report)
        return ReportResponse.model_validate(report)
    except Exception:
        db.rollback()
        raise


def get_report(db: Session, report_id: str) -> ReportResponse:
    report = _get_or_raise(db, report_id, repository)
    return ReportResponse.model_validate(report)


def list_reports(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    reporter_id: Optional[str] = None,
    reported_user_id: Optional[str] = None,
    reported_listing_id: Optional[str] = None,
    status: Optional[str] = None,
    report_type: Optional[str] = None,
) -> List[ReportResponse]:
    reports = repository.list_all(
        db,
        limit=limit,
        offset=offset,
        reporter_id=reporter_id,
        reported_user_id=reported_user_id,
        reported_listing_id=reported_listing_id,
        status=status,
        report_type=report_type,
    )
    return [ReportResponse.model_validate(r) for r in reports]


def update_report(db: Session, report_id: str, data: ReportUpdate) -> ReportResponse:
    report = _get_or_raise(db, report_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "reported_user_id" in update_data and update_data["reported_user_id"]:
        reported_user = user_repo.get_by_id(db, update_data["reported_user_id"])
        if not reported_user:
            raise HTTPException(status_code=404, detail="Reported user not found")
    
    if "reported_listing_id" in update_data and update_data["reported_listing_id"]:
        reported_listing = listing_repo.get_by_id(db, update_data["reported_listing_id"])
        if not reported_listing:
            raise HTTPException(status_code=404, detail="Reported listing not found")
    
    if "reviewed_by_id" in update_data and update_data["reviewed_by_id"]:
        reviewer = user_repo.get_by_id(db, update_data["reviewed_by_id"])
        if not reviewer:
            raise HTTPException(status_code=404, detail="Reviewer user not found")
    
    try:
        updated_report = repository.update(db, report_id, update_data)
        db.commit()
        db.refresh(updated_report)
        return ReportResponse.model_validate(updated_report)
    except Exception:
        db.rollback()
        raise


def delete_report(db: Session, report_id: str) -> dict:
    report = _get_or_raise(db, report_id, repository)
    
    try:
        repository.delete(db, report_id)
        db.commit()
        return {"message": "Report deleted successfully"}
    except Exception:
        db.rollback()
        raise


def review_report(db: Session, report_id: str, reviewer_id: str, resolution_notes: str, new_status: ReportStatus) -> ReportResponse:
    report = _get_or_raise(db, report_id, repository)
    
    if report.status not in [ReportStatus.PENDING, ReportStatus.UNDER_REVIEW]:
        raise HTTPException(
            status_code=400,
            detail=f"Cannot review report with status {report.status}"
        )
    
    reviewer = user_repo.get_by_id(db, reviewer_id)
    if not reviewer:
        raise HTTPException(status_code=404, detail="Reviewer user not found")
    
    if new_status not in [ReportStatus.RESOLVED, ReportStatus.DISMISSED]:
        raise HTTPException(
            status_code=400,
            detail="New status must be either RESOLVED or DISMISSED"
        )
    
    try:
        update_data = {
            "status": new_status,
            "reviewed_by_id": reviewer_id,
            "resolution_notes": resolution_notes,
            "resolved_at": datetime.utcnow(),
        }
        updated_report = repository.update(db, report_id, update_data)
        db.commit()
        db.refresh(updated_report)
        return ReportResponse.model_validate(updated_report)
    except Exception:
        db.rollback()
        raise


def get_report_details(db: Session, report_id: str) -> ReportDetailResponse:
    report = repository.get_with_details(db, report_id)
    if not report:
        raise HTTPException(status_code=404, detail="Report not found")
    
    response_data = {
        "id": report.id,
        "reporter_id": report.reporter_id,
        "reported_user_id": report.reported_user_id,
        "reported_listing_id": report.reported_listing_id,
        "report_type": report.report_type,
        "description": report.description,
        "status": report.status,
        "reviewed_by_id": report.reviewed_by_id,
        "resolution_notes": report.resolution_notes,
        "resolved_at": report.resolved_at,
        "created_at": report.created_at,
        "updated_at": report.updated_at,
    }
    
    if report.reporter:
        response_data["reporter"] = {
            "id": report.reporter.id,
            "email": report.reporter.email,
            "role": report.reporter.role,
        }
    
    if report.reported_user:
        response_data["reported_user"] = {
            "id": report.reported_user.id,
            "email": report.reported_user.email,
            "role": report.reported_user.role,
        }
    
    if report.reported_listing:
        response_data["reported_listing"] = {
            "id": report.reported_listing.id,
            "title": report.reported_listing.title,
            "status": report.reported_listing.status,
        }
    
    if report.reviewed_by:
        response_data["reviewed_by"] = {
            "id": report.reviewed_by.id,
            "email": report.reviewed_by.email,
            "role": report.reviewed_by.role,
        }
    
    return ReportDetailResponse(**response_data)