"""
Endpoints API para autenticación
"""

from core.database import get_core_db
from core.security import get_current_user
from fastapi import APIRouter, Depends, HTTPException, Response, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.orm import Session

from .dto import (
    ExchangeCodeRequestDTO,
    LoginRequestDTO,
    LogoutRequestDTO,
    RefreshTokenRequestDTO,
    RegisterRequestDTO,
    RegisterResponseDTO,
    SetCookieRequestDTO,
    TokenResponseDTO,
    UserInfoResponseDTO,
)
from .service import AuthService

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


@router.post("/register", response_model=RegisterResponseDTO, status_code=201)
async def register(
    register_data: RegisterRequestDTO, db: Session = Depends(get_core_db)
):
    """
    Registra un nuevo usuario en Keycloak

    El usuario debe proporcionar:
    - username: Nombre de usuario único
    - email: Email único
    - password: Contraseña (mínimo 8 caracteres)
    - first_name: Nombre
    - last_name: Apellido
    - tenant_slug: Slug del tenant al que pertenece

    El usuario se crea automáticamente en Keycloak con:
    - Cuenta habilitada
    - Rol 'user' asignado por defecto
    - Atributos de tenant
    """
    service = AuthService(db)
    return service.register(register_data)


@router.post("/login", response_model=TokenResponseDTO)
async def login(
    login_data: LoginRequestDTO, 
    request: Request, # Inject Request
    db: Session = Depends(get_core_db)
):
    """
    Autentica usuario con Keycloak y retorna tokens JWT
    
    El usuario debe proporcionar:
    - username: Usuario o email
    - password: Contraseña
    - tenant_slug: Slug del tenant al que pertenece
    """
    service = AuthService(db)
    import logging
    logger = logging.getLogger(__name__)
    return service.login(
        login_data=login_data,
        ip_address=request.client.host,
        user_agent=request.headers.get("user-agent")
    )


@router.post("/refresh", response_model=TokenResponseDTO)
async def refresh_token(
    refresh_data: RefreshTokenRequestDTO, db: Session = Depends(get_core_db)
):
    """
    Refresca el access token usando el refresh token
    """
    service = AuthService(db)
    return service.refresh_token(refresh_data)


@router.get("/me", response_model=UserInfoResponseDTO)
async def get_current_user_info(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_core_db),
):
    """
    Obtiene información del usuario actual desde el token
    """
    service = AuthService(db)
    return service.get_user_info(credentials.credentials)


@router.post("/logout")
async def logout(
    logout_data: LogoutRequestDTO,
    request: Request, # Inject request for IP/User-Agent
    db: Session = Depends(get_core_db),
    # Make current_user optional to avoid 401 on expired tokens
    # We will try to use it if available, otherwise use DTO
    # Note: Depends(get_current_user) raises HTTPException if invalid, so we cannot make it optional easily without changing dependency.
    # Instead, we will rely on DTO username since user explicitly asked for this simplified flow.
    # But if we want to support both, we can't use strict dependency here if we expect it to work on expired tokens.
    # So we remove the strict dependency for now as per "simplified" request.
):
    """
    Cierra sesión invalidando el refresh token
    """
    # Extract info for logging (optional, but harmless to keep providing context if needed, 
    # but strictly speaking we can revert to just calling service)
    # The original file likely didn't have IP extraction here unless I added it. 
    # I'll keep it simple.

    service = AuthService(db)
    return service.logout(logout_data)


@router.post("/exchange-code", response_model=TokenResponseDTO)
async def exchange_code(
    exchange_data: ExchangeCodeRequestDTO, db: Session = Depends(get_core_db)
):
    """
    Intercambia un authorization code de OAuth2 por tokens

    Este endpoint es útil cuando el frontend usa el flujo de autorización
    con proveedores externos (Microsoft, Google, etc.) a través de Keycloak.

    El código se obtiene después de que el usuario se autentica con el proveedor
    externo y Keycloak lo redirige al frontend con el código en los query params.
    """
    service = AuthService(db)
    return service.exchange_code(exchange_data)


@router.post("/set-cookie")
async def set_cookie(
    cookie_data: SetCookieRequestDTO,
    response: Response,
    db: Session = Depends(get_core_db),
):
    """
    Establece cookies HttpOnly con los tokens de autenticación

    Este endpoint se llama desde el frontend después de una autenticación
    SSO exitosa para establecer las cookies de sesión necesarias para
    la validación server-side en los layouts protegidos.

    Las cookies se configuran como:
    - HttpOnly: No accesibles desde JavaScript (mayor seguridad)
    - Secure: Solo se envían por HTTPS (en producción)
    - SameSite=Lax: Protección contra CSRF
    - Max-Age: Tiempo de vida del token
    """
    # Validar que los tokens sean válidos decodificándolos
    service = AuthService(db)
    try:
        # Validar el access token
        user_info = service.get_user_info(cookie_data.access_token)

        # Establecer las cookies
        # Access token cookie
        response.set_cookie(
            key="access_token",
            value=cookie_data.access_token,
            httponly=True,  # No accesible desde JavaScript
            secure=False,  # TODO: Cambiar a True en producción con HTTPS
            samesite="lax",  # Protección CSRF
            max_age=3600,  # 1 hora (ajustar según configuración del token)
            path="/",
        )

        # Refresh token cookie
        response.set_cookie(
            key="refresh_token",
            value=cookie_data.refresh_token,
            httponly=True,
            secure=False,  # TODO: Cambiar a True en producción con HTTPS
            samesite="lax",
            max_age=86400,  # 24 horas (ajustar según configuración del token)
            path="/",
        )

        return {
            "success": True,
            "message": "Cookies establecidas correctamente",
            "user": user_info,
        }

    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Error validando tokens: {str(e)}")
