package install

import (
	"context"
	"fmt"
	"io"
	"log"
	"strings"

	"capact.io/capact/internal/cli"
	"capact.io/capact/internal/cli/capact"
	"capact.io/capact/internal/cli/environment/create"
	"capact.io/capact/internal/cli/printer"

	"github.com/pkg/errors"
	"k8s.io/client-go/rest"
)

// Install installs Capact
func Install(ctx context.Context, w io.Writer, k8sCfg *rest.Config, opts capact.Options) (err error) {
	status := printer.NewStatus(w, "Installing Capact on cluster...")
	defer func() {
		status.End(err == nil)
	}()

	err = opts.Parameters.SetCapactValuesFromOverrides()
	if err != nil {
		return errors.Wrap(err, "while parsing Capact overrides")
	}

	err = opts.Parameters.ResolveVersion()
	if err != nil {
		return errors.Wrap(err, "while resolving version")
	}

	version := opts.Parameters.Version
	if version == capact.LocalVersionTag {
		if opts.LocalRegistryEnabled {
			opts.Parameters.Override.CapactValues.Global.ContainerRegistry.Path = create.ContainerRegistry
		}
		registryPath := opts.Parameters.Override.CapactValues.Global.ContainerRegistry.Path
		registryTag := opts.Parameters.Override.CapactValues.Global.ContainerRegistry.Tag

		// TODO can we parallelize it?
		created, err := capact.BuildImages(ctx, status, registryPath, registryTag, opts.BuildImages)
		if err != nil {
			return errors.Wrap(err, "while building images")
		}

		if opts.LocalRegistryEnabled { // push to Docker registry
			if err := capact.PushImages(ctx, status, created); err != nil {
				return err
			}
		} else { // load into a given environment
			if err := capact.LoadImages(ctx, status, created, opts); err != nil {
				return err
			}
		}
	}

	if cli.VerboseMode.IsEnabled() {
		status.InfoWithBody("Installation details:", installDetails(opts))
	}

	configuration, err := capact.GetActionConfiguration(k8sCfg, opts.Namespace)
	if err != nil {
		return err
	}

	status.Step("Loading Capact CRDs")
	crd, err := capact.LoadCRDDefinition(opts.Parameters.ActionCRDLocation)
	if err != nil {
		return err
	}

	status.Step("Applying Capact CRDs")
	err = capact.ApplyCRD(ctx, k8sCfg, crd)
	if err != nil {
		return err
	}

	helm := capact.NewHelm(configuration, opts)

	status.Step("Creating namespace %s", opts.Namespace)
	err = capact.CreateNamespace(ctx, k8sCfg, opts.Namespace)
	if err != nil {
		return err
	}

	log.SetOutput(io.Discard)
	err = helm.InstallComponents(ctx, w, status)
	if err != nil {
		return err
	}

	if opts.UpdateHostsFile || opts.UpdateTrustedCerts {
		status.Step("Preparing local changes")
		status.End(true)
	}

	if opts.UpdateHostsFile {
		err = capact.AddComponentsToHostsFile()
		if err != nil {
			return err
		}
	}

	if opts.UpdateTrustedCerts {
		err = capact.TrustSelfSigned()
		if err != nil {
			return err
		}
	}

	welcomeMessage(w)

	return nil
}

func welcomeMessage(w io.Writer) {
	msg := `
Capact installed successfully!

To begin working with Capact, use 'capact login' command.
To read more how to use CLI, check out the documentation on https://capact.io/docs/cli/getting-started#first-use.
`
	fmt.Fprintln(w, msg)
}

func installDetails(opts capact.Options) string {
	out := &strings.Builder{}
	fmt.Fprintf(out, "\tVersion: %s\n", opts.Parameters.Version)
	fmt.Fprintf(out, "\tHelm repository: %s\n", opts.Parameters.Override.HelmRepo)
	fmt.Fprintf(out, "\tCRD location: %s\n\n", opts.Parameters.ActionCRDLocation)

	return out.String()
}
