package alerts

import (
	"math/rand"
	"time"

	"github.com/nicklaw5/helix/v2"

	"git.sr.ht/~ashkeel/strimertul/twitch/chat"
)

type subMixedEvent struct {
	UserID               string
	UserLogin            string
	UserName             string
	BroadcasterUserID    string
	BroadcasterUserLogin string
	BroadcasterUserName  string
	Tier                 string
	IsGift               bool
	CumulativeMonths     int
	StreakMonths         int
	DurationMonths       int
	Message              helix.EventSubMessage
}

// Subscriptions are handled with a slight delay as info come from different events and must be aggregated
func (m *Module) addMixedEvent(event any) {
	switch sub := event.(type) {
	case helix.EventSubChannelSubscribeEvent:
		m.pendingMux.Lock()
		defer m.pendingMux.Unlock()
		if ev, ok := m.pendingSubs[sub.UserID]; ok {
			// Already pending, add extra data
			ev.IsGift = sub.IsGift
			m.pendingSubs[sub.UserID] = ev
			return
		}
		m.pendingSubs[sub.UserID] = subMixedEvent{
			UserID:               sub.UserID,
			UserLogin:            sub.UserLogin,
			UserName:             sub.UserName,
			BroadcasterUserID:    sub.BroadcasterUserID,
			BroadcasterUserLogin: sub.BroadcasterUserLogin,
			BroadcasterUserName:  sub.BroadcasterUserName,
			Tier:                 sub.Tier,
			IsGift:               sub.IsGift,
		}
		go func() {
			// Wait a bit to make sure we aggregate all events
			time.Sleep(time.Second * 3)
			m.processPendingSub(sub.UserID)
		}()
	case helix.EventSubChannelSubscriptionMessageEvent:
		m.pendingMux.Lock()
		defer m.pendingMux.Unlock()
		if ev, ok := m.pendingSubs[sub.UserID]; ok {
			// Already pending, add extra data
			ev.StreakMonths = sub.StreakMonths
			ev.DurationMonths = sub.DurationMonths
			ev.CumulativeMonths = sub.CumulativeMonths
			ev.Message = sub.Message
			return
		}
		m.pendingSubs[sub.UserID] = subMixedEvent{
			UserID:               sub.UserID,
			UserLogin:            sub.UserLogin,
			UserName:             sub.UserName,
			BroadcasterUserID:    sub.BroadcasterUserID,
			BroadcasterUserLogin: sub.BroadcasterUserLogin,
			BroadcasterUserName:  sub.BroadcasterUserName,
			Tier:                 sub.Tier,
			StreakMonths:         sub.StreakMonths,
			DurationMonths:       sub.DurationMonths,
			CumulativeMonths:     sub.CumulativeMonths,
			Message:              sub.Message,
		}
		go func() {
			// Wait a bit to make sure we aggregate all events
			time.Sleep(time.Second * 3)
			m.processPendingSub(sub.UserID)
		}()
	}
}

func (m *Module) processPendingSub(user string) {
	m.pendingMux.Lock()
	defer m.pendingMux.Unlock()
	sub, ok := m.pendingSubs[user]
	defer delete(m.pendingSubs, user)
	if !ok {
		// Somehow it's gone? Return early
		return
	}

	// One last check in case config changed
	if !m.Config.Subscription.Enabled {
		return
	}

	// Assign random message
	messageID := rand.Intn(len(m.Config.Subscription.Messages))
	tpl, ok := m.templates[templateTypeSubscription][m.Config.Subscription.Messages[messageID]]

	// If template is broken, write it as is (soft fail, plus we raise attention I guess?)
	if !ok {
		// Broken template!
		chat.WriteMessage(m.db, m.logger, chat.WriteMessageRequest{
			Message:  m.Config.Subscription.Messages[messageID],
			Announce: m.Config.Subscription.Announce,
		})
		return
	}

	// Check for variations, either by streak or gifted
	if sub.IsGift {
		variation := getBestValidVariation(m.Config.Subscription.Variations, func(variation subscriptionVariation) int {
			if variation.IsGifted != nil && *variation.IsGifted {
				return 1
			}
			return 0
		})
		tpl = m.replaceWithVariation(tpl, templateTypeSubscription, variation.Messages)
	} else if sub.DurationMonths > 0 {
		// Get variation with the highest minimum streak that's met
		variation := getBestValidVariation(m.Config.Subscription.Variations, func(variation subscriptionVariation) int {
			if variation.MinStreak != nil && sub.DurationMonths >= *variation.MinStreak {
				return sub.DurationMonths
			}
			return 0
		})
		tpl = m.replaceWithVariation(tpl, templateTypeSubscription, variation.Messages)
	}
	m.writeTemplate(tpl, sub, m.Config.Subscription.Announce)
}
