import { cloneDeep } from 'lodash-es';

export const fileActions = ['VIEW', 'DOWNLOAD', 'EDIT', 'REVIEW', 'ADD', 'REPLACE', 'REMOVE'] as const;
export type FileAction = (typeof fileActions)[number];
export type FileEffect = 'ALLOW' | 'DENY' | 'INHERIT';
export type FileActions = Partial<Record<FileAction, FileEffect>>;

export interface FilePermissionRule {
  id: string;
  formId: string;
  field: string;
  subject: 'CREATOR' | 'CURRENT_HANDLERS' | 'NODE_PARTICIPANTS' | 'NON_CURRENT_HANDLERS' | 'PARTICIPANTS';
  subjectNodeId?: string;
  trigger: 'ACTIVE' | 'CANCELLED' | 'COMPLETED' | 'ENTERED' | 'TERMINATED';
  triggerNodeId?: string;
  onReturn?: 'KEEP' | 'RELEASE';
  actions: FileActions;
}

export interface FilePermissionPolicy {
  version: 1;
  rules: FilePermissionRule[];
}

/** 仅同一表单同一附件字段保留；不要把旧授权复制到另一个表单的同名字段。 */
export function retainFilePermissions(previous: any, field: any, isSameForm: boolean): { filePermissions?: FileActions } {
  if (!isSameForm || field.__config__?.jnpfKey !== 'uploadFile' || previous?.jnpfKey !== 'uploadFile' || !Object.hasOwn(previous, 'filePermissions')) return {};
  // 保留未知值交由校验报告，不能静默删除损坏的 DENY 配置。
  return { filePermissions: cloneDeep(previous.filePermissions) };
}

/** 校验对象来自该流程版本的表单字段和节点，不能以显示名称匹配。 */
export function validateFilePermissionPolicy(policy: FilePermissionPolicy, fieldsByForm: Record<string, Set<string>>, nodeIds: Set<string>): string[] {
  if (!policy || policy.version !== 1 || !Array.isArray(policy.rules)) return ['文件权限配置版本或结构无效'];
  const errors: string[] = [];
  const ids = new Set<string>();
  for (const rule of policy.rules) {
    if (!rule || typeof rule.id !== 'string' || !rule.id.trim()) {
      errors.push('文件规则缺少 ID');
      continue;
    }
    if (ids.has(rule.id)) errors.push(`文件规则 ID 重复：${rule.id}`);
    ids.add(rule.id);
    if (!Object.hasOwn(fieldsByForm, rule.formId) || !fieldsByForm[rule.formId]?.has(rule.field)) errors.push(`附件字段不存在：${rule.id}`);
    if (!['CREATOR', 'CURRENT_HANDLERS', 'NODE_PARTICIPANTS', 'NON_CURRENT_HANDLERS', 'PARTICIPANTS'].includes(rule.subject))
      errors.push(`作用人员无效：${rule.id}`);
    if (!['ACTIVE', 'CANCELLED', 'COMPLETED', 'ENTERED', 'TERMINATED'].includes(rule.trigger)) errors.push(`生效条件无效：${rule.id}`);
    if (rule.subject === 'NODE_PARTICIPANTS' && !nodeIds.has(rule.subjectNodeId || '')) errors.push(`参与人节点不存在：${rule.id}`);
    if (['ACTIVE', 'ENTERED'].includes(rule.trigger) && !nodeIds.has(rule.triggerNodeId || '')) errors.push(`触发节点不存在：${rule.id}`);
    if (rule.trigger === 'ENTERED' && !['KEEP', 'RELEASE'].includes(rule.onReturn || '')) errors.push(`请选择退回后保留或解除规则：${rule.id}`);
    if (!rule.actions || Array.isArray(rule.actions) || typeof rule.actions !== 'object' || !Object.keys(rule.actions).length) {
      errors.push(`文件动作为空或无效：${rule.id}`);
    } else {
      for (const [action, effect] of Object.entries(rule.actions)) {
        if (!fileActions.includes(action as FileAction) || !['ALLOW', 'DENY', 'INHERIT'].includes(effect)) errors.push(`文件动作无效：${rule.id}`);
      }
    }
  }
  return errors;
}

