package historycmd

import (
	"fmt"
	"sync"
	"time"

	"code.justin.tv/foundation/history.v2/historyin"
	"github.com/sirupsen/logrus"
)

type batcherGroup struct {
	Batches int
	History *historyin.Client
	Logger  logrus.FieldLogger

	nRequest int
	lock     sync.Mutex
	batchers []historyin.Batcher
	initSync sync.Once
}

func (bg *batcherGroup) init() (err error) {
	bg.initSync.Do(func() {
		for i := 0; i < bg.Batches; i++ {
			var batcher historyin.Batcher
			if batcher, err = bg.History.Batcher(); err != nil {
				return
			}
			bg.batchers = append(bg.batchers, batcher)
		}
	})

	return
}

func (bg *batcherGroup) Add(audit *historyin.Audit) error {
	if err := bg.init(); err != nil {
		return err
	}

	bg.lock.Lock()
	nRequest := (bg.nRequest + 1) % len(bg.batchers)
	bg.nRequest = nRequest
	bg.lock.Unlock()

	batcher := bg.batchers[nRequest]
	waiting := bg.logExecutionCount("waiting for batch", 10)
	for batcher.CurrentBatchSize() > 10000 {
		waiting()
		time.Sleep(100 * time.Millisecond)
	}
	return batcher.Add(audit)
}

func (bg *batcherGroup) Start() {
	if err := bg.init(); err != nil {
		bg.Logger.Error(err)
		return
	}

	for nBatcher, batcher := range bg.batchers {
		bg.Logger.Infof("starting batcher %d", nBatcher)
		go batcher.Run()
	}
}

func (bg *batcherGroup) Wait() {
	if err := bg.init(); err != nil {
		bg.Logger.Error(err)
		return
	}

	for _, batcher := range bg.batchers {
		batcher.Drain()
	}

	for nBatcher, batcher := range bg.batchers {
		for batcher.CurrentBatchSize() > 0 {
			bg.Logger.Infof("waiting for batcher %d to drain, size is %d",
				nBatcher, batcher.CurrentBatchSize())
			time.Sleep(time.Second)
		}
	}

	for nBatcher, batcher := range bg.batchers {
		stopping := bg.logExecutionCount(
			fmt.Sprintf("stopping batcher %d", nBatcher), 1)
		for !batcher.Stop(time.Second) {
			stopping()
		}
	}
}

func (bg *batcherGroup) logExecutionCount(action string, every int) func() {
	var (
		nCount int
		lock   sync.Mutex
	)

	return func() {
		lock.Lock()
		nCount++
		if (nCount % every) == 0 {
			bg.Logger.Infof("%s: %d", action, nCount)
		}
		lock.Unlock()
	}
}
