"""Members router: list, invite, change role.

Mutating endpoints require the ``members:manage`` permission (Owner/Admin).
"""

from __future__ import annotations

import uuid

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.orm import Session

from legate.api.deps import Principal, require
from legate.api.schemas import MemberInvite, MemberOut, RoleUpdate
from legate.audit.service import record as audit_record
from legate.billing import service as billing
from legate.db.models.membership import Membership
from legate.db.models.user import User
from legate.db.models.workspace import Workspace
from legate.db.session import get_db
from legate.policy.rbac import Permission
from legate.policy.roles import Role
from legate.repositories import users as users_repo
from legate.repositories import workspaces as workspaces_repo

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


def _to_out(membership: Membership, user: User) -> MemberOut:
    return MemberOut(
        id=membership.id,
        user_id=user.id,
        email=user.email,
        name=user.name,
        role=membership.role,
        created_at=membership.created_at,
    )


def _count_owners(db: Session, workspace_id: uuid.UUID) -> int:
    return len(
        db.scalars(
            select(Membership).where(
                Membership.workspace_id == workspace_id,
                Membership.role == Role.OWNER,
            )
        ).all()
    )


@router.get("", response_model=list[MemberOut])
def list_members(
    principal: Principal = Depends(require(Permission.VIEW)),
    db: Session = Depends(get_db),
) -> list[MemberOut]:
    members = workspaces_repo.list_members(db, workspace_id=principal.workspace_id)
    out: list[MemberOut] = []
    for m in members:
        user = users_repo.get_by_id(db, m.user_id)
        if user is not None:
            out.append(_to_out(m, user))
    return out


@router.post("/invite", response_model=MemberOut, status_code=status.HTTP_201_CREATED)
def invite_member(
    body: MemberInvite,
    principal: Principal = Depends(require(Permission.MANAGE_MEMBERS)),
    db: Session = Depends(get_db),
) -> MemberOut:
    workspace = db.get(Workspace, principal.workspace_id)
    if workspace is not None:
        billing.enforce_resource(db, workspace=workspace, resource="members")

    user = users_repo.get_by_email(db, body.email)
    if user is None:
        # TODO(decision): invited users are created as inactive, password-less
        # placeholders; the accept-invite + set-password flow lands with the dashboard.
        user = users_repo.create(db, email=body.email, password_hash=None, name="", is_active=False)

    existing = workspaces_repo.get_membership(
        db, user_id=user.id, workspace_id=principal.workspace_id
    )
    if existing is not None:
        raise HTTPException(
            status.HTTP_409_CONFLICT, detail="user is already a member of this workspace"
        )

    membership = workspaces_repo.add_member(
        db, user_id=user.id, workspace_id=principal.workspace_id, role=body.role
    )
    audit_record(
        db,
        workspace_id=principal.workspace_id,
        actor=principal.actor,
        action="member.invite",
        target=str(user.id),
        metadata={"email": body.email, "role": body.role.value},
    )
    db.commit()
    db.refresh(membership)
    return _to_out(membership, user)


@router.patch("/{membership_id}/role", response_model=MemberOut)
def update_member_role(
    membership_id: uuid.UUID,
    body: RoleUpdate,
    principal: Principal = Depends(require(Permission.MANAGE_MEMBERS)),
    db: Session = Depends(get_db),
) -> MemberOut:
    membership = workspaces_repo.get_membership_by_id(
        db, membership_id=membership_id, workspace_id=principal.workspace_id
    )
    if membership is None:
        raise HTTPException(status.HTTP_404_NOT_FOUND, detail="membership not found")

    # Never let a workspace lose its last owner.
    if (
        membership.role == Role.OWNER
        and body.role != Role.OWNER
        and _count_owners(db, principal.workspace_id) <= 1
    ):
        raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="cannot demote the last owner")

    previous = membership.role
    membership.role = body.role
    audit_record(
        db,
        workspace_id=principal.workspace_id,
        actor=principal.actor,
        action="member.role_change",
        target=str(membership.user_id),
        metadata={"from": previous.value, "to": body.role.value},
    )
    db.commit()
    db.refresh(membership)

    user = users_repo.get_by_id(db, membership.user_id)
    assert user is not None
    return _to_out(membership, user)
