package markdown

import (
	"html"
	"strings"
)

var (
	headerStart = []string{"<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>"}
	headerEnd   = []string{"</h1>", "</h2>", "</h3>", "</h4>", "</h5>", "</h6>"}
)

// Render creates HTML from the supplied markdown text.
func Render(markdown string) string {
	var (
		out       = strings.Builder{}
		paragraph = strings.Builder{}
		i         = 0
		lineStart = 0
	)

	flush := func() {
		if paragraph.Len() == 0 {
			return
		}

		out.WriteString("<p>")
		writeText(&out, paragraph.String())
		out.WriteString("</p>")
		paragraph.Reset()
	}

	for {
		if i > len(markdown) {
			flush()
			return out.String()
		}

		if i != len(markdown) && markdown[i] != '\n' {
			i++
			continue
		}

		line := markdown[lineStart:i]
		lineStart = i + 1
		i++

		switch {
		case strings.HasPrefix(line, "#"):
			flush()
			space := strings.IndexByte(line, ' ')

			if space > 0 && space <= 6 {
				out.WriteString(headerStart[space-1])
				writeText(&out, line[space+1:])
				out.WriteString(headerEnd[space-1])
			}

		default:
			if len(line) == 0 {
				flush()
				continue
			}

			if paragraph.Len() > 0 {
				paragraph.WriteByte(' ')
			}

			paragraph.WriteString(line)
		}
	}
}

func writeText(out *strings.Builder, text string) {
	var (
		i          = 0
		tokenStart = 0
	)

	var (
		textStart   = -1
		textEnd     = -1
		urlStart    = -1
		parentheses = 0
	)

	for {
		if i == len(text) {
			out.WriteString(html.EscapeString(text[tokenStart:]))
			return
		}

		c := text[i]

		switch c {
		case '[':
			out.WriteString(html.EscapeString(text[tokenStart:i]))
			tokenStart = i
			textStart = i
		case ']':
			textEnd = i
		case '(':
			if parentheses == 0 {
				urlStart = i
			}

			parentheses++
		case ')':
			parentheses--

			if parentheses == 0 && textStart >= 0 && textEnd >= 0 && urlStart >= 0 {
				linkText := text[textStart+1 : textEnd]
				linkURL := text[urlStart+1 : i]

				out.WriteString("<a href=\"")
				out.WriteString(formatURL(linkURL))
				out.WriteString("\">")
				out.WriteString(html.EscapeString(linkText))
				out.WriteString("</a>")

				textStart = -1
				textEnd = -1
				urlStart = -1

				tokenStart = i + 1
			}
		}

		i++
	}
}

func formatURL(linkURL string) string {
	if strings.HasPrefix(strings.ToLower(linkURL), "javascript:") {
		return ""
	}

	return html.EscapeString(linkURL)
}