package cmd

import (
	c "docker4ssh/config"
	"docker4ssh/docker"
	"docker4ssh/validate"
	"fmt"
	"github.com/docker/docker/client"
	"github.com/spf13/cobra"
	"os"
	"path/filepath"
	"strings"
)

var cli *client.Client

var validateCmd = &cobra.Command{
	Use:   "validate",
	Short: "Validate docker4ssh specific files (config / profile files)",

	PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
		cli, err = docker.InitCli()
		return err
	},
}

var validateStrictFlag bool

var validateConfigCmd = &cobra.Command{
	Use:   "config [files]",
	Short: "Validate a docker4ssh config file",

	RunE: func(cmd *cobra.Command, args []string) error {
		return validateConfig(args)
	},
}

var validateConfigFileFlag string

var validateProfileCmd = &cobra.Command{
	Use:   "profile [files]",
	Short: "Validate docker4ssh profile files",

	RunE: func(cmd *cobra.Command, args []string) error {
		return validateProfile(args)
	},
}

func validateConfig(args []string) error {
	config, err := c.LoadConfig(validateConfigFileFlag, false)
	if err != nil {
		return err
	}

	validator := validate.NewConfigValidator(cli, validateStrictFlag, config)

	var result *validate.ValidatorResult
	if len(args) == 0 {
		result = validator.Validate()
	} else {
		var validateFuncs []func() *validate.ValidatorResult
		for _, arg := range args {
			switch strings.ToLower(arg) {
			case "profile":
				validateFuncs = append(validateFuncs, validator.ValidateProfile)
			case "api":
				validateFuncs = append(validateFuncs, validator.ValidateAPI)
			case "ssh":
				validateFuncs = append(validateFuncs, validator.ValidateSSH)
			case "database":
				validateFuncs = append(validateFuncs, validator.ValidateDatabase)
			case "network":
				validateFuncs = append(validateFuncs, validator.ValidateNetwork)
			case "logging":
				validateFuncs = append(validateFuncs, validator.ValidateLogging)
			default:
				return fmt.Errorf("'%s' is not a valid config section", arg)
			}
		}

		var errors []*validate.ValidateError
		for _, validateFunc := range validateFuncs {
			errors = append(errors, validateFunc().Errors...)
		}

		result = &validate.ValidatorResult{
			Strict: validateStrictFlag,
			Errors: errors,
		}
	}

	fmt.Println(result.String())

	if len(result.Errors) > 0 {
		os.Exit(1)
	}

	return nil
}

func validateProfile(args []string) error {
	var files []string

	if len(args) == 0 {
		args = append(args, "/etc/docker4ssh/profile")
	}
	for _, arg := range args {
		stat, err := os.Stat(arg)
		if os.IsNotExist(err) {
			return fmt.Errorf("file %s does not exist: %v", arg, err)
		}
		if stat.IsDir() {
			dir, err := os.ReadDir(arg)
			if err != nil {
				return fmt.Errorf("failed to read directory %s: %v", arg, err)
			}
			for _, file := range dir {
				path, err := filepath.Abs(file.Name())
				if err != nil {
					return err
				}
				files = append(files, path)
			}
		}
	}

	var profiles c.Profiles
	for _, file := range files {
		p, err := c.LoadProfileFile(file, c.HardcodedPreProfile())
		if err != nil {
			return err
		}
		profiles = append(profiles, p...)
	}

	var errors []*validate.ValidateError
	for _, profile := range profiles {
		errors = append(errors, validate.NewProfileValidator(cli, validateStrictFlag, profile).Validate().Errors...)
	}

	result := validate.ValidatorResult{
		Strict: validateStrictFlag,
		Errors: errors,
	}

	fmt.Println(result.String())

	return nil
}

func init() {
	rootCmd.AddCommand(validateCmd)
	validateCmd.PersistentFlags().BoolVarP(&validateStrictFlag, "strict", "s", false, "If the check should be strict")

	validateCmd.AddCommand(validateConfigCmd)
	validateConfigCmd.Flags().StringVarP(&validateConfigFileFlag, "file", "f", "/etc/docker4ssh/docker4ssh.conf", "Specify a file to check")

	validateCmd.AddCommand(validateProfileCmd)
}