package main

import (
	"bytes"
	"reflect"
	"testing"
	"time"
)

const write = "write"
const sleep = "sleep"

type SpyCountdownOperations struct {
	Calls []string
}

func (s *SpyCountdownOperations) Sleep() {
	s.Calls = append(s.Calls, sleep)
}

func (s *SpyCountdownOperations) Write(p []byte) (n int, err error) {
	s.Calls = append(s.Calls, write)
	return
}

func TestCountdownRuns(t *testing.T) {

	t.Run("Prints 3 to Go!", func(t *testing.T) {
		count := 3
		buffer := &bytes.Buffer{}
		sleeper := &SpyCountdownOperations{}
		Countdown(buffer, sleeper, count)

		got := buffer.String()
		// ` backtick allows strings with preserved newlines.
		want := `3
2
1
Go!
`

		if got != want {
			t.Errorf("got %q want %q", got, want)
		}

		if len(sleeper.Calls) != 3 {
			t.Errorf("Not enough calls to sleeper, want 3 got %q", sleeper.Calls)
		}

	})

	t.Run("Prints 4 to Go!", func(t *testing.T) {
		count := 4
		buffer := &bytes.Buffer{}
		sleeper := &SpyCountdownOperations{}
		Countdown(buffer, sleeper, count)

		got := buffer.String()
		// ` backtick allows strings with preserved newlines.
		want := `4
3
2
1
Go!
`

		if got != want {
			t.Errorf("got %q want %q", got, want)
		}

		if len(sleeper.Calls) != 4 {
			t.Errorf("Not enough calls to sleeper, want 3 got %q", sleeper.Calls)
		}

	})

	t.Run("sleep before every print", func(t *testing.T) {
		count := 3
		sleeper := &SpyCountdownOperations{}
		Countdown(sleeper, sleeper, count)

		want := []string{
			write,
			sleep,
			write,
			sleep,
			write,
			sleep,
			write,
		}

		if !reflect.DeepEqual(want, sleeper.Calls) {
			t.Errorf("wanted calls %v got %v", want, sleeper.Calls)

		}

	})

}

type SpyTimeSleeper struct {
	durationSlept time.Duration
}

func (s *SpyTimeSleeper) Sleep(duration time.Duration) {
	s.durationSlept = duration
}

func TestConfigurableSleeper(t *testing.T) {
	sleepTime := 5 * time.Second
	spyTime := &SpyTimeSleeper{}
	sleeper := ConfigurableSleeper{sleepTime, spyTime.Sleep}
	sleeper.Sleep()

	if spyTime.durationSlept != sleepTime {
		t.Errorf("should have slept for %v  but slept for %v", spyTime.durationSlept, sleepTime)
	}
}