// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later

package e2e

import (
	"bufio"
	"os"
	"strings"

	"code.gitea.io/gitea/modules/log"

	"github.com/gobwas/glob"
)

var (
	changesetFiles     []string
	changesetAvailable bool
	globalFullRun      bool
)

func initChangedFiles() {
	var changes string
	changes, changesetAvailable = os.LookupEnv("CHANGED_FILES")
	// the output of the Action seems to actually contain \n and not a newline literal
	changesetFiles = strings.Split(changes, `\n`)
	log.Info("Only running tests covered by a subset of test files. Received the following list of CHANGED_FILES: %q", changesetFiles)

	globalPatterns := []string{
		// meta and config
		"Makefile",
		"playwright.config.js",
		".forgejo/workflows/testing.yml",
		"tests/e2e/*.go",
		"tests/e2e/shared/*",
		// frontend files
		"frontend/*.js",
		"frontend/{base,index}.css",
		// templates
		"templates/base/**",
	}
	fullRunPatterns := []glob.Glob{}
	for _, expr := range globalPatterns {
		fullRunPatterns = append(fullRunPatterns, glob.MustCompile(expr, '.', '/'))
	}
	globalFullRun = false
	for _, changedFile := range changesetFiles {
		for _, pattern := range fullRunPatterns {
			if pattern.Match(changedFile) {
				globalFullRun = true
				log.Info("Changed files match global test pattern, running all tests")
				return
			}
		}
	}
}

func canSkipTest(testFile string) bool {
	// run all tests when environment variable is not set or changes match global pattern
	if !changesetAvailable || globalFullRun {
		return false
	}

	for _, changedFile := range changesetFiles {
		if strings.HasSuffix(testFile, changedFile) {
			return false
		}
		for _, pattern := range getWatchPatterns(testFile) {
			if pattern.Match(changedFile) {
				return false
			}
		}
	}
	return true
}

func getWatchPatterns(filename string) []glob.Glob {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)

	watchSection := false
	patterns := []glob.Glob{}
	for scanner.Scan() {
		line := scanner.Text()
		// check for watch block
		if strings.HasPrefix(line, "// @watch") {
			if watchSection {
				break
			}
			watchSection = true
		}
		if !watchSection {
			continue
		}

		line = strings.TrimPrefix(line, "// ")
		if line != "" {
			globPattern, err := glob.Compile(line, '.', '/')
			if err != nil {
				log.Fatal("Invalid glob pattern '%s' (skipped): %v", line, err)
			}
			patterns = append(patterns, globPattern)
		}
	}
	// if no watch block in file
	if !watchSection {
		patterns = append(patterns, glob.MustCompile("*"))
	}
	return patterns
}