"""
Rutas para gestión de usuarios de Keycloak
"""

import logging
import mimetypes
from typing import Optional
import os
import uuid
from pathlib import Path

from core.config import settings
from core.database import get_core_db
from core.s3_keys import public_user_avatar_api_path, user_avatar_key
from core.storage_s3 import delete_object_if_exists, get_object_bytes, put_object_bytes
from core.security import (
    get_current_user,
    resolve_hub_tenant_id_for_api,
    validate_access_to_resource,
)
from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
from fastapi.responses import Response
from sqlalchemy.orm import Session

from ..user_tenant.models import UserTenant
from .dto import (
    ChangePasswordRequestDTO,
    CreateUserRequestDTO,
    UpdateUserRequestDTO,
    UserListResponseDTO,
    UserResponseDTO,
    UserStatsDTO,
)
from .service import UserService

router = APIRouter(prefix="/users", tags=["Users"])

logger = logging.getLogger(__name__)

_AVATAR_EXT = {".jpg", ".jpeg", ".png", ".gif", ".webp"}


@router.get("/stats", response_model=UserStatsDTO)
async def get_user_statistics(
    request: Request,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Obtiene estadísticas de usuarios del tenant actual
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.view"])
    service = UserService(db, tenant_id, company_id)
    auth_header = request.headers.get("Authorization") or ""
    token = (
        auth_header[7:].strip()
        if auth_header.lower().startswith("bearer ")
        else auth_header.strip()
    )
    hub_tid = resolve_hub_tenant_id_for_api(
        tenant_id, request.headers.get("X-Tenant-Override")
    )
    return service.get_user_stats(
        access_token=token or None,
        hub_tenant_id=hub_tid,
        x_tenant_override=request.headers.get("X-Tenant-Override"),
    )


@router.get("/", response_model=UserListResponseDTO)
async def list_users(
    request: Request,
    company_id: int = Query(..., description="Company ID"),
    page: int = Query(1, ge=1, description="Número de página"),
    page_size: int = Query(20, ge=1, le=100, description="Tamaño de página"),
    search: Optional[str] = Query(None, description="Término de búsqueda"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Lista todos los usuarios del tenant con paginación
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.view"])
    service = UserService(db, tenant_id, company_id)
    auth_header = request.headers.get("Authorization") or ""
    token = (
        auth_header[7:].strip()
        if auth_header.lower().startswith("bearer ")
        else auth_header.strip()
    )
    hub_tid = resolve_hub_tenant_id_for_api(
        tenant_id, request.headers.get("X-Tenant-Override")
    )
    result = await service.get_tenant_users(
        page=page,
        page_size=page_size,
        search=search,
        access_token=token,
        hub_tenant_id=hub_tid,
        x_tenant_override=request.headers.get("X-Tenant-Override"),
    )
    return result


@router.get("/avatar/{tenant_id}/{keycloak_user_id}")
def get_user_avatar_image(
    tenant_id: int,
    keycloak_user_id: str,
    db: Session = Depends(get_core_db),
):
    """
    Sirve la imagen de avatar (público para poder usarla en <img src> sin Bearer).
    El almacenamiento interno puede ser clave S3 o ruta bajo uploads/.
    """
    ut = (
        db.query(UserTenant)
        .filter(
            UserTenant.tenant_id == tenant_id,
            UserTenant.keycloak_user_id == keycloak_user_id,
            UserTenant.is_active == True,
        )
        .first()
    )
    if not ut or not ut.avatar_url:
        raise HTTPException(status_code=404, detail="Avatar not found")

    raw = ut.avatar_url
    if raw.startswith("tenants/"):
        try:
            data = get_object_bytes(raw)
        except Exception:
            raise HTTPException(status_code=404, detail="Avatar not found")
        media = mimetypes.guess_type(raw)[0] or "image/jpeg"
        return Response(content=data, media_type=media)

    rel = raw.lstrip("/")
    path = Path(rel)
    if not path.is_file():
        path = Path.cwd() / rel
    if not path.is_file():
        alt = Path("/app") / rel
        if alt.is_file():
            path = alt
    if not path.is_file():
        raise HTTPException(status_code=404, detail="Avatar file not found")
    data = path.read_bytes()
    media = mimetypes.guess_type(str(path))[0] or "image/jpeg"
    return Response(content=data, media_type=media)


# === Endpoints de Perfil del Usuario Actual ===


@router.get("/me/profile", response_model=UserResponseDTO)
async def get_my_profile(
    request: Request,
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_core_db),
):
    """
    Obtiene el perfil completo del usuario actual
    """
    keycloak_user_id = current_user.get("sub")
    if not keycloak_user_id:
        raise HTTPException(status_code=400, detail="User ID not found in token")

    # Obtener user_tenant para crear servicio
    user_tenant = (
        db.query(UserTenant)
        .filter(
            UserTenant.keycloak_user_id == keycloak_user_id,
            UserTenant.is_active == True,
        )
        .first()
    )

    if not user_tenant:
        raise HTTPException(
            status_code=400, detail="User does not belong to any tenant"
        )

    service = UserService(db, user_tenant.tenant_id, user_tenant.company_id)
    auth_header = request.headers.get("Authorization") or ""
    access_token = (
        auth_header[7:].strip()
        if auth_header.lower().startswith("bearer ")
        else auth_header.strip() or None
    )
    return await service.get_current_user_profile(
        keycloak_user_id,
        current_user=current_user,
        access_token=access_token,
    )


@router.put("/me/profile", response_model=UserResponseDTO)
async def update_my_profile(
    request: Request,
    data: UpdateUserRequestDTO,
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_core_db),
):
    """
    Actualiza el perfil del usuario actual.
    Campos editables: first_name, last_name, phone.
    Email, username y otros campos de identidad solo se cambian desde el Hub.
    """
    keycloak_user_id = current_user.get("sub")
    if not keycloak_user_id:
        raise HTTPException(status_code=400, detail="User ID not found in token")

    # Obtener user_tenant
    user_tenant = (
        db.query(UserTenant)
        .filter(
            UserTenant.keycloak_user_id == keycloak_user_id,
            UserTenant.is_active == True,
        )
        .first()
    )

    if not user_tenant:
        raise HTTPException(
            status_code=400, detail="User does not belong to any tenant"
        )

    # Para sesiones autenticadas vía Workspace/Hub, la foto de perfil viene del Hub
    # y no debe mutarse localmente en Anexo76.
    if current_user.get("sub"):
        raise HTTPException(
            status_code=409,
            detail="Avatar is managed by Workspace for this user",
        )

    auth_header = request.headers.get("Authorization") or ""
    access_token = (
        auth_header[7:].strip()
        if auth_header.lower().startswith("bearer ")
        else auth_header.strip() or None
    )

    service = UserService(db, user_tenant.tenant_id, user_tenant.company_id)
    return await service.update_current_user_profile(
        keycloak_user_id=keycloak_user_id,
        current_user=current_user,
        access_token=access_token,
        first_name=data.first_name,
        last_name=data.last_name,
        phone=data.phone,
    )


@router.post("/me/avatar")
async def upload_my_avatar(
    file: UploadFile = File(...),
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_core_db),
):
    """
    Sube un avatar para el usuario actual.
    Con MinIO guarda en tenants/{tid}/users/{sub}/avatar.{ext} y persiste la clave en UserTenant.
    Retorna URL pública para <img src> (GET /users/avatar/...).
    """
    if not file.content_type or not file.content_type.startswith("image/"):
        raise HTTPException(status_code=400, detail="El archivo debe ser una imagen")

    keycloak_user_id = current_user.get("sub")
    if not keycloak_user_id:
        raise HTTPException(status_code=400, detail="User ID not found in token")

    user_tenant = (
        db.query(UserTenant)
        .filter(
            UserTenant.keycloak_user_id == keycloak_user_id,
            UserTenant.is_active == True,
        )
        .first()
    )
    if not user_tenant:
        raise HTTPException(
            status_code=400, detail="User does not belong to any tenant"
        )

    ext = Path(file.filename or "image.jpg").suffix.lower() or ".jpg"
    if ext not in _AVATAR_EXT:
        raise HTTPException(
            status_code=400,
            detail=f"Extensión no permitida. Use: {', '.join(sorted(_AVATAR_EXT))}",
        )

    contents = await file.read()
    if len(contents) > 2 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="La imagen debe ser menor a 2MB")

    tenant_id = user_tenant.tenant_id

    try:
        if settings.use_s3_object_storage:
            if user_tenant.avatar_url and str(user_tenant.avatar_url).startswith(
                "tenants/"
            ):
                delete_object_if_exists(str(user_tenant.avatar_url))
            key = user_avatar_key(tenant_id, keycloak_user_id, ext)
            ct = file.content_type or mimetypes.guess_type(f"x{ext}")[0] or "image/jpeg"
            put_object_bytes(key, contents, content_type=ct)
            user_tenant.avatar_url = key
            logger.info(
                "User avatar stored in S3 key=%s bytes=%s", key, len(contents)
            )
        else:
            upload_dir = Path("uploads/avatars")
            upload_dir.mkdir(parents=True, exist_ok=True)
            filename = f"{keycloak_user_id}{ext}"
            file_path = upload_dir / filename
            with open(file_path, "wb") as f:
                f.write(contents)
            user_tenant.avatar_url = f"/uploads/avatars/{filename}"

        db.add(user_tenant)
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=500, detail=f"Error al guardar el avatar: {str(e)}"
        ) from e

    public_url = public_user_avatar_api_path(tenant_id, keycloak_user_id)
    return {"avatar_url": public_url}


