package void

import (
	"bytes"
	"fmt"

	"github.com/yuin/goldmark/ast"
	goldrender "github.com/yuin/goldmark/renderer"
	"github.com/yuin/goldmark/util"
	"go.uber.org/multierr"
)

type VoidRenderer struct {
	errs error
}

func NewVoidRenderer() *VoidRenderer {
	return &VoidRenderer{}
}

// RegisterFuncs implements NodeRenderer.RegisterFuncs .
func (r *VoidRenderer) RegisterFuncs(reg goldrender.NodeRendererFuncRegisterer) {
	reg.Register(ast.KindParagraph, r.renderParagraph)
	reg.Register(ast.KindAutoLink, r.renderAutoLink)
	reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
	reg.Register(ast.KindRawHTML, r.renderRawHTML)
	reg.Register(ast.KindImage, r.renderImage)
	reg.Register(ast.KindText, r.renderText)
	reg.Register(ast.KindString, r.renderString)
}

func (r *VoidRenderer) Errors() error {
	return r.errs
}

func (r *VoidRenderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		w.WriteString("\n")
	}

	return ast.WalkContinue, nil
}

func (r *VoidRenderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		return ast.WalkContinue, nil
	}

	_, _ = w.WriteString(`lien hypertexte`)
	return ast.WalkContinue, nil
}

func (r *VoidRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		return ast.WalkContinue, nil
	}

	_, _ = w.WriteString(`SubstitutDeCode`)
	return ast.WalkSkipChildren, nil
}

func (r *VoidRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		return ast.WalkContinue, nil
	}

	_, _ = w.WriteString(`bloc HTML remplacé`)
	return ast.WalkSkipChildren, nil
}

func (r *VoidRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		_, _ = w.WriteString(`.`)
		return ast.WalkContinue, nil
	}
	n := node.(*ast.Image)

	// Check there is a correct image alt
	alt := nodeToText(n, source)
	if len(bytes.Fields(alt)) <= 1 {
		r.errs = multierr.Append(r.errs, fmt.Errorf("No valid image alternative defined for %q", n.Destination))
		return ast.WalkContinue, nil
	}

	return ast.WalkContinue, nil
}

func nodeToText(n ast.Node, source []byte) []byte {
	var buf bytes.Buffer
	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
		if s, ok := c.(*ast.String); ok && s.IsCode() {
			buf.Write(s.Text(source))
		} else if !c.HasChildren() {
			buf.Write(util.EscapeHTML(c.Text(source)))
		} else {
			buf.Write(nodeToText(c, source))
		}
	}
	return buf.Bytes()
}

func (r *VoidRenderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		return ast.WalkContinue, nil
	}

	n := node.(*ast.Text)
	segment := n.Segment
	w.Write(segment.Value(source))
	w.WriteString(" ")

	return ast.WalkContinue, nil
}

func (r *VoidRenderer) renderString(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
	if !entering {
		return ast.WalkContinue, nil
	}

	n := node.(*ast.String)
	if n.IsCode() {
		w.Write(n.Value)
	} else {
		w.Write(n.Value)
		w.WriteString("\n")
	}
	return ast.WalkContinue, nil
}
