package build

import (
	"path/filepath"
	"slices"

	"github.com/host-uk/core/pkg/io"
)

// Marker files for project type detection.
const (
	markerGoMod       = "go.mod"
	markerWails       = "wails.json"
	markerNodePackage = "package.json"
	markerComposer    = "composer.json"
)

// projectMarker maps a marker file to its project type.
type projectMarker struct {
	file        string
	projectType ProjectType
}

// markers defines the detection order. More specific types come first.
// Wails projects have both wails.json and go.mod, so wails is checked first.
var markers = []projectMarker{
	{markerWails, ProjectTypeWails},
	{markerGoMod, ProjectTypeGo},
	{markerNodePackage, ProjectTypeNode},
	{markerComposer, ProjectTypePHP},
}

// Discover detects project types in the given directory by checking for marker files.
// Returns a slice of detected project types, ordered by priority (most specific first).
// For example, a Wails project returns [wails, go] since it has both wails.json and go.mod.
func Discover(fs io.Medium, dir string) ([]ProjectType, error) {
	var detected []ProjectType

	for _, m := range markers {
		path := filepath.Join(dir, m.file)
		if fileExists(fs, path) {
			// Avoid duplicates (shouldn't happen with current markers, but defensive)
			if !slices.Contains(detected, m.projectType) {
				detected = append(detected, m.projectType)
			}
		}
	}

	return detected, nil
}

// PrimaryType returns the most specific project type detected in the directory.
// Returns empty string if no project type is detected.
func PrimaryType(fs io.Medium, dir string) (ProjectType, error) {
	types, err := Discover(fs, dir)
	if err != nil {
		return "", err
	}
	if len(types) == 0 {
		return "", nil
	}
	return types[0], nil
}

// IsGoProject checks if the directory contains a Go project (go.mod or wails.json).
func IsGoProject(fs io.Medium, dir string) bool {
	return fileExists(fs, filepath.Join(dir, markerGoMod)) ||
		fileExists(fs, filepath.Join(dir, markerWails))
}

// IsWailsProject checks if the directory contains a Wails project.
func IsWailsProject(fs io.Medium, dir string) bool {
	return fileExists(fs, filepath.Join(dir, markerWails))
}

// IsNodeProject checks if the directory contains a Node.js project.
func IsNodeProject(fs io.Medium, dir string) bool {
	return fileExists(fs, filepath.Join(dir, markerNodePackage))
}

// IsPHPProject checks if the directory contains a PHP project.
func IsPHPProject(fs io.Medium, dir string) bool {
	return fileExists(fs, filepath.Join(dir, markerComposer))
}

// IsCPPProject checks if the directory contains a C++ project (CMakeLists.txt).
func IsCPPProject(fs io.Medium, dir string) bool {
	return fileExists(fs, filepath.Join(dir, "CMakeLists.txt"))
}

// fileExists checks if a file exists and is not a directory.
func fileExists(fs io.Medium, path string) bool {
	return fs.IsFile(path)
}
