package filematches

import (
	"errors"
	"log"
	"os"
	"path/filepath"

	"github.com/yannh/kubeconform/pkg/validator"
)

type FileMatches struct {
	dest            string
	patterns        []string
	deploymentFiles []string
}

func (f *FileMatches) findDeploymentFiles(dest string) error {
	err := filepath.Walk(dest, f.walkFunc)
	if err != nil {
		return err
	}
	return nil
}

func (f *FileMatches) walkFunc(path string, info os.FileInfo, err error) error {
	if err != nil {
		log.Fatal(err)
		return err
	}
	if info.IsDir() {
		return nil
	}
	for _, pattern := range f.patterns {
		if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {
			return err
		} else if matched && isValidK8sFile(path) {
			f.deploymentFiles = append(f.deploymentFiles, path)
		}
	}
	return nil
}

// TODO: maybe generalize this function in the future
func isValidK8sFile(filePath string) bool {
	f, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}

	v, err := validator.New(nil, validator.Opts{Strict: true})
	if err != nil {
		log.Fatalf("failed initializing validator: %s", err)
	}

	for i, res := range v.Validate(filePath, f) { // A file might contain multiple resources
		// File starts with ---, the parser assumes a first empty resource
		if res.Status == validator.Invalid {
			log.Printf("resource %d in file %s is not valid: %s", i, filePath, res.Err)
			return false
		}
		if res.Status == validator.Error {
			log.Printf("error while processing resource %d in file %s: %s", i, filePath, res.Err)
			return false
		}
	}

	return true
}

func (f *FileMatches) hasDeploymentFiles() bool {
	return len(f.deploymentFiles) > 0
}

func createK8sFileMatches(dest string) *FileMatches {
	l := &FileMatches{
		dest:            dest,
		patterns:        []string{"*.yaml", "*.yml"},
		deploymentFiles: []string{},
	}
	err := l.findDeploymentFiles(dest)
	if err != nil {
		log.Fatal(err)
	}

	return l
}

func SearchDirectory(dest string) (bool, bool, error) {
	// check if Dockerfile exists
	var hasDockerFile bool
	dockerfilePath := dest + "/Dockerfile"
	_, err := os.Stat(dockerfilePath)
	if err == nil {
		hasDockerFile = true
	} else if errors.Is(err, os.ErrNotExist) {
		hasDockerFile = false
	} else {
		return false, false, err
	}

	// recursive directory search for valid yaml files
	fileMatches := createK8sFileMatches(dest)
	_, err = FindDraftDeploymentFiles(dest)
	hasDeploymentFiles := fileMatches.hasDeploymentFiles() || err == nil
	return hasDockerFile, hasDeploymentFiles, nil
}

func FindDraftDeploymentFiles(dest string) (deploymentType string, err error) {
	if _, err := os.Stat(dest + "/charts"); !os.IsNotExist(err) {
		return "helm", nil
	}
	if _, err := os.Stat(dest + "/overlays"); !os.IsNotExist(err) {
		return "kustomize", nil
	}
	if _, err := os.Stat(dest + "/manifests"); !os.IsNotExist(err) {
		return "manifests", nil
	}

	return "", errors.New("no supported deployment files found")
}
