from datetime import timedelta
from typing import Any

from fastapi import APIRouter, Depends, Header, HTTPException, Request, status
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.api.deps import get_current_user
from app.config import settings
from app.core.i18n import get_translator
from app.core.login_security import is_account_locked, record_failed_login, reset_failed_attempts
from common_logging import get_logger
from app.core.security import (
    create_access_token,
    decode_access_token,
    revoke_token,
    verify_password,
)
from app.crud.user import user as crud_user
from app.crud.user_role import user_role as crud_user_role
from app.db.session import get_db_without_tenant
from app.middleware.rate_limit import limiter
from app.models.user import User
from app.schemas.user import UserCreate, UserResponse
from app.services.access.rbac_bootstrap_service import RBACBootstrapService

router = APIRouter()
translator = get_translator()
logger = get_logger(__name__)


class LoginRequest(BaseModel):
    username: str | None = None
    email: str | None = None
    password: str


@router.post("/login", response_model=dict)
@limiter.limit("5/minute")
def login(
    request: Request, login_data: LoginRequest, db: Session = Depends(get_db_without_tenant)
) -> Any:
    if not login_data.email:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Email is required"
        )
    try:
        if is_account_locked(login_data.email):
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="Account locked due to too many failed login attempts. Please try again later.",
            )
    except RuntimeError as e:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(e)) from None
    user = db.query(User).filter(User.email == login_data.email).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    if not verify_password(login_data.password, user.hashed_password):
        try:
            record_failed_login(login_data.email)
        except RuntimeError as e:
            raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(e)) from None
        logger.bind(user_id=user.id, email=login_data.email).warning("Login failed: incorrect password")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    if not user.is_verified:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Email not verified. Please verify your email before logging in.",
        )
    RBACBootstrapService.sync_user_role_binding(db, user)
    user_roles = crud_user_role.get_user_roles(db, user_id=user.id)
    role_codes = [role.code for role in user_roles]
    reset_failed_attempts(login_data.email)
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={
            "sub": str(user.id),
            "email": user.email,
            "tenant_id": user.tenant_id,
            "role": user.role,
            "role_codes": role_codes,
        },
        expires_delta=access_token_expires,
    )
    logger.bind(user_id=user.id, email=user.email).info("User logged in successfully")
    return {
        "access_token": access_token,
        "token_type": "bearer",
        "user": {
            "id": user.id,
            "email": user.email,
            "name": user.name,
            "tenant_id": user.tenant_id,
            "role": user.role,
            "roles": role_codes,
        },
    }


@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
@limiter.limit("3/hour")
def register(
    request: Request, user_in: UserCreate, db: Session = Depends(get_db_without_tenant)
) -> Any:
    user = db.query(User).filter(User.email == user_in.email).first()
    if user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered"
        )
    user = crud_user.create(db=db, obj_in=user_in)
    return user


@router.post("/refresh", response_model=dict)
def refresh_token(
    db: Session = Depends(get_db_without_tenant), current_user: User = Depends(get_current_user)
) -> Any:
    user_roles = crud_user_role.get_user_roles(db, user_id=current_user.id)
    role_codes = [role.code for role in user_roles]
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={
            "sub": str(current_user.id),
            "email": current_user.email,
            "tenant_id": current_user.tenant_id,
            "role": current_user.role,
            "role_codes": role_codes,
        },
        expires_delta=access_token_expires,
    )
    return {"access_token": access_token, "token_type": "bearer"}


@router.get("/me", response_model=UserResponse)
def get_current_user_info(current_user: User = Depends(get_current_user)) -> Any:
    return current_user


@router.post("/logout")
def logout(
    authorization: str | None = Header(None), current_user: User = Depends(get_current_user)
) -> Any:
    if authorization:
        try:
            scheme, token = authorization.split()
            if scheme.lower() == "bearer":
                payload = decode_access_token(token)
                if payload and "exp" in payload:
                    revoke_token(token, payload["exp"])
        except Exception as e:
            logger.warning("Failed to revoke token during logout")
    return {"message": "Successfully logged out"}
