"""
Authentication Router

Handles all authentication-related endpoints:
- Login/Logout
- User info
- Password changes
- User preferences
"""

import sqlite3
from typing import Dict

from fastapi import APIRouter, Depends, HTTPException, Body, Request
from fastapi.responses import JSONResponse
from slowapi import Limiter
from slowapi.util import get_remote_address

from ..core.dependencies import get_current_user, get_app_state
from ..core.config import settings
from ..core.exceptions import AuthError, handle_exceptions
from ..core.responses import to_iso8601, now_iso8601
from ..models.api_models import LoginRequest, ChangePasswordRequest, UserPreferences
from modules.universal_logger import get_logger

logger = get_logger('API')

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

# Rate limiter - will be set from main app
limiter = Limiter(key_func=get_remote_address)


@router.post("/login")
@limiter.limit("5/minute")
@handle_exceptions
async def login(login_data: LoginRequest, request: Request):
    """
    Authenticate user with username and password.

    Returns JWT token or 2FA challenge if 2FA is enabled.
    """
    app_state = get_app_state()

    if not app_state.auth:
        raise HTTPException(status_code=500, detail="Authentication not initialized")

    # Query user from database
    with sqlite3.connect(app_state.auth.db_path) as conn:
        cursor = conn.cursor()

        cursor.execute("""
            SELECT password_hash, role, is_active, totp_enabled, duo_enabled, passkey_enabled
            FROM users WHERE username = ?
        """, (login_data.username,))

        row = cursor.fetchone()

    if not row:
        raise HTTPException(status_code=401, detail="Invalid credentials")

    password_hash, role, is_active, totp_enabled, duo_enabled, passkey_enabled = row

    if not is_active:
        raise HTTPException(status_code=401, detail="Account is inactive")

    if not app_state.auth.verify_password(login_data.password, password_hash):
        app_state.auth._record_login_attempt(login_data.username, False)
        raise HTTPException(status_code=401, detail="Invalid credentials")

    # Check if user has any 2FA methods enabled
    available_methods = []
    if totp_enabled:
        available_methods.append('totp')
    if passkey_enabled:
        available_methods.append('passkey')
    if duo_enabled:
        available_methods.append('duo')

    # If user has 2FA enabled, return require2FA flag
    if available_methods:
        return {
            'success': True,
            'require2FA': True,
            'availableMethods': available_methods,
            'username': login_data.username
        }

    # No 2FA - proceed with normal login
    result = app_state.auth._create_session(
        username=login_data.username,
        role=role,
        ip_address=request.client.host if request.client else None,
        remember_me=login_data.rememberMe
    )

    # Create response with cookie
    response = JSONResponse(content=result)

    # Set auth cookie (secure, httponly for security)
    max_age = 30 * 24 * 60 * 60 if login_data.rememberMe else None
    response.set_cookie(
        key="auth_token",
        value=result.get('token'),
        max_age=max_age,
        httponly=True,
        secure=settings.SECURE_COOKIES,
        samesite="lax",
        path="/"
    )

    logger.info(f"User {login_data.username} logged in successfully", module="Auth")
    return response


@router.post("/logout")
@limiter.limit("10/minute")
@handle_exceptions
async def logout(request: Request, current_user: Dict = Depends(get_current_user)):
    """Logout and invalidate session"""
    username = current_user.get('sub', 'unknown')

    response = JSONResponse(content={
        "success": True,
        "message": "Logged out successfully",
        "timestamp": now_iso8601()
    })

    # Clear auth cookie
    response.set_cookie(
        key="auth_token",
        value="",
        max_age=0,
        httponly=True,
        secure=settings.SECURE_COOKIES,
        samesite="lax",
        path="/"
    )

    logger.info(f"User {username} logged out", module="Auth")
    return response


@router.get("/me")
@limiter.limit("30/minute")
@handle_exceptions
async def get_me(request: Request, current_user: Dict = Depends(get_current_user)):
    """Get current user information"""
    app_state = get_app_state()
    username = current_user.get('sub')

    user_info = app_state.auth.get_user(username)
    if not user_info:
        raise HTTPException(status_code=404, detail="User not found")

    return user_info


@router.post("/change-password")
@limiter.limit("5/minute")
@handle_exceptions
async def change_password(
    request: Request,
    current_password: str = Body(..., embed=True),
    new_password: str = Body(..., embed=True),
    current_user: Dict = Depends(get_current_user)
):
    """Change user password"""
    app_state = get_app_state()
    username = current_user.get('sub')
    ip_address = request.client.host if request.client else None

    # Validate new password
    if len(new_password) < 8:
        raise HTTPException(status_code=400, detail="Password must be at least 8 characters")

    result = app_state.auth.change_password(username, current_password, new_password, ip_address)

    if not result['success']:
        raise HTTPException(status_code=400, detail=result.get('error', 'Password change failed'))

    logger.info(f"Password changed for user {username}", module="Auth")
    return {
        "success": True,
        "message": "Password changed successfully",
        "timestamp": now_iso8601()
    }


@router.post("/preferences")
@limiter.limit("10/minute")
@handle_exceptions
async def update_preferences(
    request: Request,
    preferences: dict = Body(...),
    current_user: Dict = Depends(get_current_user)
):
    """Update user preferences (theme, notifications, etc.)"""
    app_state = get_app_state()
    username = current_user.get('sub')

    # Validate theme if provided
    if 'theme' in preferences:
        if preferences['theme'] not in ('light', 'dark', 'system'):
            raise HTTPException(status_code=400, detail="Invalid theme value")

    result = app_state.auth.update_preferences(username, preferences)

    if not result['success']:
        raise HTTPException(status_code=400, detail=result.get('error', 'Failed to update preferences'))

    return {
        "success": True,
        "message": "Preferences updated",
        "preferences": preferences,
        "timestamp": now_iso8601()
    }
