# app/api/v1/endpoints/groups.py
import logging
from typing import List

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

from app.database import get_transactional_session, get_session
from app.auth import current_active_user
from app.models import User as UserModel, UserRoleEnum # Import model and enum
from app.schemas.group import GroupCreate, GroupPublic, GroupScheduleGenerateRequest
from app.schemas.invite import InviteCodePublic
from app.schemas.message import Message # For simple responses
from app.schemas.list import ListPublic, ListDetail
from app.schemas.chore import ChoreHistoryPublic, ChoreAssignmentPublic
from app.crud import group as crud_group
from app.crud import invite as crud_invite
from app.crud import list as crud_list
from app.crud import history as crud_history
from app.crud import schedule as crud_schedule
from app.core.exceptions import (
    GroupNotFoundError,
    GroupPermissionError,
    GroupMembershipError,
    GroupOperationError,
    GroupValidationError,
    InviteCreationError
)

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

@router.post(
    "", # Route relative to prefix "/groups"
    response_model=GroupPublic,
    status_code=status.HTTP_201_CREATED,
    summary="Create New Group",
    tags=["Groups"]
)
async def create_group(
    group_in: GroupCreate,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Creates a new group, adding the creator as the owner."""
    logger.info(f"User {current_user.email} creating group: {group_in.name}")
    created_group = await crud_group.create_group(db=db, group_in=group_in, creator_id=current_user.id)
    # Load members explicitly if needed for the response (optional here)
    # created_group = await crud_group.get_group_by_id(db, created_group.id)
    return created_group


@router.get(
    "", # Route relative to prefix "/groups"
    response_model=List[GroupPublic],
    summary="List User's Groups",
    tags=["Groups"]
)
async def read_user_groups(
    db: AsyncSession = Depends(get_session),  # Use read-only session for GET
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves all groups the current user is a member of."""
    logger.info(f"Fetching groups for user: {current_user.email}")
    groups = await crud_group.get_user_groups(db=db, user_id=current_user.id)
    return groups


@router.get(
    "/{group_id}",
    response_model=GroupPublic,
    summary="Get Group Details",
    tags=["Groups"]
)
async def read_group(
    group_id: int,
    db: AsyncSession = Depends(get_session),  # Use read-only session for GET
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves details for a specific group, including members, if the user is part of it."""
    logger.info(f"User {current_user.email} requesting details for group ID: {group_id}")
    # Check if user is a member first
    is_member = await crud_group.is_user_member(db=db, group_id=group_id, user_id=current_user.id)
    if not is_member:
        logger.warning(f"Access denied: User {current_user.email} not member of group {group_id}")
        raise GroupMembershipError(group_id, "view group details")

    group = await crud_group.get_group_by_id(db=db, group_id=group_id)
    if not group:
        logger.error(f"Group {group_id} requested by member {current_user.email} not found (data inconsistency?)")
        raise GroupNotFoundError(group_id)

    return group


@router.post(
    "/{group_id}/invites",
    response_model=InviteCodePublic,
    summary="Create Group Invite",
    tags=["Groups", "Invites"]
)
async def create_group_invite(
    group_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Generates a new invite code for the group. Requires owner/admin role (MVP: owner only)."""
    logger.info(f"User {current_user.email} attempting to create invite for group {group_id}")
    user_role = await crud_group.get_user_role_in_group(db, group_id=group_id, user_id=current_user.id)

    # --- Permission Check (MVP: Owner only) ---
    if user_role != UserRoleEnum.owner:
        logger.warning(f"Permission denied: User {current_user.email} (role: {user_role}) cannot create invite for group {group_id}")
        raise GroupPermissionError(group_id, "create invites")

    # Check if group exists (implicitly done by role check, but good practice)
    group = await crud_group.get_group_by_id(db, group_id)
    if not group:
        raise GroupNotFoundError(group_id)

    invite = await crud_invite.create_invite(db=db, group_id=group_id, creator_id=current_user.id)
    if not invite:
        logger.error(f"Failed to generate unique invite code for group {group_id}")
        # This case should ideally be covered by exceptions from create_invite now
        raise InviteCreationError(group_id)

    logger.info(f"User {current_user.email} created invite code for group {group_id}")
    return invite

@router.get(
    "/{group_id}/invites",
    response_model=InviteCodePublic, # Or Optional[InviteCodePublic] if it can be null
    summary="Get Group Active Invite Code",
    tags=["Groups", "Invites"]
)
async def get_group_active_invite(
    group_id: int,
    db: AsyncSession = Depends(get_session),  # Use read-only session for GET
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves the active invite code for the group. Requires group membership (owner/admin to be stricter later if needed)."""
    logger.info(f"User {current_user.email} attempting to get active invite for group {group_id}")
    
    # Permission check: Ensure user is a member of the group to view invite code
    # Using get_user_role_in_group which also checks membership indirectly
    user_role = await crud_group.get_user_role_in_group(db, group_id=group_id, user_id=current_user.id)
    if user_role is None: # Not a member
        logger.warning(f"Permission denied: User {current_user.email} is not a member of group {group_id} and cannot view invite code.")
        # More specific error or let GroupPermissionError handle if we want to be generic
        raise GroupMembershipError(group_id, "view invite code for this group (not a member)")

    # Fetch the active invite for the group
    invite = await crud_invite.get_active_invite_for_group(db, group_id=group_id)
    
    if not invite:
        # This case means no active (non-expired, active=true) invite exists.
        # The frontend can then prompt to generate one.
        logger.info(f"No active invite code found for group {group_id} when requested by {current_user.email}")
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="No active invite code found for this group. Please generate one."
        )
    
    logger.info(f"User {current_user.email} retrieved active invite code for group {group_id}")
    return invite # Pydantic will convert InviteModel to InviteCodePublic

@router.delete(
    "/{group_id}/leave",
    response_model=Message,
    summary="Leave Group",
    tags=["Groups"]
)
async def leave_group(
    group_id: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Removes the current user from the specified group. If the owner is the last member, the group will be deleted."""
    logger.info(f"User {current_user.email} attempting to leave group {group_id}")
    user_role = await crud_group.get_user_role_in_group(db, group_id=group_id, user_id=current_user.id)

    if user_role is None:
        raise GroupMembershipError(group_id, "leave (you are not a member)")

    # Check if owner is the last member
    if user_role == UserRoleEnum.owner:
        member_count = await crud_group.get_group_member_count(db, group_id)
        if member_count <= 1:
            # Delete the group since owner is the last member
            logger.info(f"Owner {current_user.email} is the last member. Deleting group {group_id}")
            await crud_group.delete_group(db, group_id)
            return Message(detail="Group deleted as you were the last member")

    # Proceed with removal for non-owner or if there are other members
    deleted = await crud_group.remove_user_from_group(db, group_id=group_id, user_id=current_user.id)

    if not deleted:
        # Should not happen if role check passed, but handle defensively
        logger.error(f"Failed to remove user {current_user.email} from group {group_id} despite being a member.")
        raise GroupOperationError("Failed to leave group")

    logger.info(f"User {current_user.email} successfully left group {group_id}")
    return Message(detail="Successfully left the group")

# --- Optional: Remove Member Endpoint ---
@router.delete(
    "/{group_id}/members/{user_id_to_remove}",
    response_model=Message,
    summary="Remove Member From Group (Owner Only)",
    tags=["Groups"]
)
async def remove_group_member(
    group_id: int,
    user_id_to_remove: int,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Removes a specified user from the group. Requires current user to be owner."""
    logger.info(f"Owner {current_user.email} attempting to remove user {user_id_to_remove} from group {group_id}")
    owner_role = await crud_group.get_user_role_in_group(db, group_id=group_id, user_id=current_user.id)

    # --- Permission Check ---
    if owner_role != UserRoleEnum.owner:
        logger.warning(f"Permission denied: User {current_user.email} (role: {owner_role}) cannot remove members from group {group_id}")
        raise GroupPermissionError(group_id, "remove members")

    # Prevent owner removing themselves via this endpoint
    if current_user.id == user_id_to_remove:
        raise GroupValidationError("Owner cannot remove themselves using this endpoint. Use 'Leave Group' instead.")

    # Check if target user is actually in the group
    target_role = await crud_group.get_user_role_in_group(db, group_id=group_id, user_id=user_id_to_remove)
    if target_role is None:
        raise GroupMembershipError(group_id, "remove this user (they are not a member)")

    # Proceed with removal
    deleted = await crud_group.remove_user_from_group(db, group_id=group_id, user_id=user_id_to_remove)

    if not deleted:
        logger.error(f"Owner {current_user.email} failed to remove user {user_id_to_remove} from group {group_id}.")
        raise GroupOperationError("Failed to remove member")

    logger.info(f"Owner {current_user.email} successfully removed user {user_id_to_remove} from group {group_id}")
    return Message(detail="Successfully removed member from the group")

@router.get(
    "/{group_id}/lists",
    response_model=List[ListDetail],
    summary="Get Group Lists",
    tags=["Groups", "Lists"]
)
async def read_group_lists(
    group_id: int,
    db: AsyncSession = Depends(get_session),  # Use read-only session for GET
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves all lists belonging to a specific group, if the user is a member."""
    logger.info(f"User {current_user.email} requesting lists for group ID: {group_id}")
    
    # Check if user is a member first
    is_member = await crud_group.is_user_member(db=db, group_id=group_id, user_id=current_user.id)
    if not is_member:
        logger.warning(f"Access denied: User {current_user.email} not member of group {group_id}")
        raise GroupMembershipError(group_id, "view group lists")

    # Get all lists for the user and filter by group_id
    lists = await crud_list.get_lists_for_user(db=db, user_id=current_user.id)
    group_lists = [list for list in lists if list.group_id == group_id]
    
    return group_lists

@router.post(
    "/{group_id}/chores/generate-schedule",
    response_model=List[ChoreAssignmentPublic],
    summary="Generate Group Chore Schedule",
    tags=["Groups", "Chores"]
)
async def generate_group_chore_schedule(
    group_id: int,
    schedule_in: GroupScheduleGenerateRequest,
    db: AsyncSession = Depends(get_transactional_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Generates a round-robin chore schedule for a group."""
    logger.info(f"User {current_user.email} generating chore schedule for group {group_id}")
    # Permission check: ensure user is a member (or owner/admin if stricter rules are needed)
    if not await crud_group.is_user_member(db, group_id, current_user.id):
        raise GroupMembershipError(group_id, "generate chore schedule for this group")

    try:
        assignments = await crud_schedule.generate_group_chore_schedule(
            db=db,
            group_id=group_id,
            start_date=schedule_in.start_date,
            end_date=schedule_in.end_date,
            user_id=current_user.id,
            member_ids=schedule_in.member_ids,
        )
        return assignments
    except Exception as e:
        logger.error(f"Error generating schedule for group {group_id}: {e}", exc_info=True)
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

@router.get(
    "/{group_id}/chores/history",
    response_model=List[ChoreHistoryPublic],
    summary="Get Group Chore History",
    tags=["Groups", "Chores", "History"]
)
async def get_group_chore_history(
    group_id: int,
    db: AsyncSession = Depends(get_session),
    current_user: UserModel = Depends(current_active_user),
):
    """Retrieves all chore-related history for a specific group."""
    logger.info(f"User {current_user.email} requesting chore history for group {group_id}")
    # Permission check
    if not await crud_group.is_user_member(db, group_id, current_user.id):
        raise GroupMembershipError(group_id, "view chore history for this group")
    
    return await crud_history.get_group_chore_history(db=db, group_id=group_id)