package processors

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/golangci/golangci-lint/pkg/golinters"
	"github.com/golangci/golangci-lint/pkg/logutils"

	"github.com/golangci/golangci-lint/pkg/result"
)

type ReplacementBuilder struct {
	log logutils.Log
}

func NewReplacementBuilder(log logutils.Log) *ReplacementBuilder {
	return &ReplacementBuilder{log: log}
}

func (p ReplacementBuilder) Process(issues []result.Issue) ([]result.Issue, error) {
	return transformIssues(issues, p.processIssue), nil
}

func (p ReplacementBuilder) processIssue(i *result.Issue) *result.Issue {
	misspellName := golinters.Misspell{}.Name()
	if i.FromLinter == misspellName {
		newIssue, err := p.processMisspellIssue(i)
		if err != nil {
			p.log.Warnf("Failed to build replacement for misspell issue: %s", err)
			return i
		}
		return newIssue
	}

	return i
}

func (p ReplacementBuilder) processMisspellIssue(i *result.Issue) (*result.Issue, error) {
	if len(i.SourceLines) != 1 {
		return nil, fmt.Errorf("invalid count of source lines: %d", len(i.SourceLines))
	}
	sourceLine := i.SourceLines[0]

	if i.Column() <= 0 {
		return nil, fmt.Errorf("invalid column %d", i.Column())
	}
	col0 := i.Column() - 1
	if col0 >= len(sourceLine) {
		return nil, fmt.Errorf("too big column %d", i.Column())
	}

	issueTextRE := regexp.MustCompile("`(.+)` is a misspelling of `(.+)`")
	submatches := issueTextRE.FindStringSubmatch(i.Text)
	if len(submatches) != 3 {
		return nil, fmt.Errorf("invalid count of submatches %d", len(submatches))
	}

	from, to := submatches[1], submatches[2]
	if !strings.HasPrefix(sourceLine[col0:], from) {
		return nil, fmt.Errorf("invalid prefix of source line `%s`", sourceLine)
	}

	newSourceLine := ""
	if col0 != 0 {
		newSourceLine += sourceLine[:col0]
	}

	newSourceLine += to

	sourceLineFromEnd := col0 + len(from)
	if sourceLineFromEnd < len(sourceLine) {
		newSourceLine += sourceLine[sourceLineFromEnd:]
	}

	if i.Replacement != nil {
		return nil, fmt.Errorf("issue replacement isn't nil")
	}

	iCopy := *i
	iCopy.Replacement = &result.Replacement{
		NewLines: []string{newSourceLine},
	}
	return &iCopy, nil
}

func (p ReplacementBuilder) Name() string {
	return "replacement_builder"
}

func (p ReplacementBuilder) Finish() {}