const WORKFLOW_RUN_DRAFT_PREFIX = 'easyflow:workflow-run-draft';
const WORKFLOW_RUN_DRAFT_VERSION = 1;

export const WORKFLOW_RUN_DRAFT_TTL_MS = 12 * 60 * 60 * 1000;

interface WorkflowRunDraftPayload {
  question: string;
  values: Record<string, unknown>;
}

interface StoredWorkflowRunDraft extends WorkflowRunDraftPayload {
  expiresAt: number;
  version: number;
}

type DraftStorage = Pick<Storage, 'getItem' | 'removeItem' | 'setItem'>;

/** 获取可用的会话存储。 */
export function getWorkflowRunDraftStorage() {
  try {
    return globalThis.sessionStorage;
  } catch {
    return undefined;
  }
}

/** 生成按工作流、账号和运行模式隔离的草稿键。 */
export function buildWorkflowRunDraftKey(
  workflowId: string,
  identity: string,
  shareMode: boolean,
) {
  const mode = shareMode ? 'share' : 'private';
  const scope = shareMode ? 'public' : identity || 'anonymous';
  return [
    WORKFLOW_RUN_DRAFT_PREFIX,
    `v${WORKFLOW_RUN_DRAFT_VERSION}`,
    mode,
    encodeURIComponent(scope),
    encodeURIComponent(workflowId),
  ].join(':');
}

/** 读取并按当前工作流参数定义过滤草稿。 */
export function readWorkflowRunDraft(
  storage: DraftStorage | undefined,
  key: string,
  parameters: any[],
  now = Date.now(),
): undefined | WorkflowRunDraftPayload {
  if (!storage || !key) {
    return undefined;
  }
  try {
    const raw = storage.getItem(key);
    if (!raw) {
      return undefined;
    }
    const draft = JSON.parse(raw) as Partial<StoredWorkflowRunDraft>;
    if (
      draft.version !== WORKFLOW_RUN_DRAFT_VERSION ||
      typeof draft.expiresAt !== 'number' ||
      draft.expiresAt <= now ||
      typeof draft.question !== 'string' ||
      !draft.values ||
      typeof draft.values !== 'object' ||
      Array.isArray(draft.values)
    ) {
      storage.removeItem(key);
      return undefined;
    }
    const values: Record<string, unknown> = {};
    for (const parameter of parameters) {
      const name = String(parameter?.name || '').trim();
      if (
        name &&
        Object.prototype.hasOwnProperty.call(draft.values, name) &&
        isCompatibleDraftValue(parameter, draft.values[name])
      ) {
        values[name] = draft.values[name];
      }
    }
    return { question: draft.question, values };
  } catch {
    try {
      storage.removeItem(key);
    } catch {
      // 存储不可用时无需影响页面加载。
    }
    return undefined;
  }
}

/** 保存工作流运行草稿，并设置 12 小时过期时间。 */
export function writeWorkflowRunDraft(
  storage: DraftStorage | undefined,
  key: string,
  draft: WorkflowRunDraftPayload,
  now = Date.now(),
) {
  if (!storage || !key) {
    return;
  }
  try {
    storage.setItem(
      key,
      JSON.stringify({
        ...draft,
        expiresAt: now + WORKFLOW_RUN_DRAFT_TTL_MS,
        version: WORKFLOW_RUN_DRAFT_VERSION,
      } satisfies StoredWorkflowRunDraft),
    );
  } catch {
    // 存储不可用或空间不足时不阻断工作流输入。
  }
}

/** 删除工作流运行草稿。 */
export function removeWorkflowRunDraft(
  storage: DraftStorage | undefined,
  key: string,
) {
  if (!storage || !key) {
    return;
  }
  try {
    storage.removeItem(key);
  } catch {
    // 存储不可用时无需影响重置流程。
  }
}

/** 判断当前输入是否包含需要持久化的用户修改。 */
export function hasWorkflowRunDraftContent(
  question: string,
  values: Record<string, unknown>,
  defaults: Record<string, unknown>,
) {
  if (question.length > 0) {
    return true;
  }
  return Object.keys(values).some(
    (name) => !isSameDraftValue(values[name], defaults[name]),
  );
}

function isCompatibleDraftValue(parameter: any, value: unknown) {
  const contentType = String(parameter?.contentType || '').toLowerCase();
  const formType = String(parameter?.formType || '').toLowerCase();
  if (contentType === 'file' || formType === 'checkbox') {
    return Array.isArray(value);
  }
  if (contentType === 'image') {
    return Boolean(value) && typeof value === 'object';
  }
  return (
    value === null ||
    typeof value === 'boolean' ||
    typeof value === 'number' ||
    typeof value === 'string'
  );
}

function isSameDraftValue(left: unknown, right: unknown) {
  if (left === right) {
    return true;
  }
  try {
    return JSON.stringify(left) === JSON.stringify(right);
  } catch {
    return false;
  }
}
