const LARGE_ATTACHMENT_URL_PATTERN =
  /https:\/\/mail\.officialsite\.kr\/api\/mailbox\/large-attachments\/([a-f0-9]{48})(?![a-f0-9])/gi;
const GENERATED_HTML_SECTION_PATTERN =
  /<div\b(?=[^>]*\bclass\s*=\s*["'][^"']*\bofficial-mail-attachment-links\b[^"']*["'])[^>]*>[\s\S]*?<\/div>/gi;

function tokensIn(value: string) {
  return [...value.matchAll(LARGE_ATTACHMENT_URL_PATTERN)].map((match) => match[1].toLowerCase());
}

function isGeneratedTextSection(section: string) {
  const lines = section.trim().split(/\r?\n/).slice(1);
  return section.startsWith("첨부 파일\n") && lines.length > 0 && lines.every((line) =>
    /^- .+ https:\/\/mail\.officialsite\.kr\/api\/mailbox\/large-attachments\/[a-f0-9]{48}\s*$/i.test(line));
}

function generatedTextSections(value: string) {
  const normalized = value.replace(/\r\n/g, "\n");
  const sections: Array<{ end: number; section: string; start: number }> = [];
  if (normalized.startsWith("첨부 파일\n")) {
    const breakIndex = normalized.indexOf("\n\n");
    const section = normalized.slice(0, breakIndex < 0 ? undefined : breakIndex);
    if (isGeneratedTextSection(section)) {
      sections.push({ start: 0, end: breakIndex < 0 ? normalized.length : breakIndex + 2, section });
    }
  }
  const tailIndex = normalized.lastIndexOf("\n\n첨부 파일\n");
  if (tailIndex >= 0) {
    const section = normalized.slice(tailIndex + 2);
    if (isGeneratedTextSection(section)) {
      sections.push({ start: tailIndex, end: normalized.length, section });
    }
  }
  return { normalized, sections };
}

export function getOfficialLargeAttachmentTokens(bodyHtml: string | null, bodyText: string | null) {
  const sections = [...(bodyHtml ?? "").matchAll(GENERATED_HTML_SECTION_PATTERN)].map((match) => match[0]);
  const textSections = generatedTextSections(bodyText ?? "").sections;
  return [...new Set([...sections.flatMap(tokensIn), ...textSections.flatMap(({ section }) => tokensIn(section))])];
}

export function hasUnresolvedOfficialLargeAttachmentLinks(input: {
  attachments: ReadonlyArray<{ externalUrl?: string | null; isLargeAttachment?: boolean }>;
  bodyHtml: string | null;
  bodyText: string | null;
}) {
  const tokens = getOfficialLargeAttachmentTokens(input.bodyHtml, input.bodyText);
  if (tokens.length === 0) return false;
  const available = new Set(input.attachments
    .filter((attachment) => attachment.isLargeAttachment && attachment.externalUrl)
    .flatMap((attachment) => tokensIn(attachment.externalUrl ?? "")));
  return tokens.some((token) => !available.has(token));
}

export function stripDisplayedLargeAttachmentSections(input: {
  bodyHtml: string | null;
  bodyText: string | null;
  availableTokens: string[];
}) {
  const available = new Set(input.availableTokens.map((token) => token.toLowerCase()));
  const canHide = (section: string) => {
    const tokens = tokensIn(section);
    return tokens.length > 0 && tokens.every((token) => available.has(token));
  };
  const bodyHtml = input.bodyHtml?.replace(GENERATED_HTML_SECTION_PATTERN, (section) =>
    canHide(section) ? "" : section) ?? null;
  const textSections = generatedTextSections(input.bodyText ?? "");
  const bodyText = textSections.sections.some(({ section }) => canHide(section))
    ? textSections.sections
        .filter(({ section }) => canHide(section))
        .sort((a, b) => b.start - a.start)
        .reduce((current, section) => current.slice(0, section.start) + current.slice(section.end), textSections.normalized)
    : input.bodyText;
  return { bodyHtml, bodyText };
}
