package subsonic

import (
	"bytes"
	"context"
	"errors"
	"io"
	"net/http/httptest"
	"time"

	"github.com/navidrome/navidrome/core/artwork"
	"github.com/navidrome/navidrome/log"
	"github.com/navidrome/navidrome/model"
	"github.com/navidrome/navidrome/tests"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("MediaRetrievalController", func() {
	var router *Router
	var ds model.DataStore
	mockRepo := &mockedMediaFile{}
	var artwork *fakeArtwork
	var w *httptest.ResponseRecorder

	BeforeEach(func() {
		ds = &tests.MockDataStore{
			MockedMediaFile: mockRepo,
		}
		artwork = &fakeArtwork{}
		router = New(ds, artwork, nil, nil, nil, nil, nil, nil, nil, nil, nil)
		w = httptest.NewRecorder()
	})

	Describe("GetCoverArt", func() {
		It("should return data for that id", func() {
			artwork.data = "image data"
			r := newGetRequest("id=34", "size=128")
			_, err := router.GetCoverArt(w, r)

			Expect(err).To(BeNil())
			Expect(artwork.recvId).To(Equal("34"))
			Expect(artwork.recvSize).To(Equal(128))
			Expect(w.Body.String()).To(Equal(artwork.data))
		})

		It("should return placeholder if id parameter is missing (mimicking Subsonic)", func() {
			r := newGetRequest()
			_, err := router.GetCoverArt(w, r)

			Expect(err).To(BeNil())
			Expect(w.Body.String()).To(Equal(artwork.data))
		})

		It("should fail when the file is not found", func() {
			artwork.err = model.ErrNotFound
			r := newGetRequest("id=34", "size=128")
			_, err := router.GetCoverArt(w, r)

			Expect(err).To(MatchError("Artwork not found"))
		})

		It("should fail when there is an unknown error", func() {
			artwork.err = errors.New("weird error")
			r := newGetRequest("id=34", "size=128")
			_, err := router.GetCoverArt(w, r)

			Expect(err).To(MatchError("weird error"))
		})
	})

	Describe("GetLyrics", func() {
		It("should return data for given artist & title", func() {
			r := newGetRequest("artist=Rick+Astley", "title=Never+Gonna+Give+You+Up")
			mockRepo.SetData(model.MediaFiles{
				{
					ID:     "1",
					Artist: "Rick Astley",
					Title:  "Never Gonna Give You Up",
					Lyrics: "[00:18.80]We're no strangers to love\n[00:22.80]You know the rules and so do I",
				},
			})
			response, err := router.GetLyrics(r)
			if err != nil {
				log.Error("You're missing something.", err)
			}
			Expect(err).To(BeNil())
			Expect(response.Lyrics.Artist).To(Equal("Rick Astley"))
			Expect(response.Lyrics.Title).To(Equal("Never Gonna Give You Up"))
			Expect(response.Lyrics.Value).To(Equal("We're no strangers to love\nYou know the rules and so do I"))
		})
		It("should return empty subsonic response if the record corresponding to the given artist & title is not found", func() {
			r := newGetRequest("artist=Dheeraj", "title=Rinkiya+Ke+Papa")
			mockRepo.SetData(model.MediaFiles{})
			response, err := router.GetLyrics(r)
			if err != nil {
				log.Error("You're missing something.", err)
			}
			Expect(err).To(BeNil())
			Expect(response.Lyrics.Artist).To(Equal(""))
			Expect(response.Lyrics.Title).To(Equal(""))
			Expect(response.Lyrics.Value).To(Equal(""))

		})
	})
})

type fakeArtwork struct {
	artwork.Artwork
	data     string
	err      error
	recvId   string
	recvSize int
}

func (c *fakeArtwork) GetOrPlaceholder(_ context.Context, id string, size int) (io.ReadCloser, time.Time, error) {
	if c.err != nil {
		return nil, time.Time{}, c.err
	}
	c.recvId = id
	c.recvSize = size
	return io.NopCloser(bytes.NewReader([]byte(c.data))), time.Time{}, nil
}

var _ = Describe("isSynced", func() {
	It("returns false if lyrics contain no timestamps", func() {
		Expect(isSynced("Just in case my car goes off the highway")).To(Equal(false))
		Expect(isSynced("[02.50] Just in case my car goes off the highway")).To(Equal(false))
	})
	It("returns false if lyrics is an empty string", func() {
		Expect(isSynced("")).To(Equal(false))
	})
	It("returns true if lyrics contain timestamps", func() {
		Expect(isSynced(`NF Real Music
		[00:00] First line
		[00:00.85] JUST LIKE YOU
		[00:00.85] Just in case my car goes off the highway`)).To(Equal(true))
		Expect(isSynced("[04:02:50.85] Never gonna give you up")).To(Equal(true))
		Expect(isSynced("[02:50.85] Never gonna give you up")).To(Equal(true))
		Expect(isSynced("[02:50] Never gonna give you up")).To(Equal(true))
	})

})

type mockedMediaFile struct {
	model.MediaFileRepository
	data model.MediaFiles
}

func (m *mockedMediaFile) SetData(mfs model.MediaFiles) {
	m.data = mfs
}

func (m *mockedMediaFile) GetAll(...model.QueryOptions) (model.MediaFiles, error) {
	return m.data, nil
}
