import { buildDmsPrintBarcode, type DmsPrintBarcodeBuilder } from './dmsPrintBarcode';
import type { DmsPrintAttempt, DmsPrintBatch } from './dmsPrintAttempt';
import { v4 as uuidv4 } from 'uuid';
import { defHttp } from '#/api/request';
import { onlineUtils } from '#/utils/jnpf';
import { printManagedDmsDocument } from './dmsHiprint';
import { attemptBatches } from './dmsPrintAttempt';

const MODEL_ID = '37a318b30d7c4daf9670d141b33ed1b7';
const endpoint = '/api/dms/biz/wenjian/dayin/chongda/';
export interface DmsReprintAttempt extends DmsPrintAttempt {
  printRecordId: string;
  reason: string;
  bizSign: string;
  createdAt: number | string;
  submittedAt?: number | string;
  documentId?: string;
}
export interface DmsReprintLockStatus {
  locked: boolean;
  requestId?: string;
  printRecordId: string;
  userId?: string;
  userName?: string;
  reason?: string;
  createdAt?: number | string;
  phase?: 'READY' | 'UNKNOWN' | 'PARTIAL' | 'REGISTRATION_PENDING';
  owner?: boolean;
  canCancel?: boolean;
  canResume?: boolean;
  submittedBatches?: number;
  totalBatches?: number;
}
export const REPRINT_LOCK_EVENT = 'DMS_REPRINT_LOCK_STATUS';
export const REPRINT_COMPLETED_EVENT = 'DMS_REPRINT_COMPLETED';
export interface DmsReprintLockDialog {
  buildCustomBarcode?: DmsPrintBarcodeBuilder;
  watermarkBusinessTypes?: string[];
  status: DmsReprintLockStatus;
  onConfirm?: () => void;
}
interface NativeSubmitContext {
  formData: Record<string, any>;
  params?: Record<string, any>;
  setFormData: (field: string, value: any) => void;
  formContext: {
    closeReprintForm?: () => Promise<void>;
    refreshReprint?: () => void;
    isEdit: boolean;
    submit: (resolve: (value: any) => void, reject: (error: unknown) => void) => void;
  };
}
async function post(action: string, data: unknown): Promise<DmsReprintAttempt> {
  const res = await defHttp.post<{ code: number; data: DmsReprintAttempt; msg?: string }>({ url: endpoint + action, data }, { errorMessageMode: 'none' });
  if (res.code !== 200) throw new Error(res.msg || '保存重打进度失败');
  return res.data;
}
function update(attempt: DmsPrintAttempt, action: string, data: Record<string, unknown> = {}) {
  return post(action, { requestId: attempt.id, printRecordId: (attempt as DmsReprintAttempt).printRecordId, ...data });
}
function receiptKey(attempt: DmsReprintAttempt, batch: DmsPrintBatch) {
  return `dms-reprint-receipt:${encodeURIComponent(attempt.tenantId)}:${encodeURIComponent(attempt.userId)}:${attempt.id}:${batch.id}`;
}
/** 局部实例锁防止同一表单的连点和迟到回调并发保存，服务端唯一关联兜底。 */
const submissions = new Map<string, Promise<any>>();

export async function getDmsReprintLockStatus(recordId: string): Promise<DmsReprintLockStatus> {
  const res = await defHttp.get<{ code: number; data: DmsReprintLockStatus; msg?: string }>({
    url: `${endpoint}${encodeURIComponent(recordId)}/lock-status`,
  });
  if (res.code !== 200) throw new Error(res.msg || '无法读取重打状态');
  return res.data;
}

export async function openDmsReprint(recordId: string, onConfirm?: () => void, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]) {
  if (!recordId) throw new Error('打印记录ID不能为空');
  const status = await getDmsReprintLockStatus(recordId);
  if (status.locked) {
    onlineUtils.getEmitter().emit(REPRINT_LOCK_EVENT, { status, onConfirm, buildCustomBarcode, ...(watermarkBusinessTypes ? { watermarkBusinessTypes } : {}) });
    return;
  }
  openNativeReprint(recordId, undefined, onConfirm, buildCustomBarcode, watermarkBusinessTypes);
}

async function getActive(recordId: string) {
  const res = await defHttp.get<{ code: number; data?: DmsReprintAttempt; msg?: string }>({
    url: `${endpoint}${encodeURIComponent(recordId)}/active`,
  });
  if (res.code !== 200) throw new Error(res.msg || '无法读取重打状态');
  return res.data;
}

export async function resumeDmsReprint(recordId: string, requestId: string, onConfirm?: () => void, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]) {
  let existing = await getActive(recordId);
  if (!existing || existing.id !== requestId) throw new Error('原任务状态已变化，请刷新状态后操作');
  if (attemptBatches(existing).some((batch) => batch.state === 'SENDING')) throw new Error('发送结果尚未确认，请先刷新状态并补交已有回执');
  existing = await update(existing, 'zhuangtai');
  if (existing.documentId && existing.state !== 'OPEN') {
    await sendDmsReprint(existing, undefined, buildCustomBarcode, watermarkBusinessTypes);
    onConfirm?.();
  } else if (existing.documentId) await openReprintPreview(existing, onConfirm, undefined, buildCustomBarcode, watermarkBusinessTypes);
  else openNativeReprint(recordId, existing, onConfirm, buildCustomBarcode, watermarkBusinessTypes);
}

