/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>

This file is part of Scotty.

Scotty is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.

Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
Scotty. If not, see <https://www.gnu.org/licenses/>.
*/
package subsonic

import (
	"net/http"
	"sort"
	"time"

	"github.com/delucks/go-subsonic"
	"go.uploadedlobster.com/scotty/internal/config"
	"go.uploadedlobster.com/scotty/internal/i18n"
	"go.uploadedlobster.com/scotty/internal/models"
	"go.uploadedlobster.com/scotty/internal/version"
)

type SubsonicApiBackend struct {
	client   subsonic.Client
	password string
}

func (b *SubsonicApiBackend) Name() string { return "subsonic" }

func (b *SubsonicApiBackend) Options() []models.BackendOption {
	return []models.BackendOption{{
		Name:  "server-url",
		Label: i18n.Tr("Server URL"),
		Type:  models.String,
	}, {
		Name:  "username",
		Label: i18n.Tr("User name"),
		Type:  models.String,
	}, {
		Name:  "token",
		Label: i18n.Tr("Access token"),
		Type:  models.Secret,
	}}
}

func (b *SubsonicApiBackend) FromConfig(config *config.ServiceConfig) models.Backend {
	b.client = subsonic.Client{
		Client:     &http.Client{},
		BaseUrl:    config.GetString("server-url"),
		User:       config.GetString("username"),
		ClientName: version.AppName,
	}
	b.password = config.GetString("token")
	return b
}

func (b *SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) {
	defer close(results)
	err := b.client.Authenticate(b.password)
	if err != nil {
		progress <- models.Progress{}.Complete()
		results <- models.LovesResult{Error: err}
		return
	}

	starred, err := b.client.GetStarred2(map[string]string{})
	if err != nil {
		progress <- models.Progress{}.Complete()
		results <- models.LovesResult{Error: err}
		return
	}

	progress <- models.Progress{Elapsed: int64(len(starred.Song))}.Complete()
	results <- models.LovesResult{Items: b.filterSongs(starred.Song, oldestTimestamp)}
}

func (b *SubsonicApiBackend) filterSongs(songs []*subsonic.Child, oldestTimestamp time.Time) models.LovesList {
	loves := make(models.LovesList, 0, len(songs))
	for _, song := range songs {
		love := SongAsLove(*song, b.client.User)
		if love.Created.Unix() > oldestTimestamp.Unix() {
			loves = append(loves, love)
		}
	}

	sort.Sort(loves)
	return loves
}

func SongAsLove(song subsonic.Child, username string) models.Love {
	love := models.Love{
		UserName: username,
		Created:  song.Starred,
		Track: models.Track{
			TrackName:      song.Title,
			ReleaseName:    song.Album,
			ArtistNames:    []string{song.Artist},
			TrackNumber:    song.Track,
			DiscNumber:     song.DiscNumber,
			AdditionalInfo: map[string]any{},
			Duration:       time.Duration(song.Duration * int(time.Second)),
		},
	}

	if song.Genre != "" {
		love.Track.Tags = []string{song.Genre}
	}

	return love
}