import type { InitiativeDraft } from './types';
import type { ContributionDraftShape } from './contributionDraftTypes';
import { buildOfficialBieuMauFromDraft } from './mapDraftToOfficialBieuMau';
import {
  buildInitialOfficialFormPreviewFields,
  type OfficialFormPreviewFields,
} from './officialFormPreviewFieldEdits';
import {
  convertDocxToPdfBlob,
  createOffScreenDocxCaptureHost,
} from './convertDocxToPdfBlob';
import {
  fetchApplicationFormDocxPreview,
  fetchApplicationFormOfficialPdf,
} from '../lib/applicationFormDocxApi';
import { buildOfficialFormPdfFileName } from '../lib/officialFormPdfFileName';
import { ApiArrayBufferRequestError } from '../api/client';

export type OfficialFormPdfResult = {
  pdfBlob: Blob;
  fileName: string;
  /** Server LibreOffice PDF matches what submit persists to MinIO (`official_form_pdf`); client is html2canvas fallback. */
  source: 'server' | 'client';
  /** Initial field values shown in preview-only field editor (client side only). */
  initialPreviewFields: OfficialFormPreviewFields;
};

function pdfArrayBufferLooksLikePdf(buf: ArrayBuffer): boolean {
  if (buf.byteLength < 5) return false;
  const head = new Uint8Array(buf, 0, 5);
  return String.fromCharCode(...head).startsWith('%PDF');
}

function shouldFallbackToClientOfficialPdf(err: unknown): boolean {
  if (err instanceof ApiArrayBufferRequestError) {
    if (err.status === 501) return true;
    if (/libreoffice|headless|chưa cài/i.test(err.message)) return true;
    return false;
  }
  if (err instanceof Error) {
    const m = err.message.toLowerCase();
    if (m.includes('timeout') || m.includes('network')) return true;
    if (m.includes('econnrefused') || m.includes('econnreset')) return true;
  }
  return false;
}

async function createOfficialFormPdfClientRaster(
  official: Record<string, unknown>,
  options?: { renderScale?: number },
): Promise<Blob> {
  const docxBuf = await fetchApplicationFormDocxPreview(official);
  const docxBlob = new Blob([docxBuf], {
    type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  });

  const host = createOffScreenDocxCaptureHost();
  try {
    return await convertDocxToPdfBlob(docxBlob, host, {
      renderScale: options?.renderScale ?? 2,
    });
  } finally {
    host.remove();
  }
}

/**
 * Prefer server LibreOffice PDF (same conversion as submit → MinIO `official_form_pdf`);
 * fall back to client docx-preview + html2canvas when LibreOffice is unavailable or the network fails.
 */
export async function createOfficialFormPdfFromDrafts(
  draft: InitiativeDraft,
  contribution: ContributionDraftShape,
  options?: { renderScale?: number },
): Promise<OfficialFormPdfResult> {
  const official = buildOfficialBieuMauFromDraft(
    draft,
    contribution as unknown as Record<string, unknown> | null,
  ) as unknown as Record<string, unknown>;

  const fileName = buildOfficialFormPdfFileName(official);
  const initialPreviewFields = buildInitialOfficialFormPreviewFields(official);

  try {
    const pdfBuf = await fetchApplicationFormOfficialPdf(official);
    if (!pdfArrayBufferLooksLikePdf(pdfBuf)) {
      const pdfBlob = await createOfficialFormPdfClientRaster(official, options);
      return { pdfBlob, fileName, source: 'client', initialPreviewFields };
    }
    return {
      pdfBlob: new Blob([pdfBuf], { type: 'application/pdf' }),
      fileName,
      source: 'server',
      initialPreviewFields,
    };
  } catch (e) {
    if (!shouldFallbackToClientOfficialPdf(e)) throw e;
    const pdfBlob = await createOfficialFormPdfClientRaster(official, options);
    return { pdfBlob, fileName, source: 'client', initialPreviewFields };
  }
}

export function downloadPdfBlob(blob: Blob, fileName: string): void {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = fileName;
  document.body.appendChild(a);
  a.click();
  a.remove();
  setTimeout(() => URL.revokeObjectURL(url), 4000);
}
