package md

import (
	"errors"
	"strings"

	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

var propertiesContextKey = parser.NewContextKey()

type propertiesData struct {
	properties map[string]string
	Error      error
}

type propertiesExtension struct {
}

func (e *propertiesExtension) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(
		parser.WithBlockParsers(
			util.Prioritized(&propertiesParser{}, 0),
		),
	)
}

type propertiesParser struct {
}

func (p *propertiesParser) Trigger() []byte {
	return []byte{'-'}
}

func (p *propertiesParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
	lineNumber, _ := reader.Position()
	if lineNumber != 0 {
		return nil, parser.NoChildren
	}

	line, _ := reader.PeekLine()
	if isSeparator(line) {
		return ast.NewTextBlock(), parser.NoChildren
	}

	return nil, parser.NoChildren
}

func (p *propertiesParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
	line, segment := reader.PeekLine()
	if isSeparator(line) && !util.IsBlank(line) {
		reader.Advance(segment.Len())
		return parser.Close
	}

	node.Lines().Append(segment)
	return parser.Continue | parser.NoChildren
}

func (p *propertiesParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {
	lines := node.Lines()

	data := &propertiesData{
		make(map[string]string),
		nil,
	}
	pc.Set(propertiesContextKey, data)

	for i := 0; i < lines.Len(); i++ {
		segment := lines.At(i)
		line := string(segment.Value(reader.Source()))

		index := strings.IndexRune(line, ':')
		if index == -1 {
			data.Error = errors.New("invalid property line")
			break
		}

		key := strings.TrimSpace(line[:index])
		value := strings.TrimSpace(line[index+1:])
		data.properties[key] = value
	}

	node.Parent().RemoveChild(node.Parent(), node)
}

func (p *propertiesParser) CanInterruptParagraph() bool {
	return false
}

func (p *propertiesParser) CanAcceptIndentedLine() bool {
	return false
}

func isSeparator(line []byte) bool {
	line = util.TrimRightSpace(util.TrimLeftSpace(line))

	for i := 0; i < len(line); i++ {
		if line[i] != '-' {
			return false
		}
	}

	return true
}
