from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import Optional
from datetime import datetime
from . import repository
from .schema import TimelogCreate, TimelogUpdate, TimelogStartRequest, TimelogStopRequest
from job_management import repository as job_repo
from user_management import repository as user_repo
from utils.utils import utc_now


def create_timelog(db: Session, data: TimelogCreate) -> dict:
    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")
    
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    
    if data.end_time and data.start_time and data.end_time < data.start_time:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="end_time must be after start_time")
    
    overlapping = repository.get_overlapping_timelogs(db, data.user_id, data.start_time, data.end_time)
    if overlapping:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User has overlapping time logs")
    
    try:
        timelog_data = data.model_dump()
        timelog = repository.create(db, timelog_data)
        db.commit()
        db.refresh(timelog)
        return timelog
    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_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_timelog(db: Session, timelog_id: str) -> dict:
    timelog = repository.get_by_id(db, timelog_id)
    if not timelog:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Time log not found")
    return timelog


def list_timelogs(db: Session, limit: int, offset: int, job_id: Optional[str] = None, user_id: Optional[str] = None, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None) -> dict:
    filters = {}
    if job_id:
        filters["job_id"] = job_id
    if user_id:
        filters["user_id"] = user_id
    if start_date:
        filters["start_date"] = start_date
    if end_date:
        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 update_timelog(db: Session, timelog_id: str, data: TimelogUpdate) -> dict:
    timelog = repository.get_by_id(db, timelog_id)
    if not timelog:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Time log 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_404_NOT_FOUND, detail="Job not found")
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    
    new_start = update_data.get("start_time", timelog.start_time)
    new_end = update_data.get("end_time", timelog.end_time)
    new_user = update_data.get("user_id", timelog.user_id)
    
    if new_end and new_start and new_end < new_start:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="end_time must be after start_time")
    
    overlapping = repository.get_overlapping_timelogs(db, new_user, new_start, new_end)
    overlapping = [t for t in overlapping if t.id != timelog_id]
    if overlapping:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User has overlapping time logs")
    
    try:
        updated = repository.update(db, timelog_id, update_data)
        db.commit()
        db.refresh(updated)
        return updated
    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_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_timelog(db: Session, timelog_id: str) -> bool:
    timelog = repository.get_by_id(db, timelog_id)
    if not timelog:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Time log not found")
    
    try:
        repository.delete(db, timelog_id)
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise


def start_time_tracking(db: Session, user_id: str, data: TimelogStartRequest) -> dict:
    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")
    
    user = user_repo.get_by_id(db, user_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    
    active_timelog = repository.get_active_timelog_for_user(db, user_id)
    if active_timelog:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User already has an active time log. Stop the current timer before starting a new one.")
    
    try:
        timelog_data = {
            "job_id": data.job_id,
            "user_id": user_id,
            "start_time": utc_now(),
            "notes": data.notes
        }
        timelog = repository.create(db, timelog_data)
        db.commit()
        db.refresh(timelog)
        return timelog
    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_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 stop_time_tracking(db: Session, timelog_id: str, data: TimelogStopRequest) -> dict:
    timelog = repository.get_by_id(db, timelog_id)
    if not timelog:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Time log not found")
    
    if timelog.end_time is not None:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Time log has already been stopped")
    
    end_time = utc_now()
    duration = int((end_time - timelog.start_time).total_seconds() / 60)
    
    update_data = {
        "end_time": end_time,
        "duration_minutes": duration
    }
    
    if data.notes:
        update_data["notes"] = data.notes
    
    try:
        updated = repository.update(db, timelog_id, update_data)
        db.commit()
        db.refresh(updated)
        return updated
    except Exception:
        db.rollback()
        raise