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.auth import current_active_user
from app.models import User as UserModel
from app.schemas.list import ListCreate, ListUpdate, ListPublic, ListDetail
from app.crud import list as crud_list
from app.crud import group as crud_group
from app.schemas.list import ListStatus, ListStatusWithId
from app.schemas.expense import ExpensePublic
from app.core.exceptions import (
    GroupMembershipError,
    ConflictError,
    DatabaseIntegrityError
)

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

@router.post(
    "",
    response_model=ListPublic,
    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

    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:
        if "unique constraint" in str(e).lower():
            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)}
                )
        raise


@router.get(
    "",
    response_model=PyList[ListDetail],
    summary="List Accessible Lists",
    tags=["Lists"]
)
async def read_lists(
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    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(
    "/archived",
    response_model=PyList[ListDetail],
    summary="List Archived Lists",
    tags=["Lists"]
)
async def read_archived_lists(
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Retrieves archived lists for the current user.
    """
    logger.info(f"Fetching archived lists for user: {current_user.email}")
    lists = await crud_list.get_lists_for_user(db=db, user_id=current_user.id, include_archived=True)
    return [l for l in lists if l.archived_at]


@router.get(
    "/statuses",
    response_model=PyList[ListStatusWithId],
    summary="Get Status for Multiple Lists",
    tags=["Lists"]
)
async def read_lists_statuses(
    ids: PyList[int] = Query(...),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Retrieves the status for a list of lists.
    - `updated_at`: The timestamp of the last update to the list itself.
    - `item_count`: The total number of items in the list.
    The user must have permission to view each list requested.
    Lists that the user does not have permission for will be omitted from the response.
    """
    logger.info(f"User {current_user.email} requesting statuses for list IDs: {ids}")
    
    statuses = await crud_list.get_lists_statuses_by_ids(db=db, list_ids=ids, user_id=current_user.id)
    
    return [
        ListStatusWithId(
            id=s.id,
            updated_at=s.updated_at,
            item_count=s.item_count,
            latest_item_updated_at=s.latest_item_updated_at
        ) for s in statuses
    ]


@router.get(
    "/{list_id}",
    response_model=ListDetail,
    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}")
    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,
    summary="Update List",
    tags=["Lists"],
    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:
        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:
        logger.error(f"Error updating list {list_id} for user {current_user.email}: {str(e)}")
        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,
    summary="Archive List",
    tags=["Lists"],
    responses={
        status.HTTP_409_CONFLICT: {"description": "Conflict: List has been modified, cannot archive specified version"}
    }
)
async def archive_list_endpoint(
    list_id: int,
    expected_version: Optional[int] = Query(None, description="The expected version of the list to archive for optimistic locking."),
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Archives 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 archive list ID: {list_id}, expected version: {expected_version}")
    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 archiving 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.archive_list(db=db, list_db=list_db)
    logger.info(f"List {list_id} (version: {list_db.version}) archived successfully by user {current_user.email}.")
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.post(
    "/{list_id}/unarchive",
    response_model=ListPublic,
    summary="Unarchive List",
    tags=["Lists"]
)
async def unarchive_list_endpoint(
    list_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """
    Restores an archived list.
    """
    logger.info(f"User {current_user.email} attempting to unarchive list ID: {list_id}")
    list_db = await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id, require_creator=True)

    if not list_db.archived_at:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="List is not archived.")

    updated_list = await crud_list.unarchive_list(db=db, list_db=list_db)
    
    logger.info(f"List {list_id} unarchived successfully by user {current_user.email}.")
    return updated_list


@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 update timestamp and item count 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}")
    await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    return await crud_list.get_list_status(db=db, list_id=list_id)

@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}")
    
    await crud_list.check_list_permission(db=db, list_id=list_id, user_id=current_user.id)
    
    expenses = await crud_expense.get_expenses_for_list(db, list_id=list_id, skip=skip, limit=limit)
    return expenses