package subsonic

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/deluan/navidrome/engine"
	"github.com/deluan/navidrome/log"
	"github.com/deluan/navidrome/model"
	"github.com/deluan/navidrome/server/subsonic/responses"
	"github.com/deluan/navidrome/utils"
)

type PlaylistsController struct {
	pls engine.Playlists
}

func NewPlaylistsController(pls engine.Playlists) *PlaylistsController {
	return &PlaylistsController{pls: pls}
}

func (c *PlaylistsController) GetPlaylists(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	allPls, err := c.pls.GetAll(r.Context())
	if err != nil {
		log.Error(r, err)
		return nil, NewError(responses.ErrorGeneric, "Internal error")
	}
	playlists := make([]responses.Playlist, len(allPls))
	for i, p := range allPls {
		playlists[i].Id = p.ID
		playlists[i].Name = p.Name
		playlists[i].Comment = p.Comment
		playlists[i].SongCount = p.SongCount
		playlists[i].Duration = int(p.Duration)
		playlists[i].Owner = p.Owner
		playlists[i].Public = p.Public
		playlists[i].Created = p.CreatedAt
		playlists[i].Changed = p.UpdatedAt
	}
	response := NewResponse()
	response.Playlists = &responses.Playlists{Playlist: playlists}
	return response, nil
}

func (c *PlaylistsController) GetPlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	id, err := RequiredParamString(r, "id", "id parameter required")
	if err != nil {
		return nil, err
	}
	pinfo, err := c.pls.Get(r.Context(), id)
	switch {
	case err == model.ErrNotFound:
		log.Error(r, err.Error(), "id", id)
		return nil, NewError(responses.ErrorDataNotFound, "Directory not found")
	case err != nil:
		log.Error(r, err)
		return nil, NewError(responses.ErrorGeneric, "Internal Error")
	}

	response := NewResponse()
	response.Playlist = c.buildPlaylistWithSongs(r.Context(), pinfo)
	return response, nil
}

func (c *PlaylistsController) CreatePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	songIds := utils.ParamStrings(r, "songId")
	playlistId := utils.ParamString(r, "playlistId")
	name := utils.ParamString(r, "name")
	if playlistId == "" && name == "" {
		return nil, errors.New("Required parameter name is missing")
	}
	err := c.pls.Create(r.Context(), playlistId, name, songIds)
	if err != nil {
		log.Error(r, err)
		return nil, NewError(responses.ErrorGeneric, "Internal Error")
	}
	return NewResponse(), nil
}

func (c *PlaylistsController) DeletePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	id, err := RequiredParamString(r, "id", "Required parameter id is missing")
	if err != nil {
		return nil, err
	}
	err = c.pls.Delete(r.Context(), id)
	if err == model.ErrNotAuthorized {
		return nil, NewError(responses.ErrorAuthorizationFail)
	}
	if err != nil {
		log.Error(r, err)
		return nil, NewError(responses.ErrorGeneric, "Internal Error")
	}
	return NewResponse(), nil
}

func (c *PlaylistsController) UpdatePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
	playlistId, err := RequiredParamString(r, "playlistId", "Required parameter playlistId is missing")
	if err != nil {
		return nil, err
	}
	songsToAdd := utils.ParamStrings(r, "songIdToAdd")
	songIndexesToRemove := utils.ParamInts(r, "songIndexToRemove")

	var pname *string
	if len(r.URL.Query()["name"]) > 0 {
		s := r.URL.Query()["name"][0]
		pname = &s
	}

	log.Debug(r, "Updating playlist", "id", playlistId)
	if pname != nil {
		log.Trace(r, fmt.Sprintf("-- New Name: '%s'", *pname))
	}
	log.Trace(r, fmt.Sprintf("-- Adding: '%v'", songsToAdd))
	log.Trace(r, fmt.Sprintf("-- Removing: '%v'", songIndexesToRemove))

	err = c.pls.Update(r.Context(), playlistId, pname, songsToAdd, songIndexesToRemove)
	if err == model.ErrNotAuthorized {
		return nil, NewError(responses.ErrorAuthorizationFail)
	}
	if err != nil {
		log.Error(r, err)
		return nil, NewError(responses.ErrorGeneric, "Internal Error")
	}
	return NewResponse(), nil
}

func (c *PlaylistsController) buildPlaylistWithSongs(ctx context.Context, d *engine.PlaylistInfo) *responses.PlaylistWithSongs {
	pls := &responses.PlaylistWithSongs{
		Playlist: *c.buildPlaylist(d),
	}
	pls.Entry = ToChildren(ctx, d.Entries)
	return pls
}

func (c *PlaylistsController) buildPlaylist(d *engine.PlaylistInfo) *responses.Playlist {
	pls := &responses.Playlist{}
	pls.Id = d.Id
	pls.Name = d.Name
	pls.Comment = d.Comment
	pls.SongCount = d.SongCount
	pls.Owner = d.Owner
	pls.Duration = d.Duration
	pls.Public = d.Public
	pls.Created = d.Created
	pls.Changed = d.Changed
	return pls
}
