package app

import (
	"errors"
	"fmt"

	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/client"
	"coopcloud.tech/abra/pkg/client/container"
	"coopcloud.tech/abra/pkg/config"
	"github.com/docker/cli/cli/command"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli/v2"
)

var user string
var userFlag = &cli.StringFlag{
	Name:        "user",
	Value:       "",
	Destination: &user,
}

var noTTY bool
var noTTYFlag = &cli.BoolFlag{
	Name:        "no-tty",
	Value:       false,
	Destination: &noTTY,
}

var appRunCommand = &cli.Command{
	Name: "run",
	Flags: []cli.Flag{
		noTTYFlag,
		userFlag,
	},
	Aliases:   []string{"r"},
	ArgsUsage: "<service> <args>...",
	Usage:     "Run a command in a service container",
	Action: func(c *cli.Context) error {
		app := internal.ValidateApp(c)

		if c.Args().Len() < 2 {
			internal.ShowSubcommandHelpAndError(c, errors.New("no <service> provided?"))
		}

		if c.Args().Len() < 3 {
			internal.ShowSubcommandHelpAndError(c, errors.New("no <args> provided?"))
		}

		cl, err := client.New(app.Server)
		if err != nil {
			logrus.Fatal(err)
		}

		serviceName := c.Args().Get(1)
		filters := filters.NewArgs()
		filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))

		containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
		if err != nil {
			logrus.Fatal(err)
		}
		if len(containers) > 1 {
			logrus.Fatalf("expected 1 container but got %d", len(containers))
		}

		cmd := c.Args().Slice()[2:]
		execCreateOpts := types.ExecConfig{
			AttachStderr: true,
			AttachStdin:  true,
			AttachStdout: true,
			Cmd:          cmd,
			Detach:       false,
			Tty:          true,
		}

		if user != "" {
			execCreateOpts.User = user
		}
		if noTTY {
			execCreateOpts.Tty = false
		}

		// FIXME: an absolutely monumental hack to instantiate another command-line
		// client withing our command-line client so that we pass something down
		// the tubes that satisfies the necessary interface requirements. We should
		// refactor our vendored container code to not require all this cruft.  For
		// now, It Works.
		dcli, err := command.NewDockerCli()
		if err != nil {
			logrus.Fatal(err)
		}

		if err := container.RunExec(dcli, cl, containers[0].ID, &execCreateOpts); err != nil {
			logrus.Fatal(err)
		}

		return nil
	},
	BashComplete: func(c *cli.Context) {
		switch c.NArg() {
		case 0:
			appNames, err := config.GetAppNames()
			if err != nil {
				logrus.Warn(err)
			}
			for _, a := range appNames {
				fmt.Println(a)
			}
		case 1:
			appName := c.Args().First()
			serviceNames, err := config.GetAppServiceNames(appName)
			if err != nil {
				logrus.Warn(err)
			}
			for _, s := range serviceNames {
				fmt.Println(s)
			}
		}
	},
}