import os
import uuid
import bcrypt
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
from sqlalchemy.orm import Session
from typing import List

from database import get_db
from models.user import User, AssistantAssignment, WaiterZone
from models.shift import WaiterShift
from schemas.user import UserCreate, UserUpdate, UserOut, AssistantAssignmentOut, SetZonesRequest, PermissionToggle
from routers.deps import require_staff_manager, get_current_user
from services.chat_service import add_user_to_system_group
from roles import ALL_ROLES, VALID_ROLES, get_default_permissions

router = APIRouter()

AVATAR_DIR = "/app/data/avatars"

# Permission fields that can be toggled via the API
PERMISSION_FIELDS = {
    "perm_access_dashboard",
    "perm_access_waiter_app",
    "perm_access_kds",
    "perm_cancel_orders",
    "perm_apply_discounts",
    "perm_modify_prices",
    "perm_open_orders",
    "perm_close_orders",
    "perm_view_reports",
    "perm_manage_staff",
    "perm_manage_tables",
    "perm_manage_menu",
    "perm_manage_settings",
}


# ── Helpers ───────────────────────────────────────────────────────────────────

def _user_or_404(user_id: int, db: Session) -> User:
    u = db.query(User).filter(User.id == user_id).first()
    if not u:
        raise HTTPException(status_code=404, detail="Staff member not found")
    return u


def _guard_superadmin_target(target: User, acting_user: User):
    """Prevent any modification of a superadmin by a non-superadmin."""
    if target.role == "superadmin" and acting_user.role != "superadmin":
        raise HTTPException(status_code=403, detail="Cannot modify a superadmin account")


def _apply_default_permissions(user: User, role: str):
    """Apply the default permission set for a role onto a User object."""
    for field, value in get_default_permissions(role).items():
        setattr(user, field, value)


# ── Metadata ──────────────────────────────────────────────────────────────────

@router.get("/roles")
def list_roles():
    """Return the full ordered list of available roles."""
    return ALL_ROLES


# ── CRUD ──────────────────────────────────────────────────────────────────────

@router.get("/on-shift", response_model=List[UserOut])
def list_waiters_on_shift(db: Session = Depends(get_db), user: User = Depends(get_current_user)):
    """Staff with an active (not-ended) shift. Accessible to all staff."""
    waiter_ids = db.query(WaiterShift.waiter_id).filter(WaiterShift.ended_at == None).subquery()
    return db.query(User).filter(User.id.in_(waiter_ids), User.perm_access_waiter_app == True, User.is_active == True).all()


