package main

import (
	"crypto/sha256"
	"fmt"
	"strings"
	"sync"
	"time"
)

type timerType uint8

const (
	timerTypePermit timerType = iota
	timerTypeCooldown
)

var timerStore = newTimer()

type timerEntry struct {
	kind timerType
	time time.Time
}

type timer struct {
	timers map[string]timerEntry
	lock   *sync.RWMutex
}

func newTimer() *timer {
	return &timer{
		timers: map[string]timerEntry{},
		lock:   new(sync.RWMutex),
	}
}

// Cooldown timer

func (t *timer) AddCooldown(ruleID string) {
	t.add(timerTypeCooldown, t.getCooldownTimerKey(ruleID))
}

func (t *timer) InCooldown(ruleID string, cooldown time.Duration) bool {
	return t.has(t.getCooldownTimerKey(ruleID), cooldown)
}

func (t timer) getCooldownTimerKey(ruleID string) string {
	h := sha256.New()
	fmt.Fprintf(h, "%d:%s", timerTypeCooldown, ruleID)
	return fmt.Sprintf("sha256:%x", h.Sum(nil))
}

// Permit timer

func (t *timer) AddPermit(channel, username string) {
	t.add(timerTypePermit, t.getPermitTimerKey(channel, username))
}

func (t *timer) HasPermit(channel, username string) bool {
	return t.has(t.getPermitTimerKey(channel, username), config.PermitTimeout)
}

func (t timer) getPermitTimerKey(channel, username string) string {
	h := sha256.New()
	fmt.Fprintf(h, "%d:%s:%s", timerTypePermit, channel, strings.ToLower(strings.TrimLeft(username, "@")))
	return fmt.Sprintf("sha256:%x", h.Sum(nil))
}

// Generic

func (t *timer) add(kind timerType, id string) {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.timers[id] = timerEntry{kind: kind, time: time.Now()}
}

func (t *timer) has(id string, validity time.Duration) bool {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return time.Since(t.timers[id].time) < validity
}