import { Resource } from '../model/note';
import { Range } from '../model/range';
import {
  createMarkdownReferences,
  stringifyMarkdownLinkReferenceDefinition,
} from '../services/markdown-provider';
import { FoamWorkspace } from '../model/workspace';
import { TextEdit } from './apply-text-edit';
import { Position } from '../model/position';

export const LINK_REFERENCE_DEFINITION_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
export const LINK_REFERENCE_DEFINITION_FOOTER = `[//end]: # "Autogenerated link references"`;

export const generateLinkReferences = async (
  note: Resource,
  text: string,
  eol: string,
  workspace: FoamWorkspace,
  includeExtensions: boolean
): Promise<TextEdit | null> => {
  if (!note) {
    return null;
  }

  const markdownReferences = createMarkdownReferences(
    workspace,
    note.uri,
    includeExtensions
  );

  const newReferences =
    markdownReferences.length === 0
      ? ''
      : [
          LINK_REFERENCE_DEFINITION_HEADER,
          ...markdownReferences.map(stringifyMarkdownLinkReferenceDefinition),
          LINK_REFERENCE_DEFINITION_FOOTER,
        ].join(eol);

  if (note.definitions.length === 0) {
    if (newReferences.length === 0) {
      return null;
    }

    const lines = text.split(eol);
    const end = Position.create(
      lines.length - 1,
      lines[lines.length - 1].length
    );
    const padding = end.character === 0 ? eol : `${eol}${eol}`;
    return {
      newText: `${padding}${newReferences}`,
      range: Range.createFromPosition(end, end),
    };
  } else {
    const first = note.definitions[0];
    const last = note.definitions[note.definitions.length - 1];

    let nonGeneratedReferenceDefinitions = note.definitions;

    // if we have more definitions then referenced pages AND the page refers to a page
    // we expect non-generated link definitions to be present
    // Collect all non-generated definitions, by removing the generated ones
    if (
      note.definitions.length > markdownReferences.length &&
      markdownReferences.length > 0
    ) {
      // remove all autogenerated definitions
      const beginIndex = note.definitions.findIndex(
        ({ label }) => label === '//begin'
      );
      const endIndex = note.definitions.findIndex(
        ({ label }) => label === '//end'
      );

      const generatedDefinitions = [...note.definitions].splice(
        beginIndex,
        endIndex - beginIndex + 1
      );

      nonGeneratedReferenceDefinitions = note.definitions.filter(
        x => !generatedDefinitions.includes(x)
      );
    }

    // When we only have explicitly defined link definitions &&
    // no indication of previously defined generated links &&
    // there is no reference to another page, return null
    if (
      nonGeneratedReferenceDefinitions.length > 0 &&
      note.definitions.findIndex(({ label }) => label === '//begin') < 0 &&
      markdownReferences.length === 0
    ) {
      return null;
    }

    // Format link definitions for non-generated links
    const nonGeneratedReferences = nonGeneratedReferenceDefinitions
      .map(stringifyMarkdownLinkReferenceDefinition)
      .join(eol);

    const oldReferences = note.definitions
      .map(stringifyMarkdownLinkReferenceDefinition)
      .join(eol);

    // When the newly formatted references match the old ones, OR
    // when non-generated references are present, but no new ones are generated
    // return null
    if (
      oldReferences === newReferences ||
      (nonGeneratedReferenceDefinitions.length > 0 &&
        newReferences === '' &&
        markdownReferences.length > 0)
    ) {
      return null;
    }

    let fullReferences = `${newReferences}`;
    // If there are any non-generated definitions, add those to the output as well
    if (
      nonGeneratedReferenceDefinitions.length > 0 &&
      markdownReferences.length > 0
    ) {
      fullReferences = `${nonGeneratedReferences}${eol}${newReferences}`;
    }

    return {
      // @todo: do we need to ensure new lines?
      newText: `${fullReferences}`,
      range: Range.createFromPosition(first.range!.start, last.range!.end),
    };
  }
};