@router.get("/", response_model=List[UserOut])
def list_staff(db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    """List all staff members regardless of role."""
    return db.query(User).order_by(User.created_at).all()


@router.post("/", response_model=UserOut, status_code=status.HTTP_201_CREATED)
def create_staff(body: UserCreate, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    if body.role not in VALID_ROLES:
        raise HTTPException(status_code=400, detail=f"Invalid role. Valid roles: {sorted(VALID_ROLES)}")
    if body.role == "superadmin" and user.role != "superadmin":
        raise HTTPException(status_code=403, detail="Only a superadmin can create another superadmin")
    if db.query(User).filter(User.username == body.username).first():
        raise HTTPException(status_code=400, detail="Username already exists")

    pin_hash = bcrypt.hashpw(body.pin.encode(), bcrypt.gensalt()).decode()
    new_user = User(
        username=body.username,
        pin_hash=pin_hash,
        role=body.role,
        is_active=body.is_active,
        full_name=body.full_name,
        nickname=body.nickname,
        mobile_phone=body.mobile_phone,
        email=body.email,
        note=body.note,
        hourly_rate=body.hourly_rate,
    )
    # Apply role defaults (caller can override via the body fields)
    _apply_default_permissions(new_user, body.role)

    # Allow explicit overrides from the request body for any perm field that was set
    for field in PERMISSION_FIELDS:
        val = getattr(body, field, None)
        if val is not None:
            setattr(new_user, field, val)

    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    # Give waiter-app users access to all zones by default
    if new_user.perm_access_waiter_app:
        db.add(WaiterZone(waiter_id=new_user.id, group_id=None))
        db.commit()

    add_user_to_system_group(db, new_user.id)
    return new_user


@router.put("/{waiter_id}", response_model=UserOut)
def update_staff(waiter_id: int, body: UserUpdate, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)

    if body.role is not None and body.role not in VALID_ROLES:
        raise HTTPException(status_code=400, detail=f"Invalid role. Valid roles: {sorted(VALID_ROLES)}")
    if body.role == "superadmin" and user.role != "superadmin":
        raise HTTPException(status_code=403, detail="Only a superadmin can assign the superadmin role")

    for field, value in body.model_dump(exclude_none=True).items():
        setattr(target, field, value)

    db.commit()
    db.refresh(target)
    return target


@router.put("/{waiter_id}/reset-pin")
def reset_pin(waiter_id: int, pin: str, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)
    target.pin_hash = bcrypt.hashpw(pin.encode(), bcrypt.gensalt()).decode()
    db.commit()
    return {"status": "pin reset"}


@router.put("/{waiter_id}/block")
def toggle_block(waiter_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)
    target.is_active = not target.is_active
    db.commit()
    return {"is_active": target.is_active}


@router.put("/{waiter_id}/permission")
def toggle_permission(
    waiter_id: int,
    body: PermissionToggle,
    db: Session = Depends(get_db),
    user: User = Depends(require_staff_manager),
):
    """Toggle a single permission flag for a staff member."""
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)

    if body.permission not in PERMISSION_FIELDS:
        raise HTTPException(status_code=400, detail=f"Unknown permission: {body.permission}")

    # Superadmins always have all permissions — we store the value but enforce it at auth time
    setattr(target, body.permission, body.value)
    db.commit()
    return {"permission": body.permission, "value": body.value}


@router.put("/{waiter_id}/reset-permissions")
def reset_permissions(waiter_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    """Reset a staff member's permissions to their role's defaults."""
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)
    _apply_default_permissions(target, target.role)
    db.commit()
    db.refresh(target)
    return target


@router.delete("/{waiter_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_staff(waiter_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    _guard_superadmin_target(target, user)
    db.delete(target)
    db.commit()


# ── Avatar upload / delete ───────────────────────────────────────────────────

@router.post("/{waiter_id}/avatar", response_model=UserOut)
async def upload_avatar(waiter_id: int, file: UploadFile = File(...), db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    if not file.content_type.startswith("image/"):
        raise HTTPException(status_code=400, detail="File must be an image")

    if target.avatar_url:
        old_path = os.path.join(AVATAR_DIR, os.path.basename(target.avatar_url))
        if os.path.exists(old_path):
            os.remove(old_path)

    ext = os.path.splitext(file.filename or "")[1] or ".jpg"
    filename = f"staff_{waiter_id}_{uuid.uuid4().hex[:8]}{ext}"
    dest = os.path.join(AVATAR_DIR, filename)
    os.makedirs(AVATAR_DIR, exist_ok=True)
    content = await file.read()
    with open(dest, "wb") as f:
        f.write(content)

    target.avatar_url = f"/static/avatars/{filename}"
    db.commit()
    db.refresh(target)
    return target


@router.delete("/{waiter_id}/avatar", response_model=UserOut)
def delete_avatar(waiter_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    target = _user_or_404(waiter_id, db)
    if target.avatar_url:
        old_path = os.path.join(AVATAR_DIR, os.path.basename(target.avatar_url))
        if os.path.exists(old_path):
            os.remove(old_path)
        target.avatar_url = None
        db.commit()
        db.refresh(target)
    return target


# ── Zone assignments ──────────────────────────────────────────────────────────

@router.put("/{waiter_id}/zones")
def set_zones(waiter_id: int, body: SetZonesRequest, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    """Replace all zone assignments for a staff member atomically.

    - all_zones=True  → single NULL group_id row  (sees everything)
    - group_ids=[1,2] → rows for groups 1 and 2 only
    - group_ids=[]    → no rows at all (sees nothing)
    """
    _user_or_404(waiter_id, db)
    db.query(WaiterZone).filter(WaiterZone.waiter_id == waiter_id).delete()

    if body.all_zones:
        db.add(WaiterZone(waiter_id=waiter_id, group_id=None))
    elif body.group_ids:
        for gid in body.group_ids:
            db.add(WaiterZone(waiter_id=waiter_id, group_id=gid))

    db.commit()
    zones = db.query(WaiterZone).filter(WaiterZone.waiter_id == waiter_id).all()
    return {"waiter_id": waiter_id, "zones": [{"id": z.id, "group_id": z.group_id} for z in zones]}


# ── Assistant assignments (kept for backwards compat) ─────────────────────────

@router.post("/{waiter_id}/assign-assistant", response_model=AssistantAssignmentOut)
def assign_assistant(waiter_id: int, assistant_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    existing = db.query(AssistantAssignment).filter(
        AssistantAssignment.primary_waiter_id == waiter_id,
        AssistantAssignment.assistant_waiter_id == assistant_id,
    ).first()
    if existing:
        raise HTTPException(status_code=400, detail="Assignment already exists")
    assignment = AssistantAssignment(primary_waiter_id=waiter_id, assistant_waiter_id=assistant_id)
    db.add(assignment)
    db.commit()
    db.refresh(assignment)
    return assignment


@router.delete("/{waiter_id}/assistant", status_code=status.HTTP_204_NO_CONTENT)
def remove_assistant(waiter_id: int, db: Session = Depends(get_db), user: User = Depends(require_staff_manager)):
    assignment = db.query(AssistantAssignment).filter(
        AssistantAssignment.primary_waiter_id == waiter_id
    ).first()
    if not assignment:
        raise HTTPException(status_code=404, detail="Assignment not found")
    db.delete(assignment)
    db.commit()
