from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from app.database import get_db
from app.dependencies import require_admin_token
from app.models import PasswordResetToken, User, UserRole
from app.schemas import ForgotPasswordRequest, LoginRequest, Message, ResetPasswordRequest, Token, VerifyOTPRequest
from app.security import hash_password, verify_password
from app.services.auth_service import authenticate, issue_token_for
from app.services.password_reset_service import find_user_by_email, send_password_reset_otp

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

_ADMIN_FORGOT_MSG = "If an admin account exists for this email, a password reset code has been sent."
_ADMIN_FORGOT_SENT_MSG = "Password reset code sent. Check your inbox (and spam folder)."


@router.post("/login", response_model=Token)
def admin_login(body: LoginRequest, db: Session = Depends(get_db)):
    user = authenticate(db, body.email, body.password)
    if not user or user.role != UserRole.admin:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid admin credentials")
    if not user.is_active:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Inactive account")
    return Token(access_token=issue_token_for(user))


@router.post("/logout", response_model=Message)
def admin_logout(_: User = Depends(require_admin_token)):
    """JWT is stateless: discard the token on the client. This endpoint is for symmetry."""
    return Message(message="Logged out")


async def admin_forgot_password_handler(body: ForgotPasswordRequest, db: Session) -> Message:
    user = find_user_by_email(db, str(body.email), role=UserRole.admin)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Email is not registered",
        )
    try:
        await send_password_reset_otp(db, user)
    except RuntimeError as exc:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=str(exc),
        ) from exc
    return Message(message=_ADMIN_FORGOT_SENT_MSG)


@router.post("/forgot-password", response_model=Message)
async def admin_forgot_password(body: ForgotPasswordRequest, db: Session = Depends(get_db)):
    return await admin_forgot_password_handler(body, db)


@router.post("/send-otp", response_model=Message, summary="Resend admin password reset code")
async def admin_send_reset_otp(body: ForgotPasswordRequest, db: Session = Depends(get_db)):
    return await admin_forgot_password_handler(body, db)


@router.post("/reset-password", response_model=Message)
def admin_reset_password(body: ResetPasswordRequest, db: Session = Depends(get_db)):
    user = find_user_by_email(db, str(body.email), role=UserRole.admin)
    if not user:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request")

    token_row = (
        db.query(PasswordResetToken)
        .filter(
            PasswordResetToken.user_id == user.id,
            PasswordResetToken.used.is_(False),
        )
        .order_by(PasswordResetToken.created_at.desc())
        .first()
    )
    otp = str(body.otp).strip()
    if not token_row or not verify_password(otp, token_row.otp_hash):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid or expired OTP")

    expires = token_row.expires_at
    if expires.tzinfo is None:
        expires = expires.replace(tzinfo=timezone.utc)
    if expires < datetime.now(timezone.utc):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="OTP expired")

    user.hashed_password = hash_password(body.new_password)
    token_row.used = True
    db.commit()
    return Message(message="Password updated")


@router.post("/verify-otp", response_model=Message)
def admin_verify_otp(body: VerifyOTPRequest, db: Session = Depends(get_db)):
    user = find_user_by_email(db, str(body.email), role=UserRole.admin)
    if not user:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request")

    if not user.otp or user.otp != body.otp.strip():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="insert the correct otp")

    token_row = (
        db.query(PasswordResetToken)
        .filter(
            PasswordResetToken.user_id == user.id,
            PasswordResetToken.used.is_(False),
        )
        .order_by(PasswordResetToken.created_at.desc())
        .first()
    )
    if not token_row:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="insert the correct otp")

    expires = token_row.expires_at
    if expires.tzinfo is None:
        expires = expires.replace(tzinfo=timezone.utc)
    if expires < datetime.now(timezone.utc):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="OTP expired")

    return Message(message="OTP verified successfully")
