from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import Optional
from datetime import date
from . import repository
from .schema import ReportCreate, ReportUpdate, ReportDetailResponse
from user import repository as user_repo
from utils.utils import ReportType, ReportStatus


def create_report(db: Session, data: ReportCreate) -> dict:
    user = user_repo.get_by_id(db, data.generated_by_user_id)
    if user is None:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Generated by user does not exist.")
    try:
        report = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(report)
        return report
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Report already exists.")
        if "foreign key" in msg:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Referenced resource does not exist.")
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Data integrity error.")
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def list_reports(
    db: Session,
    limit: int,
    offset: int,
    status_filter: Optional[ReportStatus] = None,
    report_type_filter: Optional[ReportType] = None,
    generated_by_user_id: Optional[str] = None,
    start_date: Optional[date] = None,
    end_date: Optional[date] = None,
) -> dict:
    filters = {}
    if status_filter is not None:
        filters["status"] = status_filter
    if report_type_filter is not None:
        filters["report_type"] = report_type_filter
    if generated_by_user_id is not None:
        filters["generated_by_user_id"] = generated_by_user_id
    if start_date is not None:
        filters["start_date"] = start_date
    if end_date is not None:
        filters["end_date"] = end_date
    items = repository.list_all(db, limit=limit, offset=offset, **filters)
    total = repository.count_all(db, **filters)
    return {"items": items, "total": total, "limit": limit, "offset": offset}


def get_report_by_id(db: Session, report_id: str) -> dict:
    report = repository.get_by_id(db, report_id)
    if report is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found.")
    return report


def get_report_details(db: Session, report_id: str) -> ReportDetailResponse:
    report = repository.get_with_details(db, report_id)
    if report is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found.")
    return report


def update_report(db: Session, report_id: str, data: ReportUpdate) -> dict:
    report = repository.get_by_id(db, report_id)
    if report is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found.")
    update_data = data.model_dump(exclude_unset=True)
    if "generated_by_user_id" in update_data and update_data["generated_by_user_id"] is not None:
        user = user_repo.get_by_id(db, update_data["generated_by_user_id"])
        if user is None:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Generated by user does not exist.")
    try:
        report = repository.update(db, report_id, update_data)
        db.commit()
        db.refresh(report)
        return report
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "unique" in msg or "duplicate" in msg:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Resource already exists.")
        if "foreign key" in msg:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Referenced resource does not exist.")
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Data integrity error.")
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_report(db: Session, report_id: str) -> None:
    report = repository.get_by_id(db, report_id)
    if report is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found.")
    try:
        repository.delete(db, report_id)
        db.commit()
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        if "foreign key" in msg:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Cannot delete report because it is referenced by other records.",
            )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Data integrity error.")
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def generate_report_service(db: Session, report_id: str) -> dict:
    report = repository.get_by_id(db, report_id)
    if report is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found.")
    if report.status == ReportStatus.COMPLETED:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Report has already been generated.")
    try:
        from utils.utils import utc_now
        report.status = ReportStatus.GENERATING
        db.flush()
        report.status = ReportStatus.COMPLETED
        report.generated_at = utc_now()
        db.commit()
        db.refresh(report)
        return report
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise