/* eslint-disable */
import { marked } from 'marked';

// other exports

import { Lexer, Parser, Tokenizer, Renderer, TextRenderer, Marked } from 'marked';
import type { Tokens, MarkedExtension, TokenizerAndRendererExtension, Token ,TokenizerExtension, MarkedOptions, TokensList, RendererExtension, RendererObject } from 'marked';

const tokenizer = new marked.Tokenizer();

tokenizer.emStrong = function emStrong(src, _maskedSrc, _prevChar) {
  const token: Tokens.Strong = {
    type: 'strong',
    text: src,
    raw: src,
    tokens: []
  };

  this.lexer.inline(token.text, token.tokens);

  return token;
};

tokenizer.inlineText = function inlineText(...args: Parameters<Tokenizer['inlineText']>) {
  const p = this.inlineText(...args);

  if (p) p.raw = p.text;

  return p;
};

let options: MarkedOptions = {
  gfm: true,
  breaks: false,
  pedantic: false,
  silent: false,
  tokenizer,
  renderer: new marked.Renderer(),
  walkTokens: token => {
    if (token.type === 'heading') {
      token.depth += 1;
    }
  }
};

options = marked.getDefaults();
options = marked.defaults;

let myOldMarked: typeof marked = marked.options(options);
myOldMarked = marked.setOptions(options);

console.log(marked('1) I am using __markdown__.'));
console.log(marked('2) I am using __markdown__.', options));

console.log(marked.parse('5) I am using __markdown__.'));
console.log(marked.parse('6) I am using __markdown__.', options));

console.log(marked.parseInline('9) I am using __markdown__.'));
console.log(marked.parseInline('10) I am using __markdown__.', options));

const text = 'Something';
const raw = 'Raw';
const tokens: TokensList = marked.lexer(text, options);
console.log(marked.parser(tokens));

const lexer = new marked.Lexer(options);
const tokens2 = lexer.lex(text);
console.log(tokens2);
const tokens3 = lexer.inlineTokens(text, tokens);
console.log(tokens3);
// verifying that the second param to inlineTokens can be ignored
const tokens3a = lexer.inlineTokens(text);
console.log(tokens3a);
const re: Record<string, Record<string, Record<string, RegExp>>> = marked.Lexer.rules;
const lexerOptions: MarkedOptions = lexer.options;

const renderer = new marked.Renderer();
renderer.heading = ({ text, depth }) => {
  return text + depth.toString();
};
renderer.hr = () => {
  return `<hr>\n`;
};
renderer.checkbox = checked => {
  return checked ? 'CHECKED' : 'UNCHECKED';
};

class ExtendedRenderer extends marked.Renderer {
  code = ({ type, raw,  text, codeBlockStyle, lang, escaped }: Tokens.Code): string => super.code({ type, raw, text, codeBlockStyle, lang, escaped });
  blockquote = ({ type, raw, text, tokens }: Tokens.Blockquote): string => super.blockquote({ type, raw, text, tokens });
  html = ({ type, raw, text, pre, block }: Tokens.HTML): string => super.html({ type, raw, text, pre, block });
  heading = ({ type, raw, text, depth, tokens }: Tokens.Heading): string => super.heading({ type, raw, text, depth, tokens });
  hr = ({ type, raw }: Tokens.Hr): string => super.hr({ type, raw });
  list = ({ type, raw, ordered, start, loose, items }: Tokens.List): string => super.list({ type, raw, ordered, start, loose, items});
  listitem = ({ type, raw, task, checked, loose, text, tokens }: Tokens.ListItem): string => super.listitem({ type, raw, task, checked, loose, text, tokens });
  checkbox = ({ type, raw, checked }: Tokens.Checkbox): string => super.checkbox({ type, raw, checked });
  paragraph = ({ type, raw, pre, text, tokens }: Tokens.Paragraph): string => super.paragraph({ type, raw, pre, text, tokens });
  table = ({ type, raw, align, header, rows }: Tokens.Table): string => super.table({ type, raw, align, header, rows });
  tablerow = ({ text }: Tokens.TableRow): string => super.tablerow({ text });
  tablecell = ({ text, tokens, header, align }: Tokens.TableCell): string => super.tablecell({ text, tokens, header, align });
  strong = ({ type, raw, text, tokens }: Tokens.Strong): string => super.strong({ type, raw, text, tokens });
  em = ({ type, raw, text, tokens }: Tokens.Em): string => super.em({ type, raw, text, tokens });
  codespan = ({ type, raw, text }: Tokens.Codespan): string => super.codespan({ type, raw, text });
  br = ({ type, raw }: Tokens.Br): string => super.br({ type, raw });
  del = ({ type, raw, text, tokens }: Tokens.Del): string => super.del({ type, raw, text, tokens });
  link = ({ type, raw, href, title, text, tokens }: Tokens.Link): string => super.link({ type, raw, href, title, text, tokens });
  image = ({ type, raw, href, title, text }: Tokens.Image): string => super.image({ type, raw, href, title, text, tokens });
}

const rendererOptions: MarkedOptions = renderer.options;

const textRenderer = new marked.TextRenderer();
console.log(textRenderer.strong({ type : 'strong', raw, text, tokens }));

const parseTestText = '- list1\n  - list1.1\n\n listend';
const parseTestTokens: TokensList = marked.lexer(parseTestText, options);

const inlineTestText = '- list1\n  - list1.1\n\n listend';
const inlineTestTokens: Token[] = marked.Lexer.lexInline(inlineTestText, options);

/* List type is `list`. */
const listToken = parseTestTokens[0] as Tokens.List;
console.log(listToken.type === 'list');

const parser = new marked.Parser();
console.log(parser.parse(parseTestTokens));
console.log(marked.Parser.parse(parseTestTokens));
const parserOptions: MarkedOptions = parser.options;

marked.use({ renderer }, { tokenizer });

marked.use({
  renderer: {
    heading({ tokens, depth }) {
      if (depth > 3) {
        return `<p>${this.parser.parseInline(tokens)}</p>`;
      }

      return false;
    },
    listitem({ text, task, checked }) {
      if (task) return `<li class="task-list-item ${checked ? 'checked' : ''}">${text}</li>\n`;
      else return `<li>${text}</li>\n`;
    }
  },
  tokenizer: {
    heading(src) {
      const cap = this.rules.block.heading.exec(src);
      if (cap) {
        let text = cap[2].trim();

        return {
          type: 'heading',
          raw: cap[0],
          depth: cap[1].length,
          text,
          tokens: this.lexer.inline(text)
        };
      }
    },
    codespan(src) {
      const match = src.match(/\$+([^\$\n]+?)\$+/);
      if (match) {
        return {
          type: 'codespan',
          raw: match[0],
          text: match[1].trim()
        };
      }

      // return false to use original codespan tokenizer
      return false;
    }
  }
});

interface NameToken extends Tokens.Generic {
    type: 'name';
    raw: string;
    text: string;
    tokens: Token[];
    items: Token[];
}

const tokenizerExtension: TokenizerExtension = {
  name: 'name',
  level: 'block',
  start: (src: string) => src.match(/name/)?.index,
  tokenizer(src: string): NameToken | undefined {
    if (src === 'name') {
      const token: NameToken = {
        type: 'name',
        raw: src,
        text: src,
        tokens: this.lexer.inline(src),
        items: []
      };
      this.lexer.inline(token.text, token.items);
      return token;
    }
  },
  childTokens: ['items']
};

const rendererExtension: RendererExtension = {
  name: 'name',
  renderer(t) {
    const token = t as NameToken;
    if (token.text === 'name') {
      // verifying that the second param to parseInline can be ignored
      console.log(this.parser.parseInline(token.items));
      return this.parser.parse(token.items);
    }
    return false;
  }
};

const tokenizerAndRendererExtension: TokenizerAndRendererExtension = {
  name: 'name',
  level: 'block',
  tokenizer(src: string) {
    if (src === 'name') {
      const token = {
        type: 'name',
        raw: src
      };
      return token;
    }
  },
  renderer(token: Tokens.Generic) {
    if (token.raw === 'name') {
      return 'name';
    }

    return false;
  }
};

