package ali

import (
	"fmt"
	"sync"
	"time"
)

type RateLimiter interface {
	CheckRecord(number string, t time.Time) bool
	InsertRecord(number string, t time.Time) error
	DisplayStatus() string
}

type historyRecord struct {
	max     int
	min     int
	records [5]time.Time
}

type rateLimiter struct {
	mu        sync.RWMutex
	recordMap map[string]*historyRecord
}

func (r *rateLimiter) CheckRecord(number string, t time.Time) bool {
	r.mu.RLock()
	defer r.mu.RUnlock()
	hRecord, isFound := r.recordMap[number]
	// If not found in record map, then the number is not limited by rate limter
	if !isFound {
		return true
	}

	if t.Add(-time.Minute).After(hRecord.records[hRecord.max]) && t.Add(-time.Hour).After(hRecord.records[hRecord.min]) {
		return true
	}

	return false
}

func (r *rateLimiter) InsertRecord(number string, t time.Time) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	hRecord, isFound := r.recordMap[number]
	if isFound {
		if hRecord.records[hRecord.max].After(t) {
			return fmt.Errorf("shouldn't accept an old record")
		}

		hRecord.records[hRecord.min] = t
		hRecord.max = hRecord.min
		for i := range hRecord.records {
			if hRecord.records[hRecord.min].After(hRecord.records[i]) {
				hRecord.min = i
			}
		}

	} else {
		newRecord := &historyRecord{}
		newRecord.records[0] = t
		newRecord.max = 0
		newRecord.min = 1
		r.recordMap[number] = newRecord
	}

	return nil
}

func (r *rateLimiter) DisplayStatus() string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	s := ""
	for k, v := range r.recordMap {
		s += fmt.Sprintf("number: %s, max: %d, min: %d\n", k, v.max, v.min)
		s += fmt.Sprintln("items:")
		for _, item := range v.records {
			if !item.IsZero() {
				s += fmt.Sprintf("- %s\n", item.String())
			}
		}
	}
	s += "\n"
	return s
}

func NewRateLimiter() RateLimiter {
	return &rateLimiter{
		recordMap: make(map[string]*historyRecord),
	}
}
