from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import Optional
from datetime import datetime, timezone
from . import repository
from .schema import ChecklistCreate, ChecklistUpdate, ChecklistitemCreate, ChecklistitemUpdate
from .models import Checklist, Checklistitem
from job_management import repository as job_repo
from user_management import repository as user_repo


def create_checklist(db: Session, data: ChecklistCreate):
    job = job_repo.get_by_id(db, data.job_id)
    if not job:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Job not found.")
    
    try:
        obj = repository.create(db, data.model_dump(), model=Checklist)
        db.commit()
        db.refresh(obj)
        return obj
    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="Checklist 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 get_checklist(db: Session, checklist_id: str):
    obj = repository.get_by_id(db, checklist_id, model=Checklist)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist not found.")
    return obj


def list_checklists(db: Session, limit: int, offset: int, job_id: Optional[str] = None):
    filters = {}
    if job_id is not None:
        filters["job_id"] = job_id
    items = repository.list_all(db, limit=limit, offset=offset, model=Checklist, **filters)
    total = repository.count_all(db, model=Checklist, **filters)
    return {"items": items, "total": total, "limit": limit, "offset": offset}


def update_checklist(db: Session, checklist_id: str, data: ChecklistUpdate):
    obj = repository.get_by_id(db, checklist_id, model=Checklist)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist not found.")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "job_id" in update_data:
        job = job_repo.get_by_id(db, update_data["job_id"])
        if not job:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Referenced job does not exist.")
    
    try:
        obj = repository.update(db, checklist_id, update_data, model=Checklist)
        db.commit()
        db.refresh(obj)
        return obj
    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="Checklist 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_checklist(db: Session, checklist_id: str):
    obj = repository.get_by_id(db, checklist_id, model=Checklist)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist not found.")
    
    try:
        repository.delete(db, checklist_id, model=Checklist)
        db.commit()
        return {"message": "Checklist deleted successfully."}
    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 checklist while it has dependent 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 create_checklist_item(db: Session, data: ChecklistitemCreate):
    checklist = repository.get_by_id(db, data.checklist_id, model=Checklist)
    if not checklist:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist not found.")
    
    if data.assigned_to_user_id is not None:
        user = user_repo.get_by_id(db, data.assigned_to_user_id)
        if not user:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Assigned user does not exist.")
    
    try:
        obj = repository.create(db, data.model_dump(), model=Checklistitem)
        db.commit()
        db.refresh(obj)
        return obj
    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="Checklist item 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 get_checklist_item(db: Session, checklist_item_id: str):
    obj = repository.get_by_id(db, checklist_item_id, model=Checklistitem)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist item not found.")
    return obj


def list_checklist_items(db: Session, limit: int, offset: int, checklist_id: Optional[str] = None, is_completed: Optional[bool] = None, assigned_to_user_id: Optional[str] = None):
    filters = {}
    if checklist_id is not None:
        filters["checklist_id"] = checklist_id
    if is_completed is not None:
        filters["is_completed"] = is_completed
    if assigned_to_user_id is not None:
        filters["assigned_to_user_id"] = assigned_to_user_id
    items = repository.list_all(db, limit=limit, offset=offset, model=Checklistitem, **filters)
    total = repository.count_all(db, model=Checklistitem, **filters)
    return {"items": items, "total": total, "limit": limit, "offset": offset}


def update_checklist_item(db: Session, checklist_item_id: str, data: ChecklistitemUpdate):
    obj = repository.get_by_id(db, checklist_item_id, model=Checklistitem)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist item not found.")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "checklist_id" in update_data:
        checklist = repository.get_by_id(db, update_data["checklist_id"], model=Checklist)
        if not checklist:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Referenced checklist does not exist.")
    
    if "assigned_to_user_id" in update_data and update_data["assigned_to_user_id"] is not None:
        user = user_repo.get_by_id(db, update_data["assigned_to_user_id"])
        if not user:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Assigned user does not exist.")
    
    try:
        obj = repository.update(db, checklist_item_id, update_data, model=Checklistitem)
        db.commit()
        db.refresh(obj)
        return obj
    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="Checklist item 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_checklist_item(db: Session, checklist_item_id: str):
    obj = repository.get_by_id(db, checklist_item_id, model=Checklistitem)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist item not found.")
    
    try:
        repository.delete(db, checklist_item_id, model=Checklistitem)
        db.commit()
        return {"message": "Checklist item deleted successfully."}
    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 checklist item while it has dependent 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 complete_checklist_item(db: Session, checklist_item_id: str):
    obj = repository.get_by_id(db, checklist_item_id, model=Checklistitem)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Checklist item not found.")
    
    if obj.is_completed:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Checklist item is already completed.")
    
    update_data = {
        "is_completed": True,
        "completed_at": datetime.now(timezone.utc)
    }
    
    try:
        obj = repository.update(db, checklist_item_id, update_data, model=Checklistitem)
        db.commit()
        db.refresh(obj)
        return obj
    except IntegrityError as exc:
        db.rollback()
        msg = str(exc.orig).lower() if exc.orig else ""
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Data integrity error.")
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise