"""
WorkItem service layer for business logic and database operations.

This module handles all database operations for work items, providing a clean
separation between the API routes and data access layer.
"""

from typing import Optional
from uuid import UUID

from fastapi import HTTPException, status
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from api.models.work_item import WorkItem
from api.models.session import Session as SessionModel
from api.schemas.work_item import WorkItemCreate, WorkItemUpdate


def get_work_items(db: Session, skip: int = 0, limit: int = 100) -> tuple[list[WorkItem], int]:
    """
    Retrieve a paginated list of work items.

    Args:
        db: Database session
        skip: Number of records to skip (for pagination)
        limit: Maximum number of records to return

    Returns:
        tuple: (list of work items, total count)

    Example:
        ```python
        work_items, total = get_work_items(db, skip=0, limit=50)
        print(f"Retrieved {len(work_items)} of {total} work items")
        ```
    """
    # Get total count
    total = db.query(WorkItem).count()

    # Get paginated results, ordered by created_at descending (newest first)
    work_items = (
        db.query(WorkItem)
        .order_by(WorkItem.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return work_items, total


def get_work_item_by_id(db: Session, work_item_id: UUID) -> WorkItem:
    """
    Retrieve a single work item by its ID.

    Args:
        db: Database session
        work_item_id: UUID of the work item to retrieve

    Returns:
        WorkItem: The work item object

    Raises:
        HTTPException: 404 if work item not found

    Example:
        ```python
        work_item = get_work_item_by_id(db, work_item_id)
        print(f"Found work item: {work_item.title}")
        ```
    """
    work_item = db.query(WorkItem).filter(WorkItem.id == str(work_item_id)).first()

    if not work_item:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Work item with ID {work_item_id} not found"
        )

    return work_item


def get_work_items_by_session(db: Session, session_id: str, skip: int = 0, limit: int = 100) -> tuple[list[WorkItem], int]:
    """
    Retrieve work items for a specific session.

    Args:
        db: Database session
        session_id: Session UUID
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of work items, total count)

    Example:
        ```python
        work_items, total = get_work_items_by_session(db, session_id)
        print(f"Session has {total} work items")
        ```
    """
    total = db.query(WorkItem).filter(WorkItem.session_id == str(session_id)).count()

    work_items = (
        db.query(WorkItem)
        .filter(WorkItem.session_id == str(session_id))
        .order_by(WorkItem.item_order, WorkItem.created_at)
        .offset(skip)
        .limit(limit)
        .all()
    )

    return work_items, total


def get_work_items_by_project(db: Session, project_id: str, skip: int = 0, limit: int = 100) -> tuple[list[WorkItem], int]:
    """
    Retrieve work items for a specific project (through sessions).

    Args:
        db: Database session
        project_id: Project UUID
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of work items, total count)

    Example:
        ```python
        work_items, total = get_work_items_by_project(db, project_id)
        print(f"Project has {total} work items")
        ```
    """
    total = (
        db.query(WorkItem)
        .join(SessionModel, WorkItem.session_id == SessionModel.id)
        .filter(SessionModel.project_id == str(project_id))
        .count()
    )

    work_items = (
        db.query(WorkItem)
        .join(SessionModel, WorkItem.session_id == SessionModel.id)
        .filter(SessionModel.project_id == str(project_id))
        .order_by(WorkItem.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return work_items, total


def get_work_items_by_client(db: Session, client_id: str, skip: int = 0, limit: int = 100) -> tuple[list[WorkItem], int]:
    """
    Retrieve work items for a specific client (through sessions).

    Args:
        db: Database session
        client_id: Client UUID
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of work items, total count)

    Example:
        ```python
        work_items, total = get_work_items_by_client(db, client_id)
        print(f"Client has {total} work items")
        ```
    """
    total = (
        db.query(WorkItem)
        .join(SessionModel, WorkItem.session_id == SessionModel.id)
        .filter(SessionModel.client_id == str(client_id))
        .count()
    )

    work_items = (
        db.query(WorkItem)
        .join(SessionModel, WorkItem.session_id == SessionModel.id)
        .filter(SessionModel.client_id == str(client_id))
        .order_by(WorkItem.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return work_items, total


def get_work_items_by_status(db: Session, status_filter: str, skip: int = 0, limit: int = 100) -> tuple[list[WorkItem], int]:
    """
    Retrieve work items by status.

    Args:
        db: Database session
        status_filter: Status to filter by (completed, in_progress, blocked, pending, deferred)
        skip: Number of records to skip
        limit: Maximum number of records to return

    Returns:
        tuple: (list of work items, total count)

    Example:
        ```python
        work_items, total = get_work_items_by_status(db, "in_progress")
        print(f"Found {total} in progress work items")
        ```
    """
    total = db.query(WorkItem).filter(WorkItem.status == status_filter).count()

    work_items = (
        db.query(WorkItem)
        .filter(WorkItem.status == status_filter)
        .order_by(WorkItem.created_at.desc())
        .offset(skip)
        .limit(limit)
        .all()
    )

    return work_items, total


def validate_session_exists(db: Session, session_id: str) -> None:
    """
    Validate that a session exists.

    Args:
        db: Database session
        session_id: Session UUID to validate

    Raises:
        HTTPException: 404 if session not found

    Example:
        ```python
        validate_session_exists(db, session_id)
        # Continues if session exists, raises HTTPException if not
        ```
    """
    session = db.query(SessionModel).filter(SessionModel.id == str(session_id)).first()
    if not session:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Session with ID {session_id} not found"
        )


def create_work_item(db: Session, work_item_data: WorkItemCreate) -> WorkItem:
    """
    Create a new work item.

    Args:
        db: Database session
        work_item_data: Work item creation data

    Returns:
        WorkItem: The created work item object

    Raises:
        HTTPException: 404 if session not found
        HTTPException: 422 if validation fails
        HTTPException: 500 if database error occurs

    Example:
        ```python
        work_item_data = WorkItemCreate(
            session_id="123e4567-e89b-12d3-a456-426614174000",
            category="infrastructure",
            title="Configure firewall rules",
            description="Updated firewall rules for new server",
            status="completed"
        )
        work_item = create_work_item(db, work_item_data)
        print(f"Created work item: {work_item.id}")
        ```
    """
    # Validate session exists
    validate_session_exists(db, work_item_data.session_id)

    try:
        # Create new work item instance
        db_work_item = WorkItem(**work_item_data.model_dump())

        # Add to database
        db.add(db_work_item)
        db.commit()
        db.refresh(db_work_item)

        return db_work_item

    except IntegrityError as e:
        db.rollback()
        # Handle foreign key constraint violations
        if "session_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Session with ID {work_item_data.session_id} not found"
            )
        elif "category" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid category. Must be one of: infrastructure, troubleshooting, configuration, development, maintenance, security, documentation"
            )
        elif "status" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid status. Must be one of: completed, in_progress, blocked, pending, deferred"
            )
        elif "priority" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid priority. Must be one of: critical, high, medium, low"
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Database error: {str(e)}"
            )
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to create work item: {str(e)}"
        )


def update_work_item(db: Session, work_item_id: UUID, work_item_data: WorkItemUpdate) -> WorkItem:
    """
    Update an existing work item.

    Args:
        db: Database session
        work_item_id: UUID of the work item to update
        work_item_data: Work item update data (only provided fields will be updated)

    Returns:
        WorkItem: The updated work item object

    Raises:
        HTTPException: 404 if work item or session not found
        HTTPException: 422 if validation fails
        HTTPException: 500 if database error occurs

    Example:
        ```python
        update_data = WorkItemUpdate(
            status="completed",
            actual_minutes=45
        )
        work_item = update_work_item(db, work_item_id, update_data)
        print(f"Updated work item: {work_item.title}")
        ```
    """
    # Get existing work item
    work_item = get_work_item_by_id(db, work_item_id)

    try:
        # Update only provided fields
        update_data = work_item_data.model_dump(exclude_unset=True)

        # If updating session_id, validate session exists
        if "session_id" in update_data and update_data["session_id"] != work_item.session_id:
            validate_session_exists(db, update_data["session_id"])

        # Apply updates
        for field, value in update_data.items():
            setattr(work_item, field, value)

        db.commit()
        db.refresh(work_item)

        return work_item

    except HTTPException:
        db.rollback()
        raise
    except IntegrityError as e:
        db.rollback()
        if "session_id" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Session not found"
            )
        elif "category" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid category. Must be one of: infrastructure, troubleshooting, configuration, development, maintenance, security, documentation"
            )
        elif "status" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid status. Must be one of: completed, in_progress, blocked, pending, deferred"
            )
        elif "priority" in str(e.orig):
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="Invalid priority. Must be one of: critical, high, medium, low"
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Database error: {str(e)}"
            )
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to update work item: {str(e)}"
        )


def delete_work_item(db: Session, work_item_id: UUID) -> dict:
    """
    Delete a work item by its ID.

    Args:
        db: Database session
        work_item_id: UUID of the work item to delete

    Returns:
        dict: Success message

    Raises:
        HTTPException: 404 if work item not found
        HTTPException: 500 if database error occurs

    Example:
        ```python
        result = delete_work_item(db, work_item_id)
        print(result["message"])  # "Work item deleted successfully"
        ```
    """
    # Get existing work item (raises 404 if not found)
    work_item = get_work_item_by_id(db, work_item_id)

    try:
        db.delete(work_item)
        db.commit()

        return {
            "message": "Work item deleted successfully",
            "work_item_id": str(work_item_id)
        }

    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to delete work item: {str(e)}"
        )
