package wand

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
)

func exec(stdin io.Reader, stdout, stderr io.Writer, exit func(int), cmd Cmd, conf Config, env, args []string) {
	_, cmd.name = filepath.Split(args[0])
	if err := validateCommand(cmd, conf); err != nil {
		fmt.Fprintf(stderr, "program error: %v\n", err)
		exit(1)
		return
	}

	if os.Getenv("_wandgenerate") == "man" {
		if err := generateMan(stdout, cmd, conf); err != nil {
			fmt.Fprintln(stderr, err)
			exit(1)
		}

		return
	}

	if os.Getenv("_wandgenerate") == "markdown" {
		level, _ := strconv.Atoi(os.Getenv("_wandmarkdownlevel"))
		if err := generateMarkdown(stdout, cmd, conf, level); err != nil {
			fmt.Fprintln(stderr, err)
			exit(1)
		}

		return
	}

	cmd = insertHelp(cmd)

	// will need root command for the config and the env:
	rootCmd := cmd
	cmd, fullCmd, args := selectCommand(cmd, args[1:])
	if cmd.helpFor != nil {
		if err := showHelp(stdout, cmd, conf, fullCmd); err != nil {
			fmt.Fprintln(stderr, err)
			exit(1)
		}

		return
	}

	if cmd.version != "" {
		if err := showVersion(stdout, cmd); err != nil {
			fmt.Fprintln(stderr, err)
			exit(1)
		}

		return
	}

	var bo []string
	if cmd.impl != nil {
		bo = boolOptions(cmd)
	}

	cl := readArgs(bo, args)
	if hasHelpOption(cmd, cl.options) {
		if err := showHelp(stdout, cmd, conf, fullCmd); err != nil {
			fmt.Fprintln(stderr, err)
			exit(1)
		}

		return
	}

	if cmd.impl == nil {
		fmt.Fprintln(stderr, errors.New("subcommand not specified"))
		suggestHelp(stderr, cmd, fullCmd)
		exit(1)
		return
	}

	c, err := readConfig(rootCmd, cl, conf)
	if err != nil {
		fmt.Fprintf(stderr, "configuration error: %v", err)
		exit(1)
		return
	}

	e := readEnv(rootCmd.name, env)
	if err := validateInput(cmd, conf, c, e, cl); err != nil {
		fmt.Fprintln(stderr, err)
		suggestHelp(stderr, cmd, fullCmd)
		exit(1)
		return
	}

	output, err := apply(stdin, stdout, cmd, c, e, cl)
	if err != nil {
		fmt.Fprintln(stderr, err)
		exit(1)
		return
	}

	if err := printOutput(stdout, output); err != nil {
		fmt.Fprintln(stderr, err)
		exit(1)
		return
	}
}
