package main

import (
	"go/parser"
	"go/token"
	"io/fs"
	"path/filepath"
	"slices"
	"strings"

	"github.com/dave/dst/decorator"
)

// Strings to look for to identify generated files.
var generatedTerms = []string{
	"do not edit",
	"generated by",

	// Related to gqlgen.
	// https://github.com/segmentio/golines/pull/86
	// NOTE(ldez): This is not a good practice, the Go convention must be followed.
	// NOTE(ldez): It has false negatives:
	// https://github.com/99designs/gqlgen/blob/523f4344e46f265edc79e2527b752ace9f250a35/plugin/resolvergen/resolver.go#L249
	// TODO(ldez): it should be removed or improved.
	"automatically regenerated",
}

func (r *Runner) skipDir(subPath string, f fs.DirEntry) bool {
	if f.IsDir() {
		switch f.Name() {
		case "vendor", "testdata", "node_modules":
			return true

		default:
			return f.Name() != "." && strings.HasPrefix(f.Name(), ".")
		}
	}

	if len(r.ignoredDirs) == 0 {
		return false
	}

	parts := strings.SplitSeq(subPath, "/")
	for part := range parts {
		if slices.Contains(r.ignoredDirs, part) {
			return true
		}
	}

	return false
}

func (r *Runner) isIgnoredFile(path string) bool {
	if !strings.HasSuffix(path, ".go") {
		return true
	}

	_, fileName := filepath.Split(path)

	return r.ignoreGenerated && strings.HasPrefix(fileName, "generated_")
}

// isGenerated checks whether the provided file bytes are from a generated file.
// Note(ldez): it follows a non-conventional notation: it should be changed in the future.
func isGenerated(content []byte) bool {
	if len(content) == 0 {
		return false
	}

	file, err := decorator.ParseFile(
		token.NewFileSet(),
		"", content,
		parser.PackageClauseOnly|parser.ParseComments,
	)
	if err != nil {
		return false
	}

	decorations := file.Decorations()

	if decorations == nil {
		return false
	}

	for _, line := range decorations.Start {
		if len(line) == 0 || !strings.HasPrefix(line, "//") {
			continue
		}

		// This approach as false positives because it will match lines like:
		// "// Code generated by ..." or "// DO NOT EDIT"
		// instead of the conventional "^// Code generated .* DO NOT EDIT\.$"
		// https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source
		for _, term := range generatedTerms {
			if strings.Contains(strings.ToLower(line), term) {
				return true
			}
		}
	}

	return false
}
