package kubernetes

import (
	"fmt"
	"time"

	apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1"
	"github.com/docker/cli/kubernetes/labels"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

// DeployWatcher watches a stack deployement
type DeployWatcher struct {
	Pods corev1.PodInterface
}

// Watch watches a stuck deployement and return a chan that will holds the state of the stack
func (w DeployWatcher) Watch(stack *apiv1beta1.Stack, serviceNames []string) chan bool {
	stop := make(chan bool)

	go w.waitForPods(stack.Name, serviceNames, stop)

	return stop
}

func (w DeployWatcher) waitForPods(stackName string, serviceNames []string, stop chan bool) {
	starts := map[string]int32{}

	for {
		time.Sleep(1 * time.Second)

		list, err := w.Pods.List(metav1.ListOptions{
			LabelSelector:        labels.SelectorForStack(stackName),
			IncludeUninitialized: true,
		})
		if err != nil {
			stop <- true
			return
		}

		for i := range list.Items {
			pod := list.Items[i]
			if pod.Status.Phase != apiv1.PodRunning {
				continue
			}

			startCount := startCount(pod)
			serviceName := pod.Labels[labels.ForServiceName]
			if startCount != starts[serviceName] {
				if startCount == 1 {
					fmt.Printf(" - Service %s has one container running\n", serviceName)
				} else {
					fmt.Printf(" - Service %s was restarted %d %s\n", serviceName, startCount-1, timeTimes(startCount-1))
				}

				starts[serviceName] = startCount
			}
		}

		if allReady(list.Items, serviceNames) {
			stop <- true
			return
		}
	}
}

func startCount(pod apiv1.Pod) int32 {
	restart := int32(0)

	for _, status := range pod.Status.ContainerStatuses {
		restart += status.RestartCount
	}

	return 1 + restart
}

func allReady(pods []apiv1.Pod, serviceNames []string) bool {
	serviceUp := map[string]bool{}

	for _, pod := range pods {
		if time.Since(pod.GetCreationTimestamp().Time) < 10*time.Second {
			return false
		}

		ready := false
		for _, cond := range pod.Status.Conditions {
			if cond.Type == apiv1.PodReady && cond.Status == apiv1.ConditionTrue {
				ready = true
			}
		}

		if !ready {
			return false
		}

		serviceName := pod.Labels[labels.ForServiceName]
		serviceUp[serviceName] = true
	}

	for _, serviceName := range serviceNames {
		if !serviceUp[serviceName] {
			return false
		}
	}

	return true
}

func timeTimes(n int32) string {
	if n == 1 {
		return "time"
	}

	return "times"
}
