package agents

import (
	"context"
	"fmt"
	"net/http"
	"sort"
	"strings"

	"github.com/navidrome/navidrome/conf"
	"github.com/navidrome/navidrome/consts"
	"github.com/navidrome/navidrome/log"
	"github.com/navidrome/navidrome/model"
	"github.com/navidrome/navidrome/utils/spotify"
	"github.com/xrash/smetrics"
)

const spotifyAgentName = "spotify"

type spotifyAgent struct {
	ctx    context.Context
	id     string
	secret string
	client *spotify.Client
}

func spotifyConstructor(ctx context.Context) Interface {
	l := &spotifyAgent{
		ctx:    ctx,
		id:     conf.Server.Spotify.ID,
		secret: conf.Server.Spotify.Secret,
	}
	hc := NewCachedHTTPClient(http.DefaultClient, consts.DefaultCachedHttpClientTTL)
	l.client = spotify.NewClient(l.id, l.secret, hc)
	return l
}

func (s *spotifyAgent) AgentName() string {
	return spotifyAgentName
}

func (s *spotifyAgent) GetImages(id, name, mbid string) ([]ArtistImage, error) {
	a, err := s.searchArtist(name)
	if err != nil {
		if err == model.ErrNotFound {
			log.Warn(s.ctx, "Artist not found in Spotify", "artist", name)
		} else {
			log.Error(s.ctx, "Error calling Spotify", "artist", name, err)
		}
		return nil, err
	}

	var res []ArtistImage
	for _, img := range a.Images {
		res = append(res, ArtistImage{
			URL:  img.URL,
			Size: img.Width,
		})
	}
	return res, nil
}

func (s *spotifyAgent) searchArtist(name string) (*spotify.Artist, error) {
	artists, err := s.client.SearchArtists(s.ctx, name, 40)
	if err != nil || len(artists) == 0 {
		return nil, model.ErrNotFound
	}
	name = strings.ToLower(name)

	// Sort results, prioritizing artists with images, with similar names and with high popularity, in this order
	sort.Slice(artists, func(i, j int) bool {
		ai := fmt.Sprintf("%-5t-%03d-%04d", len(artists[i].Images) == 0, smetrics.WagnerFischer(name, strings.ToLower(artists[i].Name), 1, 1, 2), 1000-artists[i].Popularity)
		aj := fmt.Sprintf("%-5t-%03d-%04d", len(artists[j].Images) == 0, smetrics.WagnerFischer(name, strings.ToLower(artists[j].Name), 1, 1, 2), 1000-artists[j].Popularity)
		return ai < aj
	})

	// If the first one has the same name, that's the one
	if strings.ToLower(artists[0].Name) != name {
		return nil, model.ErrNotFound
	}
	return &artists[0], err
}

func init() {
	conf.AddHook(func() {
		if conf.Server.Spotify.ID != "" && conf.Server.Spotify.Secret != "" {
			Register(spotifyAgentName, spotifyConstructor)
		}
	})
}
