package linter

import (
	"golang.org/x/tools/go/packages"
)

const (
	PresetFormatting  = "format"
	PresetComplexity  = "complexity"
	PresetStyle       = "style"
	PresetBugs        = "bugs"
	PresetUnused      = "unused"
	PresetPerformance = "performance"
)

type Config struct {
	Linter           Linter
	EnabledByDefault bool

	LoadMode packages.LoadMode

	NeedsSSARepr bool

	InPresets        []string
	Speed            int // more value means faster execution of linter
	AlternativeNames []string

	OriginalURL      string // URL of original (not forked) repo, needed for autogenerated README
	ParentLinterName string // used only for megacheck's children now
	CanAutoFix       bool
	IsSlow           bool
}

func (lc *Config) ConsiderSlow() *Config {
	lc.IsSlow = true
	return lc
}

func (lc *Config) IsSlowLinter() bool {
	return lc.IsSlow || (lc.LoadMode&packages.NeedTypesInfo != 0 && lc.LoadMode&packages.NeedDeps != 0)
}

func (lc *Config) WithLoadFiles() *Config {
	lc.LoadMode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
	return lc
}

func (lc *Config) WithLoadForGoAnalysis() *Config {
	lc = lc.WithLoadFiles()
	lc.LoadMode |= packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedTypesSizes
	return lc.ConsiderSlow()
}

func (lc *Config) WithLoadTypeInfo() *Config {
	lc = lc.WithLoadFiles()
	lc.LoadMode |= packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedTypesInfo | packages.NeedSyntax
	return lc
}

func (lc *Config) WithLoadDepsTypeInfo() *Config {
	lc = lc.WithLoadTypeInfo()
	lc.LoadMode |= packages.NeedDeps
	return lc
}

func (lc *Config) WithSSA() *Config {
	lc = lc.WithLoadDepsTypeInfo()
	lc.NeedsSSARepr = true
	return lc
}

func (lc *Config) WithPresets(presets ...string) *Config {
	lc.InPresets = presets
	return lc
}

func (lc *Config) WithSpeed(speed int) *Config {
	lc.Speed = speed
	return lc
}

func (lc *Config) WithURL(url string) *Config {
	lc.OriginalURL = url
	return lc
}

func (lc *Config) WithAlternativeNames(names ...string) *Config {
	lc.AlternativeNames = names
	return lc
}

func (lc *Config) WithParent(parentLinterName string) *Config {
	lc.ParentLinterName = parentLinterName
	return lc
}

func (lc *Config) WithAutoFix() *Config {
	lc.CanAutoFix = true
	return lc
}

func (lc *Config) GetSpeed() int {
	return lc.Speed
}

func (lc *Config) AllNames() []string {
	return append([]string{lc.Name()}, lc.AlternativeNames...)
}

func (lc *Config) Name() string {
	return lc.Linter.Name()
}

func NewConfig(linter Linter) *Config {
	lc := &Config{
		Linter: linter,
	}
	return lc.WithLoadFiles()
}