export async function cancelDmsReprint(recordId: string, requestId: string) {
  await post('quxiao-fasong', { printRecordId: recordId, requestId });
}

/** 只补交当前浏览器已经收到的成功回执；不能把未知状态推定为成功或失败。 */
export async function refreshDmsReprintLock(status: DmsReprintLockStatus) {
  if (status.owner) {
    let active = await getActive(status.printRecordId);
    if (active && active.id === status.requestId) {
      for (const batch of attemptBatches(active)) {
        const key = receiptKey(active, batch);
        let token: string | null = null;
        try {
          token = localStorage.getItem(key);
        } catch {
          /* 未保存回执时保持未知。 */
        }
        if (!token) continue;
        active = await update(active, 'queren-pici', { batchId: batch.id, token });
        try {
          localStorage.removeItem(key);
        } catch {
          /* 服务端回执幂等。 */
        }
      }
    }
  }
  return getDmsReprintLockStatus(status.printRecordId);
}

function openNativeReprint(recordId: string, existing?: DmsReprintAttempt, onConfirm?: () => void, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]) {
  const requestId = existing?.id || uuidv4();
  onlineUtils.openFormModal({
    modelId: MODEL_ID,
    mode: 'form',
    submitMode: 'form',
    params: {
      dmsReprint: true,
      buildCustomBarcode,
      watermarkBusinessTypes,
      recovering: !!existing,
      requestId,
      printRecordId: recordId,
      bupingheng_mingxi: [{ dayin_jilu_id: recordId, cuoshi: 'cuoshi3', yuanyin: existing?.reason || '' }],
      chongda_fasong_id: requestId,
    },
    fieldMapping: { bupingheng_mingxi: 'bupingheng_mingxi', chongda_fasong_id: 'chongda_fasong_id' },
    onConfirm,
  });
}

/** 由签名后的原生customSubmit调用；原生formContext.submit仍负责主子表CRUD。 */
export function submitDmsReprint(context: NativeSubmitContext): Promise<any> {
  const id = String(context.params?.requestId || context.formData.chongda_fasong_id || '');
  if (!id) return Promise.reject(new Error('请从打印记录重打入口操作'));
  const current = submissions.get(id);
  if (current) return current;
  const promise = execute(context, id).finally(() => submissions.delete(id));
  submissions.set(id, promise);
  return promise;
}
async function execute(context: NativeSubmitContext, id: string) {
  const rows = context.formData.bupingheng_mingxi;
  if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.cuoshi !== 'cuoshi3' || context.formContext.isEdit)
    throw new Error('重打必须由单条原打印记录发起，已保存记录不得再次执行');
  let attempt = await post('zhunbei-fasong', {
    requestId: id,
    printRecordId: rows[0].dayin_jilu_id,
    reason: rows[0].yuanyin,
    bizSign: context.formData.biz_sign,
  });
  // 重试保留第一次发送的签名和原因，不允许新输入改变已经发生的重打。
  function freezeForm() {
    context.setFormData('chongda_fasong_id', attempt.id);
    context.setFormData('biz_sign', attempt.bizSign);
    context.setFormData('dengji_ren_id', attempt.userId);
    context.setFormData('dengji_shijian', new Date(attempt.createdAt).getTime());
    context.setFormData('bupingheng_mingxi', [
      { ...rows[0], dayin_jilu_id: attempt.printRecordId, cuoshi: 'cuoshi3', yuanyin: attempt.reason, xin_dayin_renwu_id: '' },
    ]);
  }
  freezeForm();
  attempt = await update(attempt, 'zhuangtai');
  const saveRegistration = async () => {
    const result: any = await new Promise((resolve, reject) => context.formContext.submit(resolve, reject));
    if (result?.code !== 200) throw new Error(result?.msg || '不平衡登记保存失败');
  };
  // 恢复已成功发送的任务只补原生登记，不再打开预览或派发。
  if (attempt.state === 'CONFIRMED' || attempt.state === 'CLOSED') {
    await completeRegistration(attempt, saveRegistration);
    const completed = await update(attempt, 'zhuangtai');
    return { code: 200, msg: '重打不平衡登记已完成', data: { id: completed.documentId } };
  }
  await context.formContext.closeReprintForm?.();
  let msg = '重打信息已暂存，请在预览中手动打印，发送成功后自动登记不平衡';
  try {
    await openReprintPreview(attempt, context.formContext.refreshReprint, saveRegistration, context.params?.buildCustomBarcode, context.params?.watermarkBusinessTypes);
  } catch (error: any) {
    msg = `重打信息已暂存，预览打开失败：${error?.message || '网络异常'}。请从原打印记录点击重打继续，尚未新增不平衡登记`;
  }
  return { code: 200, msg, dmsReprintPrepared: true };
}