export interface FilePermissionDesignerContext {
  attachments: { field: string; formId: string; label: string; value: string }[];
  nodes: { label: string; value: string }[];
  fieldsByForm: Record<string, Set<string>>;
  nodeIds: Set<string>;
}

/** 只使用当前流程实际存在的人工节点与原生表单字段缓存，不采纳已删除节点/其他表单的缓存。 */
export function filePermissionDesignerContext(data: Record<string, any>, liveNodeIds: Set<string>): FilePermissionDesignerContext {
  const nodes = Object.entries(data).filter(([id, node]) => liveNodeIds.has(id) && ['approver', 'processing', 'start'].includes(node?.type));
  const startFormId = nodes.find(([, node]) => node.type === 'start')?.[1]?.formId;
  const formNames = new Map<string, string>();
  for (const [, node] of nodes) {
    const formId = node.formId || startFormId;
    if (formId) formNames.set(formId, node.formName || formNames.get(formId) || formId);
  }
  const attachments: FilePermissionDesignerContext['attachments'] = [];
  const fieldsByForm: Record<string, Set<string>> = Object.create(null);
  for (const [formId, name] of formNames) {
    fieldsByForm[formId] = new Set();
    const fields = data.global?.allFormMap?.[`form_${formId}`];
    if (!Array.isArray(fields)) continue;
    for (const field of fields) {
      if (field?.__config__?.jnpfKey !== 'uploadFile' || typeof field.id !== 'string' || !field.id || fieldsByForm[formId]!.has(field.id)) continue;
      fieldsByForm[formId]!.add(field.id);
      attachments.push({ value: JSON.stringify([formId, field.id]), formId, field: field.id, label: `${name} / ${field.fullName || field.id}` });
    }
  }
  return {
    attachments,
    fieldsByForm,
    nodes: nodes.map(([id, node]) => ({ value: id, label: node.nodeName || id })),
    nodeIds: new Set(nodes.map(([id]) => id)),
  };
}

/** 编辑器可展示的结构；未知或损坏配置保留原值，不能打开后自动覆盖为无规则。 */
export function isEditableFilePermissionPolicy(value: unknown): value is FilePermissionPolicy {
  const policy = value as FilePermissionPolicy;
  return (
    !!policy &&
    policy.version === 1 &&
    Array.isArray(policy.rules) &&
    policy.rules.every(
      (rule) =>
        !!rule &&
        typeof rule === 'object' &&
        !Array.isArray(rule) &&
        typeof rule.id === 'string' &&
        !!rule.actions &&
        typeof rule.actions === 'object' &&
        !Array.isArray(rule.actions),
    )
  );
}

export function validateDesignerFilePermissions(data: Record<string, any>, liveNodeIds: Set<string>, flowType: number): string[] {
  const policies = Object.entries(data).filter(([id, node]) => (id === 'global' || liveNodeIds.has(id)) && node?.filePermissionPolicy != null);
  if (!policies.length) return [];
  if (![0, 1].includes(flowType)) return ['当前流程类型暂不支持文件权限规则'];
  const context = filePermissionDesignerContext(data, liveNodeIds);
  const ids = new Set<string>();
  const errors: string[] = [];
  for (const [, node] of policies) {
    const policy = node.filePermissionPolicy;
    errors.push(...validateFilePermissionPolicy(policy, context.fieldsByForm, context.nodeIds));
    if (!isEditableFilePermissionPolicy(policy)) continue;
    for (const rule of policy.rules) {
      if (rule.id.startsWith('@field/')) errors.push('文件规则使用了保留标识');
      if (ids.has(rule.id)) errors.push(`文件规则 ID 重复：${rule.id}`);
      ids.add(rule.id);
    }
  }
  return errors;
}