marked.use({
  extensions: [tokenizerExtension, rendererExtension, tokenizerAndRendererExtension]
});

const asyncExtension: MarkedExtension = {
  async: true,
  async walkTokens(token) {
    if (token.type === 'code') {
      await Promise.resolve(3);
      token.text += 'foobar';
    }
  }
};

marked.use(asyncExtension);

(async() => {
const md = '# foobar';
const asyncMarked: string = await marked(md, { async: true });
const promiseMarked: Promise<string> = marked(md, { async: true });
const notAsyncMarked: string = marked(md, { async: false });
// @ts-expect-error marked can still be async if an extension sets `async: true`
const defaultMarked: string = marked(md);
// as string can be used if no extensions set `async: true`
const stringMarked: string = marked(md) as string;

const asyncMarkedParse: string = await marked.parse(md, { async: true });
const promiseMarkedParse: Promise<string> = marked.parse(md, { async: true });
const notAsyncMarkedParse: string = marked.parse(md, { async: false });
// @ts-expect-error marked can still be async if an extension sets `async: true`
const defaultMarkedParse: string = marked.parse(md);
// as string can be used if no extensions set `async: true`
const stringMarkedParse: string = marked.parse(md) as string;
})();

// Tests for List and ListItem
// Dumped from markdown list parsed data

const listAndListItemText: Tokens.List = {
  type: 'list',
  raw: '1. Text ...',
  ordered: true,
  start: 1,
  loose: false,
  items: [
    {
      type: 'list_item',
      raw: '1. Text ...',
      task: false,
      loose: false,
      text: 'Text',
      tokens: [
        {
          type: 'text',
          raw: 'Point one',
          text: 'Point one',
          tokens: [
            {
              type: 'text',
              raw: 'Point one',
              text: 'Point one'
            }
          ]
        },
        {
          type: 'list',
          raw: '',
          ordered: false,
          start: '',
          loose: false,
          items: []
        }
      ]
    }
  ]
};

const lexer2 = new Lexer();
const tokens4 = lexer2.lex('# test');
const parser2 = new Parser();
console.log(parser2.parse(tokens4));

marked.use({ renderer: new Renderer() });
marked.use({ renderer: new TextRenderer() });
marked.use({ tokenizer: new Tokenizer() });
marked.use({
  hooks: {
    preprocess(markdown) {
      if (this.options.async) {
        return Promise.resolve(markdown);
      }
      return markdown;
    },
    postprocess(html) {
      return html;
    }
  }
});
marked.use({
  hooks: {
    processAllTokens(tokens) {
      return tokens;
    }
  }
});
marked.use({
  hooks: {
    provideLexer() {
      return this.block ? Lexer.lex : Lexer.lexInline;
    },
    provideParser() {
      return this.block ? Parser.parse : Parser.parseInline;
    },
  }
});
marked.use({
  async: true,
  hooks: {
    async preprocess(markdown) {
      return markdown;
    },
    async postprocess(html) {
      return html;
    },
    async processAllTokens(tokens) {
      return tokens;
    }
  }
});

// @ts-expect-error block is not exported
import { block } from 'marked';
// @ts-expect-error inline is not exported
import { inline } from 'marked';
// Rules is exported
import type { Rules } from 'marked';

marked.parse('', {
  async: undefined,
  breaks: undefined,
  extensions: undefined,
  gfm: undefined,
  hooks: undefined,
  pedantic: undefined,
  renderer: undefined,
  silent: undefined,
  tokenizer: undefined,
  walkTokens: undefined,
});

marked.use({
  async: undefined,
  breaks: undefined,
  extensions: undefined,
  gfm: undefined,
  hooks: undefined,
  pedantic: undefined,
  renderer: undefined,
  silent: undefined,
  tokenizer: undefined,
  walkTokens: undefined,
});

const markedNumber = new Marked<number, number>();
const rendererNumber = {
  html() {
    return 1;
  }
};
function parserNumber(tokens: Token[], options?: MarkedOptions<number, number>){
    return 1;
};
markedNumber.use({ renderer: rendererNumber, hooks: { provideParser() { return parserNumber; } } });

const num: number = await markedNumber.parse('');
console.log(num);
