import hashlib
import secrets
from datetime import datetime, timedelta, timezone
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.models.platform_audit import PlatformAuditLog
from app.models.platform_staff import PlatformRoleDefinition, PlatformStaffInvitation
from app.models.user import User
from app.schemas.admin import PlatformReasonInput, PlatformRoleInput, PlatformStaffAccessInput, PlatformStaffInviteInput, PlatformStaffResponse
from app.services.email_sender import send_password_reset_email, send_platform_staff_invitation
from app.services.password_reset import create_password_reset
from app.services.platform_access import PLATFORM_PERMISSIONS, SYSTEM_ROLE_PERMISSIONS, effective_platform_permissions, is_super_admin, may_manage_target, require_platform_permission
from app.services.platform_audit import record_platform_audit

router = APIRouter(prefix="/api/admin", tags=["AAD Platform Admin"])

def _permissions(values: list[str]) -> list[str]:
    result = sorted({value.strip() for value in values if value.strip()})
    invalid = set(result) - PLATFORM_PERMISSIONS
    if invalid:
        raise HTTPException(422, f"Unknown platform permissions: {', '.join(sorted(invalid))}.")
    return result

def _role(db: Session, value: str) -> str:
    key = value.strip().upper()
    if key == "CEO":
        raise HTTPException(422, "The CEO role cannot be assigned.")
    if key in SYSTEM_ROLE_PERMISSIONS:
        return key
    if not db.scalar(select(PlatformRoleDefinition).where(PlatformRoleDefinition.key == key, PlatformRoleDefinition.is_active.is_(True))):
        raise HTTPException(422, "Select an active platform role.")
    return key

def _target(db: Session, user_id: UUID) -> User:
    target = db.get(User, user_id)
    if target is None or not target.is_platform_admin:
        raise HTTPException(404, "Platform staff member not found.")
    return target

def _last_super(db: Session, target: User) -> bool:
    if (target.platform_role or "").upper() != "SUPER_ADMIN" or target.platform_staff_status != "ACTIVE":
        return False
    count = db.scalar(select(func.count()).select_from(User).where(User.is_platform_admin.is_(True), User.platform_staff_status == "ACTIVE", User.platform_role == "SUPER_ADMIN")) or 0
    return count <= 1

def _authorize_sensitive(actor: User, target: User) -> None:
    if target.id == actor.id or not may_manage_target(actor, target):
        raise HTTPException(403, "You cannot act on an equal or higher-ranked administrator.")

@router.get("/me")
def me(staff: User = Depends(require_platform_permission("platform.view")), db: Session = Depends(get_db)):
    return {"staff": PlatformStaffResponse.model_validate(staff), "permissions": sorted(effective_platform_permissions(db, staff))}

@router.get("/roles")
def roles(_: User = Depends(require_platform_permission("staff.read")), db: Session = Depends(get_db)):
    names = {"SUPER_ADMIN": "Super Admin", "OPERATIONS": "Operations", "SUPPORT": "Support", "FINANCE": "Finance", "MARKETING": "Marketing", "READ_ONLY": "Read Only"}
    system = [{"key": key, "name": name, "permissions": sorted(SYSTEM_ROLE_PERMISSIONS[key]), "is_system": True, "is_active": True} for key, name in names.items()]
    return {"system_roles": system, "custom_roles": list(db.scalars(select(PlatformRoleDefinition))), "available_permissions": sorted(PLATFORM_PERMISSIONS)}

