# app/api/v1/endpoints/lists.py
import logging
from typing import List as PyList, Optional # Alias for Python List type hint

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

from app.database import get_transactional_session
from app.auth import current_active_user
from app.models import User as UserModel
from app.schemas.list import ListCreate, ListUpdate, ListPublic, ListDetail
from app.schemas.message import Message # For simple responses
from app.crud import list as crud_list
from app.crud import group as crud_group # Need for group membership check
from app.schemas.list import ListStatus
from app.schemas.expense import ExpensePublic  # Import ExpensePublic
from app.core.exceptions import (
    GroupMembershipError,
    ListNotFoundError,
    ListPermissionError,
    ListStatusNotFoundError,
    ConflictError, # Added ConflictError
    DatabaseIntegrityError # Added DatabaseIntegrityError
)

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

@router.post(
    "", # Route relative to prefix "/lists"
    response_model=ListPublic, # Return basic list info on creation
    status_code=status.HTTP_201_CREATED,
    summary="Create New List",
    tags=["Lists"],
    responses={
        status.HTTP_409_CONFLICT: {
            "description": "Conflict: A list with this name already exists in the specified group",
            "model": ListPublic
        }
    }
)
async def create_list(
    list_in: ListCreate,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Creates a new shopping list.
    - If `group_id` is provided, the user must be a member of that group.
    - If `group_id` is null, it's a personal list.
    - If a list with the same name already exists in the group, returns 409 with the existing list.
    """
    logger.info(f"User {current_user.email} creating list: {list_in.name}")
    group_id = list_in.group_id

    # Permission Check: If sharing with a group, verify membership
    if group_id:
        is_member = await crud_group.is_user_member(db, group_id=group_id, user_id=current_user.id)
        if not is_member:
            logger.warning(f"User {current_user.email} attempted to create list in group {group_id} but is not a member.")
            raise GroupMembershipError(group_id, "create lists")

    try:
        created_list = await crud_list.create_list(db=db, list_in=list_in, creator_id=current_user.id)
        logger.info(f"List '{created_list.name}' (ID: {created_list.id}) created successfully for user {current_user.email}.")
        return created_list
    except DatabaseIntegrityError as e:
        # Check if this is a unique constraint violation
        if "unique constraint" in str(e).lower():
            # Find the existing list with the same name in the group
            existing_list = await crud_list.get_list_by_name_and_group(
                db=db, 
                name=list_in.name, 
                group_id=group_id,
                user_id=current_user.id
            )
            if existing_list:
                logger.info(f"List '{list_in.name}' already exists in group {group_id}. Returning existing list.")
                raise HTTPException(
                    status_code=status.HTTP_409_CONFLICT,
                    detail=f"A list named '{list_in.name}' already exists in this group.",
                    headers={"X-Existing-List": str(existing_list.id)}
                )
        # If it's not a unique constraint or we couldn't find the existing list, re-raise
        raise


@router.get(
    "", # Route relative to prefix "/lists"
    response_model=PyList[ListDetail], # Return a list of detailed list info including items
    summary="List Accessible Lists",
    tags=["Lists"]
)
async def read_lists(
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
    # Add pagination parameters later if needed: skip: int = 0, limit: int = 100
):
    """
    Retrieves lists accessible to the current user:
    - Personal lists created by the user.
    - Lists belonging to groups the user is a member of.
    """
    logger.info(f"Fetching lists accessible to user: {current_user.email}")
    lists = await crud_list.get_lists_for_user(db=db, user_id=current_user.id)
    return lists


@router.get(
    "/{list_id}",
    response_model=ListDetail, # Return detailed list info including items
    summary="Get List Details",
    tags=["Lists"]
)
async def read_list(
    list_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Retrieves details for a specific list, including its items,
    if the user has permission (creator or group member).
    """
    logger.info(f"User {current_user.email} requesting details for list ID: {list_id}")
    # The check_list_permission function will raise appropriate exceptions
    list_db = await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    return list_db


@router.put(
    "/{list_id}",
    response_model=ListPublic, # Return updated basic info
    summary="Update List",
    tags=["Lists"],
    responses={ # Add 409 to responses
        status.HTTP_409_CONFLICT: {"description": "Conflict: List has been modified by someone else"}
    }
)
async def update_list(
    list_id: int,
    list_in: ListUpdate,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Updates a list's details (name, description, is_complete).
    Requires user to be the creator or a member of the list's group.
    The client MUST provide the current `version` of the list in the `list_in` payload.
    If the version does not match, a 409 Conflict is returned.
    """
    logger.info(f"User {current_user.email} attempting to update list ID: {list_id} with version {list_in.version}")
    list_db = await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)

    try:
        updated_list = await crud_list.update_list(db=db, list_db=list_db, list_in=list_in)
        logger.info(f"List {list_id} updated successfully by user {current_user.email} to version {updated_list.version}.")
        return updated_list
    except ConflictError as e: # Catch and re-raise as HTTPException for proper FastAPI response
        logger.warning(f"Conflict updating list {list_id} for user {current_user.email}: {str(e)}")
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
    except Exception as e: # Catch other potential errors from crud operation
        logger.error(f"Error updating list {list_id} for user {current_user.email}: {str(e)}")
        # Consider a more generic error, but for now, let's keep it specific if possible
        # Re-raising might be better if crud layer already raises appropriate HTTPExceptions
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred while updating the list.")


@router.delete(
    "/{list_id}",
    status_code=status.HTTP_204_NO_CONTENT, # Standard for successful DELETE with no body
    summary="Delete List",
    tags=["Lists"],
    responses={ # Add 409 to responses
        status.HTTP_409_CONFLICT: {"description": "Conflict: List has been modified, cannot delete specified version"}
    }
)
async def delete_list(
    list_id: int,
    expected_version: Optional[int] = Query(None, description="The expected version of the list to delete for optimistic locking."),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Deletes a list. Requires user to be the creator of the list.
    If `expected_version` is provided and does not match the list's current version,
    a 409 Conflict is returned.
    """
    logger.info(f"User {current_user.email} attempting to delete list ID: {list_id}, expected version: {expected_version}")
    # Use the helper, requiring creator permission
    list_db = await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id, require_creator=True)

    if expected_version is not None and list_db.version != expected_version:
        logger.warning(
            f"Conflict deleting list {list_id} for user {current_user.email}. "
            f"Expected version {expected_version}, actual version {list_db.version}."
        )
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"List has been modified. Expected version {expected_version}, but current version is {list_db.version}. Please refresh."
        )

    await crud_list.delete_list(db=db, list_db=list_db)
    logger.info(f"List {list_id} (version: {list_db.version}) deleted successfully by user {current_user.email}.")
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.get(
    "/{list_id}/status",
    response_model=ListStatus,
    summary="Get List Status",
    tags=["Lists"]
)
async def read_list_status(
    list_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Retrieves the completion status for a specific list
    if the user has permission (creator or group member).
    """
    logger.info(f"User {current_user.email} requesting status for list ID: {list_id}")
    list_db = await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    
    # Calculate status
    total_items = len(list_db.items)
    completed_items = sum(1 for item in list_db.items if item.is_complete)
    
    try:
        completion_percentage = (completed_items / total_items * 100) if total_items > 0 else 0
    except ZeroDivisionError:
        completion_percentage = 0
    
    return ListStatus(
        list_id=list_db.id,
        total_items=total_items,
        completed_items=completed_items,
        completion_percentage=completion_percentage,
        last_updated=list_db.updated_at
    )

@router.get(
    "/{list_id}/expenses",
    response_model=PyList[ExpensePublic],
    summary="Get Expenses for List",
    tags=["Lists", "Expenses"]
)
async def read_list_expenses(
    list_id: int,
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=200),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Retrieves expenses associated with a specific list
    if the user has permission (creator or group member).
    """
    from app.crud import expense as crud_expense
    
    logger.info(f"User {current_user.email} requesting expenses for list ID: {list_id}")
    
    # Check if user has permission to access this list
    await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    
    # Get expenses for this list
    expenses = await crud_expense.get_expenses_for_list(db, list_id=list_id, skip=skip, limit=limit)
    return expenses