from uuid import UUID

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

from app.api.workspace import get_verified_current_user
from app.core.permissions import Permission, Role
from app.db.session import get_db
from app.models.user import User
from app.schemas.staff import StaffBranchesUpdate, StaffCreate, StaffResponse, StaffRoleUpdate, StaffStatusUpdate
from app.services.rbac import require_permission
from app.services.staff import StaffError, add_staff, membership_for_business, set_branch_access, staff_for_business, _protect_last_ceo
from app.services.audit import record_audit

router = APIRouter(tags=["Staff"])


def _response(db: Session, membership) -> StaffResponse:
    return StaffResponse(id=membership.id, business_id=membership.business_id, user_id=membership.user_id, role=membership.role, all_branches=membership.all_branches, is_active=membership.is_active, user=db.get(User, membership.user_id))


def _error(exc: Exception):
    if isinstance(exc, LookupError):
        raise HTTPException(status.HTTP_404_NOT_FOUND, str(exc))
    if isinstance(exc, StaffError):
        raise HTTPException(status.HTTP_422_UNPROCESSABLE_CONTENT, str(exc))
    raise exc


@router.get("/api/businesses/{business_id}/staff", response_model=list[StaffResponse])
def list_staff(business_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    return [_response(db, membership) for membership, _ in staff_for_business(db, business_id)]


@router.post("/api/businesses/{business_id}/staff", response_model=StaffResponse, status_code=status.HTTP_201_CREATED)
def create_staff(business_id: UUID, data: StaffCreate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    actor = require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    if data.role == Role.CEO and actor.role != Role.CEO.value:
        raise HTTPException(status.HTTP_403_FORBIDDEN, "Only an active CEO may add another CEO.")
    try:
        return _response(db, add_staff(db, business_id, str(data.email), data.role, data.all_branches, data.branch_ids, user.id))
    except Exception as exc:
        db.rollback(); _error(exc)


@router.get("/api/businesses/{business_id}/staff/{membership_id}", response_model=StaffResponse)
def get_staff(business_id: UUID, membership_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    try: return _response(db, membership_for_business(db, business_id, membership_id))
    except Exception as exc: _error(exc)


@router.patch("/api/businesses/{business_id}/staff/{membership_id}/role", response_model=StaffResponse)
def change_role(business_id: UUID, membership_id: UUID, data: StaffRoleUpdate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    actor = require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    try:
        target = membership_for_business(db, business_id, membership_id, lock=True)
        if target.user_id == user.id and data.role == Role.CEO and target.role != Role.CEO.value:
            raise StaffError("You cannot promote yourself to CEO.")
        if data.role == Role.CEO and actor.role != Role.CEO.value:
            raise StaffError("Only an active CEO may grant CEO authority.")
        if target.role == Role.CEO.value and actor.role != Role.CEO.value:
            raise StaffError("Only an active CEO may modify a CEO membership.")
        _protect_last_ceo(db, target, new_role=data.role)
        previous_role = target.role
        target.role = data.role.value
        record_audit(db, business_id, 'STAFF_ROLE_CHANGED', 'membership', target.id, user.id, None, {
            'staff_member_id': str(target.id), 'previous_role': previous_role, 'new_role': target.role,
        })
        db.commit(); db.refresh(target)
        return _response(db, target)
    except Exception as exc:
        db.rollback(); _error(exc)


@router.patch("/api/businesses/{business_id}/staff/{membership_id}/status", response_model=StaffResponse)
def change_status(business_id: UUID, membership_id: UUID, data: StaffStatusUpdate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    actor = require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    try:
        target = membership_for_business(db, business_id, membership_id, lock=True)
        if target.role == Role.CEO.value and actor.role != Role.CEO.value:
            raise StaffError("Only an active CEO may modify a CEO membership.")
        _protect_last_ceo(db, target, active=data.is_active)
        previous_status = target.is_active
        target.is_active = data.is_active
        record_audit(db, business_id, 'STAFF_STATUS_CHANGED', 'membership', target.id, user.id, None, {
            'staff_member_id': str(target.id), 'previous_status': previous_status, 'new_status': target.is_active,
        })
        db.commit(); db.refresh(target)
        return _response(db, target)
    except Exception as exc:
        db.rollback(); _error(exc)


@router.put("/api/businesses/{business_id}/staff/{membership_id}/branches", response_model=StaffResponse)
def update_branches(business_id: UUID, membership_id: UUID, data: StaffBranchesUpdate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    actor = require_permission(db, user, business_id, Permission.MANAGE_STAFF)
    try:
        target = membership_for_business(db, business_id, membership_id, lock=True)
        if target.role == Role.CEO.value and actor.role != Role.CEO.value:
            raise StaffError("Only an active CEO may modify a CEO membership.")
        if target.user_id == user.id and data.all_branches and not actor.all_branches:
            raise StaffError("You cannot grant yourself all-branch access.")
        return _response(db, set_branch_access(db, target, data.all_branches, data.branch_ids, user.id))
    except Exception as exc:
        db.rollback(); _error(exc)
