// SPDX-FileCopyrightText: 2024 Nicolas Peugnet <nicolas@club1.fr>
// SPDX-License-Identifier: GPL-2.0-or-later

package markdown

import (
	"bytes"
	"html/template"
	"regexp"

	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/util"
	"salsa.debian.org/lintian/lintian-ssg/markdown/goldmark_ext"
)

var (
	mdInline = goldmark.New(goldmark.WithParser(parser.NewParser(
		parser.WithBlockParsers(util.Prioritized(parser.NewParagraphParser(), 100)),
		parser.WithInlineParsers(parser.DefaultInlineParsers()...),
	)))
	mdFull = goldmark.New(
		goldmark.WithParser(parser.NewParser(
			parser.WithBlockParsers(
				// adapted from parser.DefaultBlockParsers(), with headings removed
				util.Prioritized(parser.NewThematicBreakParser(), 200),
				util.Prioritized(parser.NewListParser(), 300),
				util.Prioritized(parser.NewListItemParser(), 400),
				util.Prioritized(goldmark_ext.NewAnyIndentCodeBlockParser(), 500),
				util.Prioritized(parser.NewFencedCodeBlockParser(), 700),
				util.Prioritized(parser.NewBlockquoteParser(), 800),
				util.Prioritized(parser.NewHTMLBlockParser(), 900),
				util.Prioritized(parser.NewParagraphParser(), 1000),
			),
			parser.WithInlineParsers(append(
				parser.DefaultInlineParsers(),
				util.Prioritized(goldmark_ext.NewManpageLinkParser(), 1000),
				util.Prioritized(goldmark_ext.NewBugLinkParser(), 1000),
			)...),
			parser.WithParagraphTransformers(parser.DefaultParagraphTransformers()...),
		)),
		goldmark.WithRendererOptions(
			html.WithUnsafe(),
			renderer.WithNodeRenderers(util.Prioritized(NewNoEscapeCodeBlockRenderer(), 100)),
		),
	)
	// htmlEntRegex is a regular expression that matches HTML entities that
	// are safe to replace with their corresponding UTF-8 character.
	//
	// The name of the entity is captured in the first group.
	htmlEntRegex        = regexp.MustCompile(`\b&(ast|lowbar);\b`)
	htmlEntReplacements = map[string]string{
		"&ast;":    "*",
		"&lowbar;": "_",
	}
)

// replaceSafeHTMLEntities replaces HTML entities with their corresponding
// UTF-8 character when it is safe to do so.
//
// HTML entites have been over-used in Lintian tags' description, and keeping
// them as is would make the rest of the code more complicated, so we replace
// them back.
func replaceSafeHTMLEntities(str string) string {
	return htmlEntRegex.ReplaceAllStringFunc(str, func(s string) string {
		return htmlEntReplacements[s]
	})
}

type Style int

const (
	StyleInline Style = iota
	StyleFull
)

func ToHTML(src string, style Style) template.HTML {
	var err error
	buf := bytes.Buffer{}
	switch style {
	case StyleInline:
		err = mdInline.Convert([]byte(src), &buf)
	case StyleFull:
		src = replaceSafeHTMLEntities(src)
		err = mdFull.Convert([]byte(src), &buf)
	}
	if err != nil {
		// As we use a bytes.Buffer, goldmark.Convert should never return errors.
		panic(err)
	}
	return template.HTML(buf.String())
}
