/*
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 maloja

import (
	"errors"
	"sort"
	"strings"
	"time"

	"github.com/spf13/viper"
	"go.uploadedlobster.com/scotty/internal/models"
)

type MalojaApiBackend struct {
	client Client
	nofix  bool
}

func (b *MalojaApiBackend) Name() string { return "maloja" }

func (b *MalojaApiBackend) FromConfig(config *viper.Viper) models.Backend {
	b.client = NewClient(
		config.GetString("server-url"),
		config.GetString("token"),
	)
	b.nofix = config.GetBool("nofix")
	return b
}

func (b *MalojaApiBackend) StartImport() error  { return nil }
func (b *MalojaApiBackend) FinishImport() error { return nil }

func (b *MalojaApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
	page := 0
	perPage := MaxItemsPerGet

	defer close(results)

	// We need to gather the full list of listens in order to sort them
	listens := make(models.ListensList, 0, 2*perPage)
	p := models.Progress{Total: int64(perPage)}

out:
	for {
		result, err := b.client.GetScrobbles(page, perPage)
		if err != nil {
			progress <- p.Complete()
			results <- models.ListensResult{Error: err}
			return
		}

		count := len(result.List)
		if count == 0 {
			break
		}

		for _, scrobble := range result.List {
			if scrobble.ListenedAt > oldestTimestamp.Unix() {
				p.Elapsed += 1
				listens = append(listens, scrobble.AsListen())
			} else {
				break out
			}
		}

		p.Total += int64(perPage)
		progress <- p
		page += 1
	}

	sort.Sort(listens)
	progress <- p.Complete()
	results <- models.ListensResult{Listens: listens}
}

func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) {
	for _, listen := range export.Listens {
		scrobble := NewScrobble{
			Title:    listen.TrackName,
			Artists:  listen.ArtistNames,
			Album:    listen.ReleaseName,
			Duration: int64(listen.PlaybackDuration.Seconds()),
			Length:   int64(listen.Duration.Seconds()),
			Time:     listen.ListenedAt.Unix(),
			Nofix:    b.nofix,
		}

		resp, err := b.client.NewScrobble(scrobble)
		if err != nil {
			return importResult, err
		} else if resp.Status != "success" {
			return importResult, errors.New(resp.Error.Description)
		}

		importResult.UpdateTimestamp(listen.ListenedAt)
		importResult.ImportCount += 1
		progress <- models.Progress{}.FromImportResult(importResult)
	}

	return importResult, nil
}

func (s Scrobble) AsListen() models.Listen {
	track := s.Track
	listen := models.Listen{
		ListenedAt:       time.Unix(s.ListenedAt, 0),
		PlaybackDuration: time.Duration(s.Duration * int64(time.Second)),
		Track: models.Track{
			TrackName:      track.Title,
			ReleaseName:    track.Album.Title,
			ArtistNames:    track.Artists,
			Duration:       time.Duration(track.Length * int64(time.Second)),
			AdditionalInfo: map[string]any{},
		},
	}

	client, found := strings.CutPrefix(s.Origin, "client:")
	if found {
		listen.AdditionalInfo["media_player"] = client
	}

	return listen
}