// 发送成功与原生保存分属两个系统：以任务状态和唯一关联恢复，失败时只补登记。
const registrations = new Map<string, Promise<void>>();
function completeRegistration(original: DmsReprintAttempt, saveRegistration?: () => Promise<void>): Promise<void> {
  const current = registrations.get(original.id);
  if (current) return current;
  const promise = (async () => {
    let attempt = await update(original, 'zhuangtai');
    if (!['CONFIRMED', 'CLOSED'].includes(attempt.state)) throw new Error('尚未收到全部打印发送成功回传，不能登记不平衡');
    if (attempt.state !== 'CLOSED') {
      try {
        if (!attempt.documentId) {
          if (!saveRegistration) throw new Error('请从原打印记录继续任务，补存不平衡登记');
          try {
            await saveRegistration();
          } catch (error) {
            attempt = await update(attempt, 'zhuangtai');
            if (!attempt.documentId) throw error;
          }
          attempt = await update(attempt, 'zhuangtai');
          if (!attempt.documentId) throw new Error('尚未确认原生登记保存结果，请恢复原任务核对');
        }
        attempt = await update(attempt, 'jieshu-fasong');
      } catch (error: any) {
        throw new Error(`打印已发送成功，但不平衡登记未完成：${error?.message || '网络异常'}。请继续原任务补登记，系统不会重复打印`);
      }
    }
    onlineUtils.getEmitter().emit(REPRINT_COMPLETED_EVENT, { printRecordId: attempt.printRecordId, requestId: attempt.id });
  })().finally(() => registrations.delete(original.id));
  registrations.set(original.id, promise);
  return promise;
}

async function openReprintPreview(attempt: DmsReprintAttempt, onPrintSuccess?: () => void, saveRegistration?: () => Promise<void>, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]) {
  const numbers = JSON.parse(attempt.numbersJson || '[]');
  const number = numbers[0];
  if (!number?.serialNumber) throw new Error('原流水缺失，不能打开重打预览');
  const opened = await onlineUtils.previewDmsFile({
    wenjianZhongxinId: attempt.fileVersionId,
    previewFormat: 'pdf',
    watermarkType: 'print',
    customBarcode: await buildDmsPrintBarcode(number, buildCustomBarcode),
    ...(watermarkBusinessTypes ? { watermarkBusinessTypes } : {}),
    showPrintButton: true,
    printOptions: { copies: 1 },
    closeOnPrintSuccess: true,
    onPrintSuccess,
    reprint: { requestId: attempt.id, printRecordId: attempt.printRecordId, send: () => sendDmsReprint(attempt, saveRegistration, buildCustomBarcode, watermarkBusinessTypes) },
  });
  if (!opened) throw new Error('打印预览未能打开，请稍后重试');
}

const sending = new Map<string, Promise<void>>();
/** 预览手动发送或恢复已确认任务；先核对原状态，已发送只补登记，不重新取号或派发。 */
export function sendDmsReprint(original: DmsReprintAttempt, saveRegistration?: () => Promise<void>, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]): Promise<void> {
  const current = sending.get(original.id);
  if (current) return current;
  const promise = send(original, saveRegistration, buildCustomBarcode, watermarkBusinessTypes).finally(() => sending.delete(original.id));
  sending.set(original.id, promise);
  return promise;
}
async function send(original: DmsReprintAttempt, saveRegistration?: () => Promise<void>, buildCustomBarcode?: DmsPrintBarcodeBuilder, watermarkBusinessTypes?: string[]) {
  let attempt = await update(original, 'zhuangtai');
  if (String(attempt.state) === 'CANCELLED') throw new Error('本次重打已取消，请关闭预览');
  if (attempt.state === 'CONFIRMED' || attempt.state === 'CLOSED') {
    await completeRegistration(attempt, saveRegistration);
    return;
  }
  if (!attempt.documentId && !saveRegistration) throw new Error('请从原打印记录继续重打，恢复原生登记上下文后再打印');
  const finish = () => completeRegistration(attempt, saveRegistration);
  async function submitted(original: DmsPrintAttempt, batch: DmsPrintBatch, token: string) {
    const current = original as DmsReprintAttempt;
    const key = receiptKey(current, batch);
    try {
      localStorage.setItem(key, token);
    } catch {
      /* 成功事实优先持久化到服务端。 */
    }
    attempt = await update(current, 'queren-pici', { batchId: batch.id, token });
    try {
      localStorage.removeItem(key);
    } catch {
      /* 重复回传服务端幂等。 */
    }
    if (attempt.state !== 'OPEN') await finish();
    return attempt;
  }
  async function recover(original: DmsPrintAttempt) {
    attempt = original as DmsReprintAttempt;
    for (const batch of attemptBatches(original)) {
      let token: string | null = null;
      try {
        token = localStorage.getItem(receiptKey(attempt, batch));
      } catch {
        /* 无证据保持未知。 */
      }
      if (token) await submitted(attempt, batch, token);
    }
    return attempt;
  }
  await printManagedDmsDocument('原流水重打', { fileVersionId: attempt.fileVersionId, copies: 1, buildCustomBarcode, watermarkBusinessTypes }, attempt, {
    update,
    recover,
    submitted,
    allowHistoricalSerial: true,
  });
  await finish();
}
