package document_test

import (
	"testing"

	"github.com/rs/zerolog"
)

func TestSoak(t *testing.T) {
	if soak == 0 {
		t.Skip("-soak flag not provided or 0, so skipping.")
	}

	setGlobalLogLevel(zerolog.TraceLevel)
	soaker := &soaker{
		documentTester: newDocumentTester(t, seed, plainManagerSpawnFun),
	}
	soaker.documentName = soaker.randomWord(false)
	defer soaker.close()

	soaker.run()
}

type soaker struct {
	*documentTester

	documentName string

	mutator  *mutatingClient
	listener *listeningClient
}

func (self *soaker) run() {
	self.boot()
	for event := uint(0); event < soak; event++ {
		log := self.log.With().Uint("event", event).Logger()
		self.performRandomAction(log)
	}
}

func (self *soaker) boot() {
	self.log.Trace().Msg("booting")
	self.is.True(self.mutator == nil)
	self.is.True(self.listener == nil)

	self.spawnDocument(self.documentName)
	self.mutator = self.newMutatingClientWithEmptyRoot()
	self.listener = self.newListeningClient()
	self.validateExpected()

}

func (self *soaker) performRandomAction(log zerolog.Logger) {
	n := self.rng.Intn(10)
	switch true {
	case n == 0:
		log.Trace().Msg("restarting document")

		self.closeDocument()

		self.spawnDocument(self.documentName)
		self.mutator.documentClient = self.documentClientFactory.NewClient()
		self.listener = self.newListeningClient()
		self.validateExpected()
		return

	case n <= 2 && self.mutator.canUndo():
		log.Trace().Msg("undo")
		self.mutator.sendUndo()
		self.validateExpected()
		return

	case n <= 4 && self.mutator.canRedo():
		log.Trace().Msg("redo")
		self.mutator.sendRedo()
		self.validateExpected()
		return

	default:
		log.Trace().Msg("mutating words")
		for mutations := self.rng.Intn(5); mutations >= 0; mutations-- {
			action := self.rng.Intn(5)
			switch {
			case action == 0:
				self.mutator.unsentGeneration.addWord()
			case action == 1 && len(self.mutator.unsentGeneration.orderedWords) > 1:
				self.mutator.unsentGeneration.deleteWord()
			default:
				self.mutator.unsentGeneration.editExistingWord()
			}
		}
		self.mutator.unsentGeneration.send()
		self.validateExpected()
		return
	}
}

func (self *soaker) validateExpected() {
	rendering, ok := self.listener.nextDocumentRendering()
	self.is.True(ok)
	expected := self.mutator.unsentGeneration.previous.String()
	self.is.Equal(expected, rendering)
}
