package spotifyweb

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

	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/utils"
	"github.com/wtfutil/wtf/view"
	"github.com/zmb3/spotify"
)

var (
	auth           spotify.Authenticator
	tempClientChan = make(chan *spotify.Client)
	state          = "wtfSpotifyWebStateString"
	authURL        string
	callbackPort   string
	redirectURI    string
)

// Info is the struct that contains all the information the Spotify player displays to the user
type Info struct {
	Artists     string
	Title       string
	Album       string
	TrackNumber int
	Status      string
}

// Widget is the struct used by all WTF widgets to transfer to the main widget controller
type Widget struct {
	view.KeyboardWidget
	view.TextWidget

	Info

	client      *spotify.Client
	clientChan  chan *spotify.Client
	playerState *spotify.PlayerState
	settings    *Settings
}

func authHandler(w http.ResponseWriter, r *http.Request) {
	tok, err := auth.Token(state, r)
	if err != nil {
		http.Error(w, "Couldn't get token", http.StatusForbidden)
	}
	if st := r.FormValue("state"); st != state {
		http.NotFound(w, r)
	}
	// use the token to get an authenticated client
	client := auth.NewClient(tok)
	_, err = fmt.Fprintf(w, "Login Completed!")
	if err != nil {
		return
	}
	tempClientChan <- &client
}

// NewWidget creates a new widget for WTF
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
	redirectURI = "http://localhost:" + settings.callbackPort + "/callback"

	auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadCurrentlyPlaying, spotify.ScopeUserReadPlaybackState, spotify.ScopeUserModifyPlaybackState)
	auth.SetAuthInfo(settings.clientID, settings.secretKey)
	authURL = auth.AuthURL(state)

	var client *spotify.Client
	var playerState *spotify.PlayerState

	widget := Widget{
		KeyboardWidget: view.NewKeyboardWidget(app, pages, settings.common),
		TextWidget:     view.NewTextWidget(app, settings.common),

		Info: Info{},

		client:      client,
		clientChan:  tempClientChan,
		playerState: playerState,
		settings:    settings,
	}

	http.HandleFunc("/callback", authHandler)
	go func() {
		err := http.ListenAndServe(":"+callbackPort, nil)
		if err != nil {
			return
		}
	}()

	go func() {
		// wait for auth to complete
		client = <-tempClientChan

		// use the client to make calls that require authorization
		_, err := client.CurrentUser()
		if err != nil {
			panic(err)
		}

		playerState, err = client.PlayerState()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		widget.client = client
		widget.playerState = playerState
		widget.Refresh()
	}()

	// While I wish I could find the reason this doesn't work, I can't.
	//
	// Normally, this should open the URL to the browser, however it opens the Explorer window in Windows.
	// This mostly likely has to do with the fact that the URL includes some very special characters that no terminal likes.
	// The only solution would be to include quotes in the command, which is why I do here, but it doesn't work.
	//
	// If inconvenient, I'll remove this option and save the URL in a file or some other method.
	utils.OpenFile(`"` + authURL + `"`)

	widget.settings.common.RefreshInterval = 5

	widget.initializeKeyboardControls()
	widget.View.SetInputCapture(widget.InputCapture)

	widget.View.SetWrap(true)
	widget.View.SetWordWrap(true)

	widget.KeyboardWidget.SetView(widget.View)

	return &widget
}

func (w *Widget) refreshSpotifyInfos() error {
	if w.client == nil || w.playerState == nil {
		return errors.New("authentication failed! Please log in to Spotify by visiting the following page in your browser: " + authURL)
	}
	var err error
	w.playerState, err = w.client.PlayerState()
	if err != nil {
		return errors.New("extracting player state failed! Please refresh or restart WTF")
	}
	w.Info.Album = fmt.Sprint(w.playerState.CurrentlyPlaying.Item.Album.Name)
	artists := ""
	for _, artist := range w.playerState.CurrentlyPlaying.Item.Artists {
		artists += artist.Name + ", "
	}
	artists = artists[:len(artists)-2]
	w.Info.Artists = artists
	w.Info.Title = fmt.Sprint(w.playerState.CurrentlyPlaying.Item.Name)
	w.Info.TrackNumber = w.playerState.CurrentlyPlaying.Item.TrackNumber
	if w.playerState.CurrentlyPlaying.Playing {
		w.Info.Status = "Playing"
	} else {
		w.Info.Status = "Paused"
	}
	return nil
}

// Refresh refreshes the current view of the widget
func (w *Widget) Refresh() {
	w.Redraw(w.createOutput)
}

func (widget *Widget) HelpText() string {
	return widget.KeyboardWidget.HelpText()
}

func (w *Widget) createOutput() (string, string, bool) {
	var output string

	err := w.refreshSpotifyInfos()
	if err != nil {
		output = err.Error()
	} else {
		output += utils.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.CommonSettings().Width)
		output += utils.CenterText(fmt.Sprintf("[green]Title:[white] %v\n", w.Info.Title), w.CommonSettings().Width)
		output += utils.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artists), w.CommonSettings().Width)
		output += utils.CenterText(fmt.Sprintf("[green]Album:[white] %v\n", w.Info.Album), w.CommonSettings().Width)
		if w.playerState.ShuffleState {
			output += utils.CenterText("[green]Shuffle:[white] on\n", w.CommonSettings().Width)
		} else {
			output += utils.CenterText("[green]Shuffle:[white] off\n", w.CommonSettings().Width)
		}
	}
	return w.CommonSettings().Title, output, true
}