@router.post("/roles", status_code=201)
def create_role(payload: PlatformRoleInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    if not is_super_admin(staff):
        raise HTTPException(403, "Only a Super Admin can create platform roles.")
    key = payload.key.strip().upper()
    if key in SYSTEM_ROLE_PERMISSIONS or db.scalar(select(PlatformRoleDefinition).where(PlatformRoleDefinition.key == key)):
        raise HTTPException(409, "That role key is reserved or already exists.")
    role = PlatformRoleDefinition(key=key, name=payload.name.strip(), description=payload.description, permissions=_permissions(payload.permissions), created_by=staff.id)
    db.add(role); db.flush(); record_platform_audit(db, staff.id, "PLATFORM_ROLE_CREATED", "platform_role", role.id, details={"key": key}); db.commit(); db.refresh(role)
    return role

@router.get("/staff", response_model=list[PlatformStaffResponse])
def staff_list(_: User = Depends(require_platform_permission("staff.read")), db: Session = Depends(get_db)):
    return list(db.scalars(select(User).where(User.is_platform_admin.is_(True)).order_by(User.created_at.desc())))

@router.post("/staff/invitations", status_code=201)
def invite(payload: PlatformStaffInviteInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    if not is_super_admin(staff):
        raise HTTPException(403, "A Super Admin must approve platform staff invitations.")
    role_key, raw = _role(db, payload.role_key), secrets.token_urlsafe(32)
    invitation = PlatformStaffInvitation(email=str(payload.email).lower(), role_key=role_key, permission_overrides=_permissions(payload.permission_overrides), token_hash=hashlib.sha256(raw.encode()).hexdigest(), invited_by=staff.id, expires_at=datetime.now(timezone.utc) + timedelta(hours=72))
    db.add(invitation); db.flush()
    try:
        send_platform_staff_invitation(invitation.email, raw)
    except Exception:
        db.rollback(); raise HTTPException(503, "Invitation email could not be delivered.")
    record_platform_audit(db, staff.id, "PLATFORM_STAFF_INVITED", "platform_staff_invitation", invitation.id, details={"email": invitation.email, "role_key": role_key}); db.commit()
    return {"id": invitation.id, "email": invitation.email, "role_key": role_key, "expires_at": invitation.expires_at}

@router.patch("/staff/{user_id}/access", response_model=PlatformStaffResponse)
def update_access(user_id: UUID, payload: PlatformStaffAccessInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    if not is_super_admin(staff):
        raise HTTPException(403, "Only a Super Admin can change platform access.")
    target = _target(db, user_id); _authorize_sensitive(staff, target); role_key = _role(db, payload.role_key)
    if _last_super(db, target) and role_key != "SUPER_ADMIN":
        raise HTTPException(409, "The last active Super Admin cannot be demoted.")
    before = {"role": target.platform_role, "permissions": target.platform_permissions}
    target.platform_role, target.platform_permissions, target.platform_staff_status = role_key, _permissions(payload.permission_overrides), "ACTIVE"
    target.platform_session_version += 1
    record_platform_audit(db, staff.id, "PLATFORM_STAFF_ACCESS_CHANGED", "user", target.id, payload.reason, {"before": before, "after": {"role": role_key, "permissions": target.platform_permissions}}); db.commit(); db.refresh(target)
    return target

def _status_action(db, actor, target, status_value, action, reason):
    _authorize_sensitive(actor, target)
    if status_value != "ACTIVE" and _last_super(db, target):
        raise HTTPException(409, "The last active Super Admin cannot be suspended or removed.")
    target.platform_staff_status = status_value; target.platform_session_version += 1
    record_platform_audit(db, actor.id, action, "user", target.id, reason); db.commit(); db.refresh(target); return target

@router.post("/staff/{user_id}/suspend", response_model=PlatformStaffResponse)
def suspend(user_id: UUID, payload: PlatformReasonInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    return _status_action(db, staff, _target(db, user_id), "SUSPENDED", "PLATFORM_STAFF_SUSPENDED", payload.reason)

@router.post("/staff/{user_id}/reactivate", response_model=PlatformStaffResponse)
def reactivate(user_id: UUID, payload: PlatformReasonInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    return _status_action(db, staff, _target(db, user_id), "ACTIVE", "PLATFORM_STAFF_REACTIVATED", payload.reason)

@router.post("/staff/{user_id}/remove-access")
def remove(user_id: UUID, payload: PlatformReasonInput, staff: User = Depends(require_platform_permission("staff.manage")), db: Session = Depends(get_db)):
    target = _target(db, user_id); _authorize_sensitive(staff, target)
    if _last_super(db, target):
        raise HTTPException(409, "The last active Super Admin cannot be removed.")
    target.is_platform_admin = False; target.platform_role = None; target.platform_permissions = []; target.platform_staff_status = "REVOKED"; target.platform_session_version += 1
    record_platform_audit(db, staff.id, "PLATFORM_STAFF_ACCESS_REMOVED", "user", target.id, payload.reason); db.commit()
    return {"id": target.id, "platform_access_removed": True}

@router.post("/staff/{user_id}/revoke-sessions")
def revoke(user_id: UUID, payload: PlatformReasonInput, staff: User = Depends(require_platform_permission("security.manage")), db: Session = Depends(get_db)):
    target = _target(db, user_id); _authorize_sensitive(staff, target); target.platform_session_version += 1
    record_platform_audit(db, staff.id, "PLATFORM_STAFF_SESSIONS_REVOKED", "user", target.id, payload.reason); db.commit()
    return {"id": target.id, "sessions_revoked": True}

@router.post("/staff/{user_id}/password-reset")
def reset(user_id: UUID, payload: PlatformReasonInput, staff: User = Depends(require_platform_permission("security.manage")), db: Session = Depends(get_db)):
    target = _target(db, user_id); _authorize_sensitive(staff, target); token = create_password_reset(db, target)
    try:
        send_password_reset_email(target.email, token)
    except Exception:
        db.rollback(); raise HTTPException(503, "Password reset email could not be delivered.")
    target.platform_session_version += 1; record_platform_audit(db, staff.id, "PLATFORM_STAFF_PASSWORD_RESET_REQUIRED", "user", target.id, payload.reason); db.commit()
    return {"id": target.id, "delivery": "EMAIL_SENT"}

@router.get("/audit")
def audit(_: User = Depends(require_platform_permission("audit.read")), db: Session = Depends(get_db)):
    return list(db.scalars(select(PlatformAuditLog).order_by(PlatformAuditLog.created_at.desc()).limit(200)))