@router.get("/{user_id}", response_model=UserResponseDTO)
async def get_user_detail(
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Obtiene información detallada de un usuario específico
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.view"])
    service = UserService(db, tenant_id, company_id)
    return await service.get_user(user_id)


@router.post("/", response_model=UserResponseDTO, status_code=201)
async def create_new_user(
    data: CreateUserRequestDTO,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Crea un nuevo usuario a través del Hub y lo asocia al tenant
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.create"])
    service = UserService(db, tenant_id, company_id)
    user = await service.create_user(
        email=data.email,
        username=data.username,
        first_name=data.first_name,
        last_name=data.last_name,
        password=data.password,
        role=data.role,
        enabled=data.enabled,
        email_verified=data.email_verified,
    )
    return user


@router.put("/{user_id}", response_model=UserResponseDTO)
async def update_user_detail(
    user_id: str,
    data: UpdateUserRequestDTO,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Actualiza información de un usuario
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.update"])
    service = UserService(db, tenant_id, company_id)
    user = await service.update_user(
        user_id=user_id,
        first_name=data.first_name,
        last_name=data.last_name,
        email=data.email,
        enabled=data.enabled,
        email_verified=data.email_verified,
        role=data.role,
        avatar_url=data.avatar_url,
        phone=data.phone,
        bio=data.bio,
        preferences=data.preferences,
    )
    return user


@router.get("/{user_id}/tenant-count")
async def get_user_tenant_count(
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Retorna en cuántos tenants está registrado el usuario.
    """
    tenant_id = validate_access_to_resource(
        db, company_id, current_user, required_permissions=["user.view"]
    )
    service = UserService(db, tenant_id, company_id)
    count = service.get_user_tenant_count(user_id)
    return {"tenant_count": count}


@router.delete("/{user_id}")
async def delete_user_route(
    request: Request,
    user_id: str,
    company_id: int = Query(..., description="Company ID"),
    soft_delete: bool = Query(
        True,
        description="Si es True, solo desactiva. Si es False, elimina permanentemente",
    ),
    scope: str = Query(
        "current",
        description="'current' para borrar solo del tenant activo, 'all' para borrar de todos los tenants",
    ),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Elimina un usuario del tenant.
    scope='current' (default): solo del tenant activo.
    scope='all': de todos los tenants en los que aparece el usuario.
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.delete"])
    auth_header = request.headers.get("Authorization") or ""
    token = (
        auth_header[7:].strip()
        if auth_header.lower().startswith("bearer ")
        else auth_header.strip()
    )
    hub_tid = resolve_hub_tenant_id_for_api(
        tenant_id, request.headers.get("X-Tenant-Override")
    )
    service = UserService(db, tenant_id, company_id)
    await service.delete_user(
        user_id,
        soft_delete=soft_delete,
        scope=scope,
        access_token=token or None,
        hub_tenant_id=hub_tid,
    )
    return {"message": "User deleted successfully"}


@router.post("/{user_id}/change-password")
async def change_user_password(
    user_id: str,
    data: ChangePasswordRequestDTO,
    company_id: int = Query(..., description="Company ID"),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Cambia la contraseña de un usuario a través del Hub
    """
    tenant_id = validate_access_to_resource(db, company_id, current_user, required_permissions=["user.update"])
    service = UserService(db, tenant_id, company_id)
    await service.change_password(user_id, data.password, data.temporary)
    return {"message": "Password changed successfully"}
