/*
 * SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
 *
 * SPDX-License-Identifier: AGPL-3.0-only
 */
import {
  defaultNoteFrontmatter,
  extractFirstHeading,
  extractFrontmatter,
  generateNoteTitle,
  type NoteFrontmatter,
  NoteType,
  parseNoteFrontmatter,
} from '@hedgedoc/commons';
import { parseDocument } from 'htmlparser2';
import MarkdownIt from 'markdown-it';

interface FrontmatterExtractionResult {
  title: string;
  description: string;
  tags: string[];
  noteType: NoteType;
}

interface FrontmatterParserResult {
  frontmatter: NoteFrontmatter;
  firstLineOfContentIndex: number;
}

/**
 * Parses the frontmatter of the given content and extracts the metadata that are necessary to create a new revision
 *
 * @param content the revision content that contains the frontmatter
 * @returns the extracted metadata, including the title, description, tags, and note type
 */
export function extractRevisionMetadataFromContent(content: string): FrontmatterExtractionResult {
  const parserResult = parseFrontmatter(content);
  const frontmatter = parserResult?.frontmatter;
  const firstLineOfContentIndex = parserResult?.firstLineOfContentIndex;

  const title = generateNoteTitle(frontmatter, () =>
    extractFirstHeadingFromContent(
      generateContentWithoutFrontmatter(firstLineOfContentIndex, content),
    ),
  );
  const description = frontmatter?.description ?? '';
  const tags = frontmatter?.tags ?? [];
  const noteType = frontmatter?.type ?? NoteType.DOCUMENT;

  return { title, description, tags, noteType };
}

/**
 * Generates the content of a revision without the frontmatter.
 *
 * @param firstLineOfContentIndex the index of the first line of content after the frontmatter
 * @param content the full content including frontmatter
 * @returns the content without frontmatter
 */
function generateContentWithoutFrontmatter(
  firstLineOfContentIndex: number | undefined,
  content: string,
): string {
  return firstLineOfContentIndex === undefined
    ? content
    : content.split('\n').slice(firstLineOfContentIndex).join('\n');
}

/**
 * Parses the frontmatter from the given content and returns the parsed frontmatter and the index of the first line of content.
 *
 * @param content the content to parse
 * @returns an object containing the parsed frontmatter and the index of the first line of content, or undefined if no frontmatter was found
 */
function parseFrontmatter(content: string): FrontmatterParserResult | undefined {
  const extractionResult = extractFrontmatter(content.split('\n'));
  const rawText = extractionResult?.rawText;
  if (!rawText) {
    return undefined;
  }

  const firstLineOfContentIndex = extractionResult.lineOffset + 1;
  const frontmatterParseResult = parseNoteFrontmatter(rawText);
  const noteFrontmatter =
    frontmatterParseResult.error !== undefined
      ? defaultNoteFrontmatter
      : frontmatterParseResult.value;
  return {
    frontmatter: noteFrontmatter,
    firstLineOfContentIndex: firstLineOfContentIndex,
  };
}

/**
 * Extracts the first heading from the given markdown content
 *
 * @param content the content to extract the first heading from
 * @returns the first heading or undefined if no heading was found
 */
function extractFirstHeadingFromContent(content: string): string | undefined {
  const markdownIt = new MarkdownIt('default');
  const html = markdownIt.render(content);
  const document = parseDocument(html);
  return extractFirstHeading(document);
}
