package api

import (
	"strconv"
	"sync"
	"time"
)

// rememberedTitles bounds the title memory below. A household plays a handful of things
// at once; this is generous enough that a report always finds its title and small enough
// that a hostile client cannot grow it into a leak.
const rememberedTitles = 256

// playbackTitles remembers what each item was called when the player asked to play it.
//
// Progress and stop reports carry an item id and nothing else — the client already knows
// the title and has no reason to send it back — so without this every playback event
// after the first reads as an opaque GUID. Deliberately in memory and deliberately lossy:
// a restarted gateway logs an id until the next play, which is a better trade than a
// round trip to Emby on a report the viewer never sees.
type playbackTitles struct {
	mu     sync.Mutex
	titles map[string]string
	order  []string
}

func (p *playbackTitles) remember(itemID, title string) {
	if itemID == "" || title == "" {
		return
	}
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.titles == nil {
		p.titles = make(map[string]string, rememberedTitles)
	}
	if _, known := p.titles[itemID]; !known {
		p.order = append(p.order, itemID)
		if len(p.order) > rememberedTitles {
			delete(p.titles, p.order[0])
			p.order = p.order[1:]
		}
	}
	p.titles[itemID] = title
}

// name falls back to the id rather than an empty field: a line that says which item it
// could not name is still usable.
func (p *playbackTitles) name(itemID string) string {
	p.mu.Lock()
	defer p.mu.Unlock()
	if title, known := p.titles[itemID]; known {
		return title
	}
	return itemID
}

// millisecondDuration renders a playhead as "1h42m30s" rather than a seven-digit
// millisecond count, which no one can read at a glance.
func millisecondDuration(milliseconds int64) time.Duration {
	return (time.Duration(max64(milliseconds, 0)) * time.Millisecond).Round(time.Second)
}

// watchedPercent is how a stop is read at a glance: "did they finish it, or give up?"
func watchedPercent(positionMs, durationMs int64) string {
	if durationMs <= 0 {
		return "unknown"
	}
	percent := max64(positionMs, 0) * 100 / durationMs
	return strconv.FormatInt(min64(percent, 100), 10) + "%"
}

func min64(v, ceiling int64) int64 {
	if v > ceiling {
		return ceiling
	}
	return v
}
