package logutils

import (
	"os"
	"strings"
)

// EnvTestRun value: "1"
const EnvTestRun = "GL_TEST_RUN"

// envDebug value: one or several debug keys.
// examples:
// - Remove output to `/dev/null`: `GL_DEBUG=linters_output ./golangci-lint run`
// - Show linters configuration: `GL_DEBUG=enabled_linters golangci-lint run`
// - Some analysis details: `GL_DEBUG=goanalysis/analyze,goanalysis/facts golangci-lint run`
const envDebug = "GL_DEBUG"

const (
	DebugKeyBinSalt        = "bin_salt"      // Forces the usage of constant as salt (only for maintainers).
	DebugKeyGoModSalt      = "gomod_salt"    // Display logs related to the salt computation from the go.mod file.
	DebugKeyConfigReader   = "config_reader" // Display logs related to configuration loading.
	DebugKeyEmpty          = ""
	DebugKeyEnabledLinters = "enabled_linters" // Display logs related to the enabled linters inside the [lintersdb.Manager].
	DebugKeyExec           = "exec"            // Display logs related to the lock file.
	DebugKeyGoEnv          = "goenv"           // Display logs related to [goenv.Env].
	DebugKeyLintersContext = "linters_context" // Display logs related to the package analysis context (not related to [context.Context]).
	DebugKeyLintersDB      = "lintersdb"       // Display logs related to the linters/formatters loading.
	DebugKeyLoader         = "loader"          // Display logs related to package loading (including `go/packages` internal debugging).
	DebugKeyPkgCache       = "pkgcache"        // Display logs related to cache.
	DebugKeyRunner         = "runner"          // Display logs related to the linter runner.
	DebugKeyStopwatch      = "stopwatch"       // Display logs related to the stopwatch of the cache.
	DebugKeyTest           = "test"            // Display debug logs during integration tests.
)

// Printers.
const (
	DebugKeyCheckstylePrinter  = "checkstyle_printer"
	DebugKeyCodeClimatePrinter = "codeclimate_printer"
	DebugKeySarifPrinter       = "sarif_printer"
	DebugKeyTabPrinter         = "tab_printer"
	DebugKeyTeamCityPrinter    = "teamcity_printer"
	DebugKeyTextPrinter        = "text_printer"
)

// Processors.
const (
	DebugKeyExclusionPaths      = "exclusion_paths"
	DebugKeyExclusionRules      = "exclusion_rules"
	DebugKeyFilenameUnadjuster  = "filename_unadjuster"
	DebugKeyGeneratedFileFilter = "generated_file_filter" // Debugs a filter excluding autogenerated source code.
	DebugKeyInvalidIssue        = "invalid_issue"
	DebugKeyMaxFromLinter       = "max_from_linter"
	DebugKeyMaxSameIssues       = "max_same_issues"
	DebugKeyNolintFilter        = "nolint_filter" // Debugs a filter excluding issues by `//nolint` comments.
	DebugKeyPathAbsoluter       = "path_absoluter"
	DebugKeyPathPrettifier      = "path_prettifier"
	DebugKeyPathRelativity      = "path_relativity"
	DebugKeySeverityRules       = "severity_rules"
	DebugKeySourceCode          = "source_code"
)

// Analysis.
const (
	DebugKeyGoAnalysis = "goanalysis"

	DebugKeyGoAnalysisAnalyze     = DebugKeyGoAnalysis + "/analyze"
	DebugKeyGoAnalysisIssuesCache = DebugKeyGoAnalysis + "/issues/cache"
	DebugKeyGoAnalysisMemory      = DebugKeyGoAnalysis + "/memory"

	DebugKeyGoAnalysisFacts        = DebugKeyGoAnalysis + "/facts"
	DebugKeyGoAnalysisFactsCache   = DebugKeyGoAnalysisFacts + "/cache"
	DebugKeyGoAnalysisFactsExport  = DebugKeyGoAnalysisFacts + "/export"
	DebugKeyGoAnalysisFactsInherit = DebugKeyGoAnalysisFacts + "/inherit"
)

// Linters and Formatters.
const (
	DebugKeyFormatter        = "formatter"         // Display logs from the shared logger for formatters.
	DebugKeyFormattersOutput = "formatters_output" // Display logs from formatters themselves.
	DebugKeyLinter           = "linter"            // Display logs from the shared logger for linters.
	DebugKeyLintersOutput    = "linters_output"    // Display logs from linters themselves.

	DebugKeyForbidigo   = "forbidigo"   // Debugs `forbidigo` linter.
	DebugKeyGoCritic    = "gocritic"    // Debugs `gocritic` linter.
	DebugKeyGovet       = "govet"       // Debugs `govet` linter.
	DebugKeyRevive      = "revive"      // Debugs `revive` linter.
	DebugKeyStaticcheck = "staticcheck" // Debugs `staticcheck` linter.
)

func getEnabledDebugs() map[string]bool {
	ret := map[string]bool{}
	debugVar := os.Getenv(envDebug)
	if debugVar == "" {
		return ret
	}

	for tag := range strings.SplitSeq(debugVar, ",") {
		ret[tag] = true
	}

	return ret
}

var enabledDebugs = getEnabledDebugs()

type DebugFunc func(format string, args ...any)

func nopDebugf(_ string, _ ...any) {}

func Debug(tag string) DebugFunc {
	if !enabledDebugs[tag] {
		return nopDebugf
	}

	logger := NewStderrLog(tag)
	logger.SetLevel(LogLevelDebug)

	return func(format string, args ...any) {
		logger.Debugf(format, args...)
	}
}

func HaveDebugTag(tag string) bool {
	return enabledDebugs[tag]
}

var verbose bool

func SetupVerboseLog(log Log, isVerbose bool) {
	if isVerbose {
		verbose = isVerbose
		log.SetLevel(LogLevelInfo)
	}
}

func IsVerbose() bool {
	return verbose
}
