import logging
from typing import List as PyList, Optional

from fastapi import APIRouter, Depends, HTTPException, status, Response, Query
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_transactional_session
from app.models import User as UserModel
from app.models import Item as ItemModel
from app.schemas.item import ItemCreate, ItemUpdate, ItemPublic
from app.crud import item as crud_item
from app.crud import list as crud_list
from app.core.exceptions import ItemNotFoundError, ListPermissionError, ConflictError
from app.auth import current_active_user
from app.core.redis import broadcast_event

logger = logging.getLogger(__name__)
router = APIRouter()

async def get_item_and_verify_access(
    item_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user)
) -> ItemModel:
    """Dependency to get an item and verify the user has access to its list."""
    item_db = await crud_item.get_item_by_id(db, item_id=item_id)
    if not item_db:
        raise ItemNotFoundError(item_id)

    try:
        await crud_list.check_list_permission(db=db, list_id=item_db.list_id, user_id=current_user.id)
    except ListPermissionError as e:
        raise ListPermissionError(item_db.list_id, "access this item's list")
    return item_db


@router.post(
    "/lists/{list_id}/items",
    response_model=ItemPublic,
    status_code=status.HTTP_201_CREATED,
    summary="Add Item to List",
    tags=["Items"]
)
async def create_list_item(
    list_id: int,
    item_in: ItemCreate,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Adds a new item to a specific list. User must have access to the list."""
    user_email = current_user.email
    logger.info(f"User {user_email} adding item to list {list_id}: {item_in.name}")
    try:
        await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    except ListPermissionError as e:
        raise ListPermissionError(list_id, "add items to this list")

    created_item = await crud_item.create_item(
        db=db, item_in=item_in, list_id=list_id, user_id=current_user.id
    )
    logger.info(f"Item '{created_item.name}' (ID: {created_item.id}) added to list {list_id} by user {user_email}.")
    return created_item


@router.get(
    "/lists/{list_id}/items",
    response_model=PyList[ItemPublic],
    summary="List Items in List",
    tags=["Items"]
)
async def read_list_items(
    list_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves all items for a specific list if the user has access."""
    user_email = current_user.email
    logger.info(f"User {user_email} listing items for list {list_id}")
    try:
        await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    except ListPermissionError as e:    
        raise ListPermissionError(list_id, "view items in this list")

    items = await crud_item.get_items_by_list_id(db=db, list_id=list_id)
    return items


@router.put(
    "/lists/{list_id}/items/{item_id}",
    response_model=ItemPublic,
    summary="Update Item",
    tags=["Items"],
    responses={
        status.HTTP_409_CONFLICT: {"description": "Conflict: Item has been modified by someone else"}
    }
)
async def update_item(
    list_id: int,
    item_id: int,
    item_in: ItemUpdate,
    item_db: ItemModel = Depends(get_item_and_verify_access),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Updates an item's details (name, quantity, is_complete, price).
    User must have access to the list the item belongs to.
    The client MUST provide the current `version` of the item in the `item_in` payload.
    If the version does not match, a 409 Conflict is returned.
    Sets/unsets `completed_by_id` based on `is_complete` flag.
    """
    user_email = current_user.email
    logger.info(f"User {user_email} attempting to update item ID: {item_id} with version {item_in.version}")

    try:
        updated_item = await crud_item.update_item(
            db=db, item_db=item_db, item_in=item_in, user_id=current_user.id
        )
        logger.info(f"Item {item_id} updated successfully by user {user_email} to version {updated_item.version}.")
        return updated_item
    except ConflictError as e:
        logger.warning(f"Conflict updating item {item_id} for user {user_email}: {str(e)}")
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
    except Exception as e:
        logger.error(f"Error updating item {item_id} for user {user_email}: {str(e)}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while updating the item.")


@router.delete(
    "/lists/{list_id}/items/{item_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="Delete Item",
    tags=["Items"],
    responses={
        status.HTTP_409_CONFLICT: {"description": "Conflict: Item has been modified, cannot delete specified version"}
    }
)
async def delete_item(
    list_id: int,
    item_id: int,
    expected_version: Optional[int] = Query(None, description="The expected version of the item to delete for optimistic locking."),
    item_db: ItemModel = Depends(get_item_and_verify_access),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Deletes an item. User must have access to the list the item belongs to.
    If `expected_version` is provided and does not match the item's current version,
    a 409 Conflict is returned.
    """
    user_email = current_user.email

    if expected_version is not None and item_db.version != expected_version:
        logger.warning(
            f"Conflict deleting item {item_id} for user {user_email}. "
            f"Expected version {expected_version}, actual version {item_db.version}."
        )
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Item has been modified. Expected version {expected_version}, but current version is {item_db.version}. Please refresh."
        )

    await crud_item.delete_item(db=db, item_db=item_db)
    logger.info(f"Item {item_id} (version {item_db.version}) deleted successfully by user {user_email}.")
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.post(
    "/items/{item_id}/claim",
    response_model=ItemPublic,
    summary="Claim an Item",
    tags=["Items"],
    responses={
        status.HTTP_409_CONFLICT: {"description": "Item is already claimed or completed"},
        status.HTTP_403_FORBIDDEN: {"description": "User does not have permission to claim this item"}
    }
)
async def claim_item(
    item_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Marks an item as claimed by the current user."""
    item_db = await get_item_and_verify_access(item_id, db, current_user)

    if item_db.list.group_id is None:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Items on personal lists cannot be claimed.")
        
    try:
        updated_item = await crud_item.claim_item(db, item=item_db, user_id=current_user.id)
        
        # Broadcast the event
        event = {
            "type": "item_claimed",
            "payload": {
                "list_id": updated_item.list_id,
                "item_id": updated_item.id,
                "claimed_by": {
                    "id": current_user.id,
                    "name": current_user.name
                },
                "claimed_at": updated_item.claimed_at.isoformat(),
                "version": updated_item.version
            }
        }
        await broadcast_event(f"list_{updated_item.list_id}", event)
        
        return updated_item
    except ConflictError as e:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))


@router.delete(
    "/items/{item_id}/claim",
    response_model=ItemPublic,
    summary="Unclaim an Item",
    tags=["Items"],
    responses={
        status.HTTP_403_FORBIDDEN: {"description": "User cannot unclaim an item they did not claim"}
    }
)
async def unclaim_item(
    item_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Removes the current user's claim from an item."""
    item_db = await get_item_and_verify_access(item_id, db, current_user)

    if item_db.claimed_by_user_id != current_user.id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="You can only unclaim items that you have claimed.")

    updated_item = await crud_item.unclaim_item(db, item=item_db)

    # Broadcast the event
    event = {
        "type": "item_unclaimed",
        "payload": {
            "list_id": updated_item.list_id,
            "item_id": updated_item.id,
            "version": updated_item.version
        }
    }
    await broadcast_event(f"list_{updated_item.list_id}", event)
    
    return updated_item


@router.put(
    "/lists/{list_id}/items/reorder",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="Reorder Items in List",
    tags=["Items"]
)
async def reorder_list_items(
    list_id: int,
    ordered_ids: PyList[int],
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Reorders items in a list based on the provided ordered list of item IDs."""
    user_email = current_user.email
    logger.info(f"User {user_email} reordering items in list {list_id}: {ordered_ids}")
    
    try:
        await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    except ListPermissionError as e:
        raise ListPermissionError(list_id, "reorder items in this list")

    await crud_item.reorder_items(db=db, list_id=list_id, ordered_ids=ordered_ids)
    
    # Broadcast the reorder event
    event = {
        "type": "item_reordered",
        "payload": {
            "list_id": list_id,
            "ordered_ids": ordered_ids
        }
    }
    await broadcast_event(f"list_{list_id}", event)
    
    logger.info(f"Items in list {list_id} reordered successfully by user {user_email}.")
    return Response(status_code=status.HTTP_204_NO_CONTENT)