package factory

import (
	"errors"
	"fmt"
	"strings"
	"testing"
	"time"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"

	buildapi "github.com/openshift/origin/pkg/build/api"
	controller "github.com/openshift/origin/pkg/controller"
)

type buildUpdater struct {
	Build *buildapi.Build
}

func (b *buildUpdater) Update(namespace string, build *buildapi.Build) error {
	b.Build = build
	return nil
}

func TestLimitedLogAndRetryFinish(t *testing.T) {
	updater := &buildUpdater{}
	err := errors.New("funky error")

	now := unversioned.Now()
	retry := controller.Retry{
		Count:          0,
		StartTimestamp: unversioned.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-31, now.Second(), now.Nanosecond(), now.Location()),
	}
	if limitedLogAndRetry(updater, 30*time.Minute)(&buildapi.Build{Status: buildapi.BuildStatus{Phase: buildapi.BuildPhaseNew}}, err, retry) {
		t.Error("Expected no more retries after reaching timeout!")
	}
	if updater.Build == nil {
		t.Fatal("BuildUpdater wasn't called!")
	}
	if updater.Build.Status.Phase != buildapi.BuildPhaseFailed {
		t.Errorf("Expected status %s, got %s!", buildapi.BuildPhaseFailed, updater.Build.Status.Phase)
	}
	if !strings.Contains(updater.Build.Status.Message, "Funky error.") {
		t.Errorf("Expected message to contain %v, got %s!", err.Error(), updater.Build.Status.Message)
	}
	if updater.Build.Status.CompletionTimestamp == nil {
		t.Error("Expected CompletionTimestamp to be set!")
	}
}

func TestLimitedLogAndRetryProcessing(t *testing.T) {
	updater := &buildUpdater{}
	err := errors.New("funky error")

	now := unversioned.Now()
	retry := controller.Retry{
		Count:          0,
		StartTimestamp: unversioned.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-10, now.Second(), now.Nanosecond(), now.Location()),
	}
	if !limitedLogAndRetry(updater, 30*time.Minute)(&buildapi.Build{Status: buildapi.BuildStatus{Phase: buildapi.BuildPhaseNew}}, err, retry) {
		t.Error("Expected more retries!")
	}
	if updater.Build != nil {
		t.Fatal("BuildUpdater shouldn't be called!")
	}
}

func TestControllerRetryFunc(t *testing.T) {
	obj := &kapi.Pod{}
	obj.Name = "testpod"
	obj.Namespace = "testNS"

	testErr := fmt.Errorf("test error")
	tests := []struct {
		name       string
		retryCount int
		isFatal    func(err error) bool
		err        error
		expect     bool
	}{
		{
			name:       "maxRetries-1 retries",
			retryCount: maxRetries - 1,
			err:        testErr,
			expect:     true,
		},
		{
			name:       "maxRetries+1 retries",
			retryCount: maxRetries + 1,
			err:        testErr,
			expect:     false,
		},
		{
			name:       "isFatal returns true",
			retryCount: 0,
			err:        testErr,
			isFatal: func(err error) bool {
				if err != testErr {
					t.Errorf("Unexpected error: %v", err)
				}
				return true
			},
			expect: false,
		},
		{
			name:       "isFatal returns false",
			retryCount: 0,
			err:        testErr,
			isFatal: func(err error) bool {
				if err != testErr {
					t.Errorf("Unexpected error: %v", err)
				}
				return false
			},
			expect: true,
		},
	}

	for _, tc := range tests {
		f := retryFunc("test kind", tc.isFatal)
		result := f(obj, tc.err, controller.Retry{Count: tc.retryCount})
		if result != tc.expect {
			t.Errorf("%s: unexpected result. Expected: %v. Got: %v", tc.name, tc.expect, result)
		}
	}
}