package settlement

import (
	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	"go.uber.org/zap"
)

// HandleAuditEvent catches a new AuditEvent and
// adds AuditProcessor call to the execution queue.
func (p *Processor) HandleAuditEvent(e event.Event) {
	ev := e.(AuditEvent)

	epoch := ev.Epoch()

	if !p.state.IsAlphabet() {
		p.log.Info(logs.SettlementNonAlphabetModeIgnoreAuditPayments)

		return
	}

	log := &logger.Logger{Logger: p.log.With(
		zap.Uint64("epoch", epoch),
	)}

	log.Info(logs.SettlementNewAuditSettlementEvent)

	if epoch == 0 {
		log.Debug(logs.SettlementIgnoreGenesisEpoch)
		return
	}

	handler := &auditEventHandler{
		log:   log,
		epoch: epoch,
		proc:  p.auditProc,
	}

	err := p.pool.Submit(handler.handle)
	if err != nil {
		log.Warn(logs.SettlementCouldNotAddHandlerOfAuditEventToQueue,
			zap.String("error", err.Error()),
		)

		return
	}

	log.Debug(logs.SettlementAuditEventHandlingSuccessfullyScheduled)
}

func (p *Processor) HandleIncomeCollectionEvent(e event.Event) {
	ev := e.(BasicIncomeCollectEvent)
	epoch := ev.Epoch()

	if !p.state.IsAlphabet() {
		p.log.Info(logs.SettlementNonAlphabetModeIgnoreIncomeCollectionEvent)

		return
	}

	p.log.Info(logs.SettlementStartBasicIncomeCollection,
		zap.Uint64("epoch", epoch))

	p.contextMu.Lock()
	defer p.contextMu.Unlock()

	if _, ok := p.incomeContexts[epoch]; ok {
		p.log.Error(logs.SettlementIncomeContextAlreadyExists,
			zap.Uint64("epoch", epoch))

		return
	}

	incomeCtx, err := p.basicIncome.CreateContext(epoch)
	if err != nil {
		p.log.Error(logs.SettlementCantCreateIncomeContext,
			zap.String("error", err.Error()))

		return
	}

	p.incomeContexts[epoch] = incomeCtx

	err = p.pool.Submit(func() {
		incomeCtx.Collect()
	})
	if err != nil {
		p.log.Warn(logs.SettlementCouldNotAddHandlerOfBasicIncomeCollectionToQueue,
			zap.String("error", err.Error()),
		)

		return
	}
}

func (p *Processor) HandleIncomeDistributionEvent(e event.Event) {
	ev := e.(BasicIncomeDistributeEvent)
	epoch := ev.Epoch()

	if !p.state.IsAlphabet() {
		p.log.Info(logs.SettlementNonAlphabetModeIgnoreIncomeDistributionEvent)

		return
	}

	p.log.Info(logs.SettlementStartBasicIncomeDistribution,
		zap.Uint64("epoch", epoch))

	p.contextMu.Lock()
	defer p.contextMu.Unlock()

	incomeCtx, ok := p.incomeContexts[epoch]
	delete(p.incomeContexts, epoch)

	if !ok {
		p.log.Warn(logs.SettlementIncomeContextDistributionDoesNotExists,
			zap.Uint64("epoch", epoch))

		return
	}

	err := p.pool.Submit(func() {
		incomeCtx.Distribute()
	})
	if err != nil {
		p.log.Warn(logs.SettlementCouldNotAddHandlerOfBasicIncomeDistributionToQueue,
			zap.String("error", err.Error()),
		)

		return
	}
}