package auth

import (
	"time"

	"github.com/lestrrat-go/jwx/v3/jwt"
)

// Claims represents the typed JWT claims used throughout Navidrome,
// replacing the untyped map[string]any approach.
type Claims struct {
	// Standard JWT claims
	Issuer    string
	Subject   string // username for session tokens
	IssuedAt  time.Time
	ExpiresAt time.Time

	// Custom claims
	UserID  string // "uid"
	IsAdmin bool   // "adm"
	ID      string // "id" - artwork/mediafile ID
	Format  string // "f" - audio format
	BitRate int    // "b" - audio bitrate
	ShareID string // "sid" - share ID for share stream tokens
}

// ToMap converts Claims to a map[string]any for use with TokenAuth.Encode().
// Only non-zero fields are included.
func (c Claims) ToMap() map[string]any {
	m := make(map[string]any)
	if c.Issuer != "" {
		m[jwt.IssuerKey] = c.Issuer
	}
	if c.Subject != "" {
		m[jwt.SubjectKey] = c.Subject
	}
	if !c.IssuedAt.IsZero() {
		m[jwt.IssuedAtKey] = c.IssuedAt.UTC().Unix()
	}
	if !c.ExpiresAt.IsZero() {
		m[jwt.ExpirationKey] = c.ExpiresAt.UTC().Unix()
	}
	if c.UserID != "" {
		m["uid"] = c.UserID
	}
	if c.IsAdmin {
		m["adm"] = c.IsAdmin
	}
	if c.ID != "" {
		m["id"] = c.ID
	}
	if c.Format != "" {
		m["f"] = c.Format
	}
	if c.BitRate != 0 {
		m["b"] = c.BitRate
	}
	if c.ShareID != "" {
		m["sid"] = c.ShareID
	}
	return m
}

func (c Claims) WithExpiresAt(t time.Time) Claims {
	c.ExpiresAt = t
	return c
}

// ClaimsFromToken extracts Claims directly from a jwt.Token using token.Get().
func ClaimsFromToken(token jwt.Token) Claims {
	var c Claims
	c.Issuer, _ = token.Issuer()
	c.Subject, _ = token.Subject()
	c.IssuedAt, _ = token.IssuedAt()
	c.ExpiresAt, _ = token.Expiration()

	var uid string
	if err := token.Get("uid", &uid); err == nil {
		c.UserID = uid
	}
	var adm bool
	if err := token.Get("adm", &adm); err == nil {
		c.IsAdmin = adm
	}
	var id string
	if err := token.Get("id", &id); err == nil {
		c.ID = id
	}
	var f string
	if err := token.Get("f", &f); err == nil {
		c.Format = f
	}
	if err := token.Get("b", &c.BitRate); err != nil {
		var bf float64
		if err := token.Get("b", &bf); err == nil {
			c.BitRate = int(bf)
		}
	}
	var sid string
	if err := token.Get("sid", &sid); err == nil {
		c.ShareID = sid
	}
	return c
}
