package subsonic

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

	"github.com/kennygrant/sanitize"
	"github.com/navidrome/navidrome/log"
	"github.com/navidrome/navidrome/model"
	"github.com/navidrome/navidrome/server/subsonic/responses"
	"github.com/navidrome/navidrome/utils"
)

type SearchingController struct {
	ds model.DataStore
}

type searchParams struct {
	query        string
	artistCount  int
	artistOffset int
	albumCount   int
	albumOffset  int
	songCount    int
	songOffset   int
}

func NewSearchingController(ds model.DataStore) *SearchingController {
	return &SearchingController{ds: ds}
}

func (c *SearchingController) getParams(r *http.Request) (*searchParams, error) {
	var err error
	sp := &searchParams{}
	sp.query, err = requiredParamString(r, "query")
	if err != nil {
		return nil, err
	}
	sp.artistCount = utils.ParamInt(r, "artistCount", 20)
	sp.artistOffset = utils.ParamInt(r, "artistOffset", 0)
	sp.albumCount = utils.ParamInt(r, "albumCount", 20)
	sp.albumOffset = utils.ParamInt(r, "albumOffset", 0)
	sp.songCount = utils.ParamInt(r, "songCount", 20)
	sp.songOffset = utils.ParamInt(r, "songOffset", 0)
	return sp, nil
}

func (c *SearchingController) searchAll(r *http.Request, sp *searchParams) (model.MediaFiles, model.Albums, model.Artists) {
	q := sanitize.Accents(strings.ToLower(strings.TrimSuffix(sp.query, "*")))
	ctx := r.Context()

	artists, err := c.ds.Artist(ctx).Search(q, sp.artistOffset, sp.artistCount)
	if err != nil {
		log.Error(ctx, "Error searching for Artists", err)
	}
	albums, err := c.ds.Album(ctx).Search(q, sp.albumOffset, sp.albumCount)
	if err != nil {
		log.Error(ctx, "Error searching for Albums", err)
	}
	mediaFiles, err := c.ds.MediaFile(ctx).Search(q, sp.songOffset, sp.songCount)
	if err != nil {
		log.Error(ctx, "Error searching for MediaFiles", err)
	}

	log.Debug(ctx, fmt.Sprintf("Search resulted in %d songs, %d albums and %d artists",
		len(mediaFiles), len(albums), len(artists)), "query", sp.query)
	return mediaFiles, albums, artists
}

func (c *SearchingController) Search2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	sp, err := c.getParams(r)
	if err != nil {
		return nil, err
	}
	mfs, als, as := c.searchAll(r, sp)

	response := newResponse()
	searchResult2 := &responses.SearchResult2{}
	searchResult2.Artist = make([]responses.Artist, len(as))
	for i, artist := range as {
		searchResult2.Artist[i] = responses.Artist{
			Id:         artist.ID,
			Name:       artist.Name,
			AlbumCount: artist.AlbumCount,
			UserRating: artist.Rating,
		}
		if artist.Starred {
			searchResult2.Artist[i].Starred = &artist.StarredAt
		}
	}
	searchResult2.Album = childrenFromAlbums(r.Context(), als)
	searchResult2.Song = childrenFromMediaFiles(r.Context(), mfs)
	response.SearchResult2 = searchResult2
	return response, nil
}

func (c *SearchingController) Search3(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	ctx := r.Context()
	sp, err := c.getParams(r)
	if err != nil {
		return nil, err
	}
	mfs, als, as := c.searchAll(r, sp)

	response := newResponse()
	searchResult3 := &responses.SearchResult3{}
	searchResult3.Artist = make([]responses.ArtistID3, len(as))
	for i, artist := range as {
		searchResult3.Artist[i] = toArtistID3(ctx, artist)
	}
	searchResult3.Album = childrenFromAlbums(ctx, als)
	searchResult3.Song = childrenFromMediaFiles(ctx, mfs)
	response.SearchResult3 = searchResult3
	return response, nil
}
