"""User self-service API endpoints for profile retrieval and updates."""

from __future__ import annotations

from typing import TYPE_CHECKING
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import col, select

from app.core.auth import AuthContext, delete_clerk_user, get_auth_context
from app.db import crud
from app.db.session import get_session
from app.models.activity_events import ActivityEvent
from app.models.agents import Agent
from app.models.approval_task_links import ApprovalTaskLink
from app.models.approvals import Approval
from app.models.board_group_memory import BoardGroupMemory
from app.models.board_groups import BoardGroup
from app.models.board_memory import BoardMemory
from app.models.board_onboarding import BoardOnboardingSession
from app.models.boards import Board
from app.models.gateways import Gateway
from app.models.organization_board_access import OrganizationBoardAccess
from app.models.organization_invite_board_access import OrganizationInviteBoardAccess
from app.models.organization_invites import OrganizationInvite
from app.models.organization_members import OrganizationMember
from app.models.organizations import Organization
from app.models.task_dependencies import TaskDependency
from app.models.task_fingerprints import TaskFingerprint
from app.models.tasks import Task
from app.models.users import User
from app.schemas.common import OkResponse
from app.schemas.users import UserRead, UserUpdate

if TYPE_CHECKING:
    from sqlmodel.ext.asyncio.session import AsyncSession

router = APIRouter(prefix="/users", tags=["users"])
AUTH_CONTEXT_DEP = Depends(get_auth_context)
SESSION_DEP = Depends(get_session)


async def _delete_organization_tree(
    session: AsyncSession,
    *,
    organization_id: UUID,
) -> None:
    """Delete an organization and dependent rows without committing."""
    board_ids = select(Board.id).where(col(Board.organization_id) == organization_id)
    task_ids = select(Task.id).where(col(Task.board_id).in_(board_ids))
    agent_ids = select(Agent.id).where(col(Agent.board_id).in_(board_ids))
    member_ids = select(OrganizationMember.id).where(
        col(OrganizationMember.organization_id) == organization_id,
    )
    invite_ids = select(OrganizationInvite.id).where(
        col(OrganizationInvite.organization_id) == organization_id,
    )
    group_ids = select(BoardGroup.id).where(
        col(BoardGroup.organization_id) == organization_id,
    )

    await crud.delete_where(
        session,
        ActivityEvent,
        col(ActivityEvent.task_id).in_(task_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        ActivityEvent,
        col(ActivityEvent.agent_id).in_(agent_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        TaskDependency,
        col(TaskDependency.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        TaskFingerprint,
        col(TaskFingerprint.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        ApprovalTaskLink,
        col(ApprovalTaskLink.approval_id).in_(
            select(Approval.id).where(col(Approval.board_id).in_(board_ids))
        ),
        commit=False,
    )
    await crud.delete_where(
        session,
        Approval,
        col(Approval.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        BoardMemory,
        col(BoardMemory.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        BoardOnboardingSession,
        col(BoardOnboardingSession.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationBoardAccess,
        col(OrganizationBoardAccess.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationInviteBoardAccess,
        col(OrganizationInviteBoardAccess.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationBoardAccess,
        col(OrganizationBoardAccess.organization_member_id).in_(member_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationInviteBoardAccess,
        col(OrganizationInviteBoardAccess.organization_invite_id).in_(invite_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        Task,
        col(Task.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        Agent,
        col(Agent.board_id).in_(board_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        Board,
        col(Board.organization_id) == organization_id,
        commit=False,
    )
    await crud.delete_where(
        session,
        BoardGroupMemory,
        col(BoardGroupMemory.board_group_id).in_(group_ids),
        commit=False,
    )
    await crud.delete_where(
        session,
        BoardGroup,
        col(BoardGroup.organization_id) == organization_id,
        commit=False,
    )
    await crud.delete_where(
        session,
        Gateway,
        col(Gateway.organization_id) == organization_id,
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationInvite,
        col(OrganizationInvite.organization_id) == organization_id,
        commit=False,
    )
    await crud.delete_where(
        session,
        OrganizationMember,
        col(OrganizationMember.organization_id) == organization_id,
        commit=False,
    )
    await crud.update_where(
        session,
        User,
        col(User.active_organization_id) == organization_id,
        active_organization_id=None,
        commit=False,
    )
    await crud.delete_where(
        session,
        Organization,
        col(Organization.id) == organization_id,
        commit=False,
    )


@router.get("/me", response_model=UserRead)
async def get_me(auth: AuthContext = AUTH_CONTEXT_DEP) -> UserRead:
    """Return the authenticated user's current profile payload."""
    if auth.actor_type != "user" or auth.user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return UserRead.model_validate(auth.user)


@router.patch("/me", response_model=UserRead)
async def update_me(
    payload: UserUpdate,
    session: AsyncSession = SESSION_DEP,
    auth: AuthContext = AUTH_CONTEXT_DEP,
) -> UserRead:
    """Apply partial profile updates for the authenticated user."""
    if auth.actor_type != "user" or auth.user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    updates = payload.model_dump(exclude_unset=True)
    user: User = auth.user
    for key, value in updates.items():
        setattr(user, key, value)
    session.add(user)
    await session.commit()
    await session.refresh(user)
    return UserRead.model_validate(user)


@router.delete("/me", response_model=OkResponse)
async def delete_me(
    session: AsyncSession = SESSION_DEP,
    auth: AuthContext = AUTH_CONTEXT_DEP,
) -> OkResponse:
    """Delete the authenticated account and any personal-only organizations."""
    if auth.actor_type != "user" or auth.user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)

    user: User = auth.user
    await delete_clerk_user(user.clerk_user_id)
    memberships = await OrganizationMember.objects.filter_by(user_id=user.id).all(session)

    await crud.update_where(
        session,
        OrganizationInvite,
        col(OrganizationInvite.created_by_user_id) == user.id,
        created_by_user_id=None,
        commit=False,
    )
    await crud.update_where(
        session,
        OrganizationInvite,
        col(OrganizationInvite.accepted_by_user_id) == user.id,
        accepted_by_user_id=None,
        commit=False,
    )
    await crud.update_where(
        session,
        Task,
        col(Task.created_by_user_id) == user.id,
        created_by_user_id=None,
        commit=False,
    )

    for member in memberships:
        org_members = await OrganizationMember.objects.filter_by(
            organization_id=member.organization_id,
        ).all(session)
        if len(org_members) <= 1:
            await _delete_organization_tree(
                session,
                organization_id=member.organization_id,
            )
            continue
        await crud.delete_where(
            session,
            OrganizationBoardAccess,
            col(OrganizationBoardAccess.organization_member_id) == member.id,
            commit=False,
        )
        await crud.delete_where(
            session,
            OrganizationMember,
            col(OrganizationMember.id) == member.id,
            commit=False,
        )

    await crud.delete_where(
        session,
        User,
        col(User.id) == user.id,
        commit=False,
    )
    await session.commit()
    return OkResponse()
