from uuid import UUID

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

from sqlalchemy import select as sa_select

from src.apps.auth.schemas.requests import (
    AcceptInvitationRequest,
    ChangePasswordRequest,
    LoginRequest,
    RefreshTokenRequest,
    UpdateProfileRequest,
)
from src.apps.auth.schemas.responses import UserResponse
from src.apps.auth.services.auth_service import AuthService
from src.core.dependencies import get_current_user
from src.core.schemas.response import success
from src.core.utils.rate_limit import check_rate_limit
from src.database.session import get_db

router = APIRouter(prefix="/auth", tags=["Auth"])


@router.post("/login", response_model=dict)
async def login(
    body: LoginRequest,
    request: Request,
    db: AsyncSession = Depends(get_db),
):
    tenant_id = getattr(request.state, "tenant_id", None)
    service = AuthService(db)
    access_token, refresh_token, user, account_status = await service.login(
        body.email, body.password, tenant_id
    )
    return success(
        data={
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer",
            "user": UserResponse.model_validate(user).model_dump(),
            "account_status": account_status,
        },
        message="Login successful",
    )


@router.post("/refresh", response_model=dict)
async def refresh_token(
    body: RefreshTokenRequest,
    db: AsyncSession = Depends(get_db),
):
    service = AuthService(db)
    access_token = await service.refresh_access_token(body.refresh_token)
    return success(
        data={"access_token": access_token, "token_type": "bearer"},
        message="Token refreshed",
    )


@router.post("/logout", status_code=204)
async def logout(
    body: RefreshTokenRequest,
    db: AsyncSession = Depends(get_db),
):
    service = AuthService(db)
    await service.logout(body.refresh_token)


@router.get("/me", response_model=dict)
async def get_me(
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    from src.apps.tenants.models.account import Account

    data = UserResponse.model_validate(current_user).model_dump()
    account_status = None
    if current_user.tenant_id:
        result = await db.execute(
            sa_select(Account).where(Account.id == current_user.tenant_id)
        )
        account = result.scalar_one_or_none()
        if account:
            account_status = account.status
    data["account_status"] = account_status
    return success(data=data)


@router.get("/verify-invitation", response_model=dict)
async def verify_invitation(
    token: str = Query(...),
    db: AsyncSession = Depends(get_db),
):
    """Validate an invitation token and return the invitee's pre-fill data."""
    from src.apps.auth.models.user import User
    from src.apps.tenants.models.account import Account
    from src.core.security import verify_invitation_token

    payload = verify_invitation_token(token)
    if not payload:
        raise HTTPException(status_code=400, detail="Invalid or expired invitation link.")

    user_id = UUID(payload["sub"])
    result = await db.execute(
        sa_select(User).where(User.id == user_id, User.deleted_at.is_(None))
    )
    user = result.scalar_one_or_none()

    if not user or user.status != "invited" or user.email != payload.get("email"):
        raise HTTPException(status_code=400, detail="Invalid or expired invitation link.")

    org_name = None
    if user.tenant_id:
        tenant_result = await db.execute(
            sa_select(Account).where(Account.id == user.tenant_id)
        )
        tenant = tenant_result.scalar_one_or_none()
        if tenant:
            org_name = tenant.organization_name

    return success(data={
        "email": user.email,
        "name": f"{user.first_name} {user.last_name}".strip() or user.email,
        "role": user.role,
        "organization_name": org_name,
    })


@router.post("/accept-invitation", response_model=dict)
async def accept_invitation(
    body: AcceptInvitationRequest,
    db: AsyncSession = Depends(get_db),
):
    """Accept an invitation: set password + activate the user account."""
    from src.apps.auth.models.user import User
    from src.core.constants import UserStatus
    from src.core.security import hash_password, verify_invitation_token

    payload = verify_invitation_token(body.token)
    if not payload:
        raise HTTPException(status_code=400, detail="Invalid or expired invitation link.")

    user_id = UUID(payload["sub"])
    tenant_id = payload.get("tenant_id")

    result = await db.execute(
        sa_select(User).where(User.id == user_id, User.deleted_at.is_(None))
    )
    user = result.scalar_one_or_none()

    if not user or user.email != payload.get("email"):
        raise HTTPException(status_code=400, detail="Invalid or expired invitation link.")

    if tenant_id and str(user.tenant_id) != tenant_id:
        raise HTTPException(status_code=400, detail="Invalid or expired invitation link.")

    if user.status != UserStatus.INVITED.value:
        raise HTTPException(
            status_code=409,
            detail="This invitation has already been accepted. Please log in.",
        )

    user.password_hash = hash_password(body.password)
    user.status = UserStatus.ACTIVE.value
    await db.flush()

    return success(message="Account set up successfully. You can now log in.")


@router.patch("/me", response_model=dict)
async def update_profile(
    body: UpdateProfileRequest,
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    if body.first_name is not None:
        current_user.first_name = body.first_name
    if body.last_name is not None:
        current_user.last_name = body.last_name
    if body.avatar_url is not None:
        current_user.avatar_url = body.avatar_url
    await db.flush()
    return success(
        data=UserResponse.model_validate(current_user).model_dump(),
        message="Profile updated",
    )


@router.post("/change-password", response_model=dict)
async def change_password(
    body: ChangePasswordRequest,
    current_user=Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """INDL-55. Self-service only — target is always the JWT-resolved caller,
    never a client-supplied user id (AC-08/AC-11). Current-password
    verification is throttled per-user (AC-10) to blunt brute-forcing via a
    stolen-but-live access token. On success, every refresh token for this
    user is revoked so a leaked refresh token stops working immediately."""
    await check_rate_limit(f"change-password:{current_user.id}", limit=5, window=900)

    service = AuthService(db)
    await service.change_password(
        current_user, body.current_password, body.new_password
    )
    return success(message="Password changed successfully")
