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

import (
	"os"
	"time"

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

type JSPFBackend struct {
	filePath   string
	title      string
	creator    string
	identifier string
	tracks     []jspf.Track
}

func (b *JSPFBackend) Name() string { return "jspf" }

func (b *JSPFBackend) FromConfig(config *viper.Viper) models.Backend {
	b.filePath = config.GetString("file-path")
	b.title = config.GetString("title")
	b.creator = config.GetString("username")
	b.identifier = config.GetString("identifier")
	b.tracks = make([]jspf.Track, 0)
	return b
}

func (b *JSPFBackend) StartImport() error { return nil }
func (b *JSPFBackend) FinishImport() error {
	err := b.writeJSPF(b.tracks)
	return err
}

func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) {
	for _, listen := range export.Listens {
		track := listenAsTrack(listen)
		b.tracks = append(b.tracks, track)
		importResult.ImportCount += 1
		importResult.UpdateTimestamp(listen.ListenedAt)
	}

	progress <- models.Progress{}.FromImportResult(importResult)
	return importResult, nil
}

func (b *JSPFBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) {
	for _, love := range export.Loves {
		track := loveAsTrack(love)
		b.tracks = append(b.tracks, track)
		importResult.ImportCount += 1
		importResult.UpdateTimestamp(love.Created)
	}

	progress <- models.Progress{}.FromImportResult(importResult)
	return importResult, nil
}

func listenAsTrack(l models.Listen) jspf.Track {
	l.FillAdditionalInfo()
	track := trackAsTrack(l.Track)
	extension := makeMusicBrainzExtension(l.Track)
	extension.AddedAt = l.ListenedAt
	extension.AddedBy = l.UserName
	track.Extension[jspf.MusicBrainzTrackExtensionId] = extension

	if l.RecordingMbid != "" {
		track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(l.RecordingMbid))
	}

	return track
}

func loveAsTrack(l models.Love) jspf.Track {
	l.FillAdditionalInfo()
	track := trackAsTrack(l.Track)
	extension := makeMusicBrainzExtension(l.Track)
	extension.AddedAt = l.Created
	extension.AddedBy = l.UserName
	track.Extension[jspf.MusicBrainzTrackExtensionId] = extension

	recordingMbid := l.Track.RecordingMbid
	if l.RecordingMbid != "" {
		recordingMbid = l.RecordingMbid
	}
	if recordingMbid != "" {
		track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(recordingMbid))
	}

	return track
}

func trackAsTrack(t models.Track) jspf.Track {
	track := jspf.Track{
		Title:     t.TrackName,
		Album:     t.ReleaseName,
		Creator:   t.ArtistName(),
		TrackNum:  t.TrackNumber,
		Extension: map[string]any{},
	}

	return track
}

func makeMusicBrainzExtension(t models.Track) jspf.MusicBrainzTrackExtension {
	extension := jspf.MusicBrainzTrackExtension{
		AdditionalMetadata: t.AdditionalInfo,
		ArtistIdentifiers:  make([]string, len(t.ArtistMbids)),
	}

	for i, mbid := range t.ArtistMbids {
		extension.ArtistIdentifiers[i] = "https://musicbrainz.org/artist/" + string(mbid)
	}

	if t.ReleaseMbid != "" {
		extension.ReleaseIdentifier = "https://musicbrainz.org/release/" + string(t.ReleaseMbid)
	}

	// The tracknumber tag would be redundant
	delete(extension.AdditionalMetadata, "tracknumber")

	return extension
}

func (b JSPFBackend) writeJSPF(tracks []jspf.Track) error {
	playlist := jspf.JSPF{
		Playlist: jspf.Playlist{
			Title:      b.title,
			Creator:    b.creator,
			Identifier: b.identifier,
			Date:       time.Now(),
			Tracks:     tracks,
		},
	}

	file, err := os.Create(b.filePath)
	if err != nil {
		return err
	}

	defer file.Close()
	return playlist.Write(file)
}