package api

import (
	"sort"
	"sync"

	"code.secondbit.org/uuid.hg"
)

type UsageTracker interface {
	TrackUpload(id uuid.ID, bytes int64) error
	TrackDownload(id uuid.ID, bytes int64) error
	GetUsage(id uuid.ID) (Usage, error)
	GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error)
}

type Usage struct {
	UserID           uuid.ID
	UploadedBytes    int64
	DownloadedBytes  int64
	UploadRequests   int64
	DownloadRequests int64
}

func (u Usage) score() float64 {
	storageCost := .026 * float64(u.UploadedBytes) / 1073741824
	bandwidthCost := .012 * float64(u.DownloadedBytes) / 1073741824
	uploadReqCost := .02 * float64(u.UploadRequests) / 1000
	downloadReqCost := .01 * float64(u.DownloadRequests) / 10000
	return storageCost + bandwidthCost + uploadReqCost + downloadReqCost
}

type topUsages []Usage

func (t topUsages) Less(i, j int) bool {
	return t[i].score() > t[j].score()
}

func (t topUsages) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

func (t topUsages) Len() int {
	return len(t)
}

type MemTracker struct {
	usages map[string]Usage
	sync.RWMutex
}

func NewMemTracker() *MemTracker {
	return &MemTracker{
		usages: map[string]Usage{},
	}
}

func (m *MemTracker) TrackUpload(id uuid.ID, bytes int64) error {
	m.Lock()
	defer m.Unlock()

	use := m.usages[id.String()]

	use.UploadedBytes = use.UploadedBytes + bytes
	use.UploadRequests = use.UploadRequests + 1
	m.usages[id.String()] = use
	return nil
}

func (m *MemTracker) TrackDownload(id uuid.ID, bytes int64) error {
	m.Lock()
	defer m.Unlock()

	use := m.usages[id.String()]

	use.DownloadedBytes = use.DownloadedBytes + bytes
	use.DownloadRequests = use.DownloadRequests + 1
	m.usages[id.String()] = use
	return nil
}

func (m *MemTracker) GetUsage(id uuid.ID) (Usage, error) {
	m.RLock()
	defer m.RUnlock()

	return m.usages[id.String()], nil
}

func (m *MemTracker) GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) {
	m.RLock()
	defer m.RUnlock()

	usages := []Usage{}
	for _, usage := range m.usages {
		usages = append(usages, usage)
	}
	sortedUsages := topUsages(usages)
	sort.Sort(sortedUsages)
	usages = []Usage(sortedUsages)

	if int64(len(usages)) <= offset {
		return []Usage{}, nil
	}
	if int64(len(usages)) > offset+num {
		return usages[offset : offset+num], nil
	} else {
		return usages[offset:], nil
	}
}
