"""User CRUD (admin/ceo/rh only) + listing for assignments."""
from datetime import datetime, timezone, timedelta
from bson import ObjectId
from bson.errors import InvalidId
from fastapi import APIRouter, Depends, HTTPException

from auth import hash_password
from deps import get_db, get_current_user, require_roles
from models import CreateUserIn, UpdateUserIn, UserOut, USER_MGMT_ROLES
from emailer import send_email, render_invite
from routes_activation import generate_random_password, generate_activation_token, activation_link

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

_user_mgmt = require_roles(*USER_MGMT_ROLES)


def _ser(u: dict) -> dict:
    return {
        "id": str(u["_id"]),
        "email": u["email"],
        "name": u.get("name", ""),
        "role": u.get("role", "collaborator"),
        "department_id": u.get("department_id"),
        "avatar_url": u.get("avatar_url"),
        "active": u.get("active", True),
        "must_change_password": u.get("must_change_password", False),
        "created_at": u.get("created_at"),
    }


@router.get("", response_model=list[UserOut])
async def list_users(user: dict = Depends(get_current_user)):
    if user["role"] == "client":
        raise HTTPException(status_code=403, detail="Sin acceso")
    db = get_db()
    cursor = db.users.find({}).sort("created_at", -1)
    return [_ser(u) async for u in cursor]


@router.post("")
async def create_user(payload: CreateUserIn, current: dict = Depends(_user_mgmt)):
    db = get_db()
    email = payload.email.lower().strip()
    if await db.users.find_one({"email": email}):
        raise HTTPException(status_code=400, detail="El email ya está registrado")

    random_pwd = generate_random_password()
    token = generate_activation_token()
    expires_at = (datetime.now(timezone.utc) + timedelta(days=7)).isoformat()

    doc = {
        "email": email,
        "name": payload.name,
        "password_hash": hash_password(random_pwd),
        "role": payload.role,
        "department_id": payload.department_id,
        "avatar_url": None,
        "active": False,
        "must_change_password": True,
        "activation_token": token,
        "activation_expires_at": expires_at,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "created_by": current["id"],
    }
    result = await db.users.insert_one(doc)
    doc["_id"] = result.inserted_id

    if payload.send_invite:
        link = activation_link(token)
        html = render_invite(payload.name, email, random_pwd, payload.role, link)
        await send_email(email, "Activación de tu cuenta — 5to sueño", html)

    return {**_ser(doc), "_activation_link": activation_link(token), "_temp_password": random_pwd}


@router.patch("/{user_id}", response_model=UserOut)
async def update_user(user_id: str, payload: UpdateUserIn, _: dict = Depends(_user_mgmt)):
    db = get_db()
    try:
        oid = ObjectId(user_id)
    except InvalidId:
        raise HTTPException(status_code=400, detail="ID inválido")
    update: dict = {}
    if payload.name is not None:
        update["name"] = payload.name
    if payload.role is not None:
        update["role"] = payload.role
    if payload.department_id is not None:
        update["department_id"] = payload.department_id or None
    if payload.password is not None:
        update["password_hash"] = hash_password(payload.password)
        update["must_change_password"] = False
    if not update:
        raise HTTPException(status_code=400, detail="Nada para actualizar")
    res = await db.users.find_one_and_update({"_id": oid}, {"$set": update}, return_document=True)
    if not res:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
    return _ser(res)


@router.post("/{user_id}/resend-activation")
async def resend_activation(user_id: str, _: dict = Depends(_user_mgmt)):
    db = get_db()
    try: oid = ObjectId(user_id)
    except InvalidId: raise HTTPException(status_code=400, detail="ID inválido")
    user = await db.users.find_one({"_id": oid})
    if not user: raise HTTPException(status_code=404, detail="Usuario no encontrado")
    if user.get("active") and not user.get("must_change_password"):
        raise HTTPException(status_code=400, detail="El usuario ya está activo")
    token = generate_activation_token()
    expires = (datetime.now(timezone.utc) + timedelta(days=7)).isoformat()
    new_pwd = generate_random_password()
    await db.users.update_one(
        {"_id": oid},
        {"$set": {
            "activation_token": token,
            "activation_expires_at": expires,
            "password_hash": hash_password(new_pwd),
            "must_change_password": True,
        }},
    )
    link = activation_link(token)
    html = render_invite(user["name"], user["email"], new_pwd, user["role"], link)
    await send_email(user["email"], "Nuevo enlace de activación — 5to sueño", html)
    return {"ok": True, "activation_link": link}


@router.delete("/{user_id}")
async def delete_user(user_id: str, current: dict = Depends(_user_mgmt)):
    db = get_db()
    try:
        oid = ObjectId(user_id)
    except InvalidId:
        raise HTTPException(status_code=400, detail="ID inválido")
    if str(oid) == current["id"]:
        raise HTTPException(status_code=400, detail="No puedes eliminarte a ti mismo")
    res = await db.users.delete_one({"_id": oid})
    if res.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
    return {"ok": True}
