package cmd

import (
	"io"

	"github.com/spf13/cobra"
)

var orderedEnvironments = []string{"prod", "staging", "dev"}

type envCurrentResponse struct {
	ActiveEnv string `json:"active_env"`
}

type envListResponse struct {
	ActiveEnv    string            `json:"active_env"`
	Environments []envListItemInfo `json:"environments"`
}

type envListItemInfo struct {
	Name      string `json:"name"`
	URL       string `json:"url"`
	HasAPIKey bool   `json:"has_api_key"`
	Active    bool   `json:"active"`
}

type runtimeResolver func() (*RuntimeContext, error)

func envCommandHandler(ctx *RuntimeContext, args []string, stdout io.Writer) error {
	command := newEnvCommand(staticRuntime(ctx), stdout)
	if err := executeCobraCommand(command, args); err != nil {
		return normalizeFamilyCommandError(err, "env")
	}
	return nil
}

func staticRuntime(ctx *RuntimeContext) runtimeResolver {
	return func() (*RuntimeContext, error) {
		return ctx, nil
	}
}

func newEnvCommand(runtime runtimeResolver, stdout io.Writer) *cobra.Command {
	command := newCommand(commandMetadata{
		Use:   "env",
		Short: "Inspect and switch configured environments.",
		Long:  "Inspect and switch configured environments.",
		Example: `  clinic env use staging
  clinic env current
  clinic env list`,
	}, stdout)
	command.RunE = func(c *cobra.Command, _ []string) error {
		return c.Help()
	}
	command.AddCommand(
		newEnvUseCommand(runtime, stdout),
		newEnvCurrentCommand(runtime, stdout),
		newEnvListCommand(runtime, stdout),
	)
	return command
}

func newEnvUseCommand(runtime runtimeResolver, stdout io.Writer) *cobra.Command {
	command := newCommand(commandMetadata{
		Use:   "use <prod|staging|dev>",
		Short: "Persist the active environment in local config.",
		Long: `Persist the active environment in local config.

Arguments:
  <prod|staging|dev>  Environment to make active.`,
		Example: `  clinic env use prod
  clinic env use dev`,
	}, stdout)
	command.Args = cobra.ExactArgs(1)
	command.RunE = func(_ *cobra.Command, args []string) error {
		ctx, err := runtime()
		if err != nil {
			return err
		}
		return runEnvUse(ctx, args, stdout)
	}
	return command
}

func newEnvCurrentCommand(runtime runtimeResolver, stdout io.Writer) *cobra.Command {
	command := newCommand(commandMetadata{
		Use:   "current",
		Short: "Show the active environment from local config.",
		Long: `Show the active environment from local config.

Output:
  JSON with the active environment name.`,
	}, stdout)
	command.Args = cobra.NoArgs
	command.RunE = func(_ *cobra.Command, args []string) error {
		ctx, err := runtime()
		if err != nil {
			return err
		}
		return runEnvCurrent(ctx, args, stdout)
	}
	return command
}

func newEnvListCommand(runtime runtimeResolver, stdout io.Writer) *cobra.Command {
	command := newCommand(commandMetadata{
		Use:   "list",
		Short: "List configured environments and which one is active.",
		Long: `List configured environments and which one is active.

Output:
  JSON with active_env and all configured environments.`,
	}, stdout)
	command.Args = cobra.NoArgs
	command.RunE = func(_ *cobra.Command, args []string) error {
		ctx, err := runtime()
		if err != nil {
			return err
		}
		return runEnvList(ctx, args, stdout)
	}
	return command
}

func runEnvUse(ctx *RuntimeContext, args []string, stdout io.Writer) error {
	if _, err := ctx.Config.ResolveEnv(args[0]); err != nil {
		return err
	}

	ctx.Config.ActiveEnv = args[0]
	if err := ctx.Config.Save(); err != nil {
		return err
	}

	return writeJSON(stdout, envCurrentResponse{ActiveEnv: ctx.Config.ActiveEnv})
}

func runEnvCurrent(ctx *RuntimeContext, args []string, stdout io.Writer) error {
	return writeJSON(stdout, envCurrentResponse{ActiveEnv: ctx.Config.ActiveEnv})
}

func runEnvList(ctx *RuntimeContext, args []string, stdout io.Writer) error {
	items := make([]envListItemInfo, 0, len(orderedEnvironments))
	for _, name := range orderedEnvironments {
		env, ok := ctx.Config.Environments[name]
		if !ok {
			continue
		}
		items = append(items, envListItemInfo{
			Name:      name,
			URL:       env.URL,
			HasAPIKey: env.APIKey != "",
			Active:    ctx.Config.ActiveEnv == name,
		})
	}

	return writeJSON(stdout, envListResponse{
		ActiveEnv:    ctx.Config.ActiveEnv,
		Environments: items,
	})
}
