package main

import (
	"bytes"
	"fmt"
	"strings"

	"srs.epita.fr/fic-server/admin/sync"
	lib "srs.epita.fr/fic-server/repochecker/grammalecte/lib"
	"srs.epita.fr/fic-server/repochecker/grammalecte/void"

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

func GrammalecteCheckMDText(str string, lang string, exceptions *sync.CheckExceptions, forbiddenStrings ...string) (errs error) {
	if !isRecognizedLanguage(lang) {
		return
	}

	if exceptions != nil {
		for k := range *exceptions {
			tmp := strings.SplitN(k, ":", 3)
			if len(tmp) == 3 && tmp[1] == "quote" {
				str = strings.Replace(str, tmp[2], "une citation a été remplacée ici", -1)
			}
		}
	}

	for _, s := range forbiddenStrings {
		if strings.Contains(str, s) {
			if !exceptions.HasException(":not-forbidden-string:" + s) {
				errs = multierr.Append(errs, fmt.Errorf("Forbidden string %q included in file content, don't write your flag in text", s))
			}
		}
	}

	checker := &grammarChecker{
		exceptions: exceptions,
	}

	voidRenderer := void.NewVoidRenderer()

	markdown := goldmark.New(
		goldmark.WithParserOptions(
			parser.WithASTTransformers(
				util.Prioritized(checker, 200),
			),
		),
		goldmark.WithRenderer(
			renderer.NewRenderer(
				renderer.WithNodeRenderers(
					util.Prioritized(voidRenderer, 30),
				),
			),
		),
	)

	var buf bytes.Buffer
	if err := markdown.Convert([]byte(str), &buf); err != nil {
		errs = multierr.Append(errs, err)
	}

	errs = multierr.Append(errs, checker.errs)
	errs = multierr.Append(errs, voidRenderer.Errors())

	for _, err := range multierr.Errors(grammalecte("", buf.String(), 0, exceptions, &CommonOpts)) {
		if gerror, ok := err.(lib.GrammarError); ok {
			if (gerror.RuleId == "redondances_paragraphe" || gerror.RuleId == "redondances_phrase") && gerror.GetPassage() == "SubstitutDeCode" {
				continue
			}
		} else if serror, ok := err.(lib.SpellingError); ok {
			if serror.Value == "SubstitutDeCode" {
				continue
			}
		}

		errs = multierr.Append(errs, err)
	}

	return
}

type grammarChecker struct {
	exceptions *sync.CheckExceptions
	errs       error
}

func (t *grammarChecker) Transform(doc *ast.Document, reader text.Reader, pc parser.Context) {
	ast.Walk(doc, func(node ast.Node, enter bool) (ast.WalkStatus, error) {
		if !enter {
			return ast.WalkContinue, nil
		}

		switch child := node.(type) {
		case *ast.Image:
			if len(child.Title) > 0 {
				t.errs = multierr.Append(t.errs, grammalecte("", string(child.Title), 0, t.exceptions, &CommonOpts))
			}
		case *ast.Link:
			if len(child.Title) > 0 {
				t.errs = multierr.Append(t.errs, grammalecte("", string(child.Title), 0, t.exceptions, &CommonOpts))
			}
		}

		return ast.WalkContinue, nil
	})
}
