package kubernetes

import (
	"fmt"

	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/utils"
	"github.com/wtfutil/wtf/view"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Widget contains all the config for the widget
type Widget struct {
	view.TextWidget

	objects    []string
	title      string
	kubeconfig string
	namespaces []string
	context    string
	settings   *Settings
}

// NewWidget creates a new instance of the widget
func NewWidget(app *tview.Application, settings *Settings) *Widget {
	widget := Widget{
		TextWidget: view.NewTextWidget(app, settings.common),

		objects:    settings.objects,
		title:      settings.title,
		kubeconfig: settings.kubeconfig,
		namespaces: settings.namespaces,
		settings:   settings,
		context:    settings.context,
	}

	widget.View.SetWrap(true)

	return &widget
}

// Refresh executes the command and updates the view with the results
func (widget *Widget) Refresh() {
	title := widget.generateTitle()
	client, err := widget.getInstance()

	if err != nil {
		widget.Redraw(func() (string, string, bool) { return title, err.Error(), true })
		return
	}

	var content string

	if utils.Includes(widget.objects, "nodes") {
		nodeList, nodeError := client.getNodes()
		if nodeError != nil {
			widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting node data [white]\n", true })
			return
		}
		content += fmt.Sprintf("[%s]Nodes[white]\n", widget.settings.common.Colors.Subheading)
		for _, node := range nodeList {
			content += fmt.Sprintf("%s\n", node)
		}
		content += "\n"
	}

	if utils.Includes(widget.objects, "deployments") {
		deploymentList, deploymentError := client.getDeployments(widget.namespaces)
		if deploymentError != nil {
			widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting deployment data [white]\n", true })
			return
		}
		content += fmt.Sprintf("[%s]Deployments[white]\n", widget.settings.common.Colors.Subheading)
		for _, deployment := range deploymentList {
			content += fmt.Sprintf("%s\n", deployment)
		}
		content += "\n"
	}

	if utils.Includes(widget.objects, "pods") {
		podList, podError := client.getPods(widget.namespaces)
		if podError != nil {
			widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting pod data [white]\n", false })
			return
		}
		content += fmt.Sprintf("[%s]Pods[white]\n", widget.settings.common.Colors.Subheading)
		for _, pod := range podList {
			content += fmt.Sprintf("%s\n", pod)
		}
		content += "\n"
	}

	widget.Redraw(func() (string, string, bool) { return title, content, false })
}

/* -------------------- Unexported Functions -------------------- */

// generateTitle generates a title for the widget
func (widget *Widget) generateTitle() string {
	if len(widget.title) != 0 {
		return widget.title
	}
	title := "Kube"

	if widget.context != "" {
		title = fmt.Sprintf("%s (%s)", title, widget.context)
	}

	if len(widget.namespaces) == 1 {
		title += fmt.Sprintf(" - Namespace: %s", widget.namespaces[0])
	} else if len(widget.namespaces) > 1 {
		title += fmt.Sprintf(" - Namespaces: %q", widget.namespaces)
	}
	return title
}

// getPods returns a slice of pod strings
func (client *clientInstance) getPods(namespaces []string) ([]string, error) {
	var podList []string
	if len(namespaces) != 0 {
		for _, namespace := range namespaces {
			pods, err := client.Client.CoreV1().Pods(namespace).List(metav1.ListOptions{})
			if err != nil {
				return nil, err
			}

			for _, pod := range pods.Items {
				var podString string
				status := pod.Status.Phase
				name := pod.ObjectMeta.Name
				if len(namespaces) == 1 {
					podString = fmt.Sprintf("%-50s %s", name, status)
				} else {
					podString = fmt.Sprintf("%-20s %-50s %s", namespace, name, status)
				}
				podList = append(podList, podString)
			}
		}
	} else {
		pods, err := client.Client.CoreV1().Pods("").List(metav1.ListOptions{})
		if err != nil {
			return nil, err
		}
		for _, pod := range pods.Items {
			podString := fmt.Sprintf("%-20s %-50s %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name, pod.Status.Phase)
			podList = append(podList, podString)
		}
	}

	return podList, nil
}

// get Deployments returns a string slice of pod strings
func (client *clientInstance) getDeployments(namespaces []string) ([]string, error) {
	var deploymentList []string
	if len(namespaces) != 0 {
		for _, namespace := range namespaces {
			deployments, err := client.Client.AppsV1().Deployments(namespace).List(metav1.ListOptions{})
			if err != nil {
				return nil, err
			}

			for _, deployment := range deployments.Items {
				var deployString string
				if len(namespaces) == 1 {
					deployString = fmt.Sprintf("%-50s", deployment.ObjectMeta.Name)
				} else {
					deployString = fmt.Sprintf("%-20s %-50s", deployment.ObjectMeta.Namespace, deployment.ObjectMeta.Name)
				}
				deploymentList = append(deploymentList, deployString)
			}
		}
	} else {
		deployments, err := client.Client.AppsV1().Deployments("").List(metav1.ListOptions{})
		if err != nil {
			return nil, err
		}

		for _, deployment := range deployments.Items {
			deployString := fmt.Sprintf("%-20s %-50s", deployment.ObjectMeta.Namespace, deployment.ObjectMeta.Name)
			deploymentList = append(deploymentList, deployString)
		}
	}
	return deploymentList, nil
}

// getNodes returns a string slice of nodes
func (client *clientInstance) getNodes() ([]string, error) {
	var nodeList []string

	nodes, err := client.Client.CoreV1().Nodes().List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	for _, node := range nodes.Items {
		var nodeStatus string
		for _, condition := range node.Status.Conditions {
			if condition.Reason == "KubeletReady" {
				switch {
				case condition.Status == "True":
					nodeStatus = "Ready"
				case condition.Reason == "False":
					nodeStatus = "NotReady"
				default:
					nodeStatus = "Unknown"
				}
			}
		}
		nodeString := fmt.Sprintf("%-50s %s", node.ObjectMeta.Name, nodeStatus)
		nodeList = append(nodeList, nodeString)
	}
	return nodeList, nil
}
