/**
 * 权限控制组件
 *
 * 按钮级权限控制 — 无权限时隐藏或禁用子元素：
 *
 * @example
 * // 无权限时隐藏
 * <Permission code="device:operate">
 *   <Button>开始充电</Button>
 * </Permission>
 *
 * @example
 * // 无权限时禁用
 * <Permission code="device:operate" fallback="disabled">
 *   <Button>开始充电</Button>
 * </Permission>
 *
 * @example
 * // 多权限码（全部满足）
 * <Permission code={["device:view", "device:operate"]}>
 *   <Button>操作设备</Button>
 * </Permission>
 */

import { Children, cloneElement, isValidElement } from 'react';
import type { ReactElement, ReactNode } from 'react';
import { hasPermission, hasAllPermissions } from '@/utils/permission';

interface PermissionProps {
  /** 权限码或权限码数组（数组时需全部满足） */
  code: string | string[];
  /**
   * 无权限时的行为：
   * - "hidden"（默认）：不渲染子元素
   * - "disabled"：渲染但禁用子元素
   */
  fallback?: 'hidden' | 'disabled';
  /** 子元素 */
  children: ReactNode;
}

export function Permission({ code, fallback = 'hidden', children }: PermissionProps) {
  // 判断是否有权限
  const permitted = Array.isArray(code)
    ? hasAllPermissions(code)
    : hasPermission(code);

  // 有权限 — 正常渲染
  if (permitted) {
    return <>{children}</>;
  }

  // hidden 模式 — 不渲染
  if (fallback === 'hidden') {
    return null;
  }

  // disabled 模式 — 给子元素添加 disabled 属性
  return (
    <>
      {Children.map(children, (child) => {
        if (isValidElement(child)) {
          return cloneElement(child as ReactElement<{ disabled?: boolean }>, {
            disabled: true,
          });
        }
        return child;
      })}
    </>
  );
}

/**
 * 页面级权限控制 — 无权限时显示替代内容
 *
 * @example
 * <RequirePermission code="energy:view" fallback={<NoPermission />}>
 *   <EnergyPage />
 * </RequirePermission>
 */
interface RequirePermissionProps {
  /** 权限码 */
  code: string;
  /** 无权限时显示的内容 */
  fallback?: ReactNode;
  /** 子元素 */
  children: ReactNode;
}

export function RequirePermission({ code, fallback, children }: RequirePermissionProps) {
  const permitted = hasPermission(code);

  if (permitted) {
    return <>{children}</>;
  }

  if (fallback) {
    return <>{fallback}</>;
  }

  return null;
}
