/*
 * Strawberry Music Player
 * Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
 *
 * Strawberry 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.
 *
 * Strawberry 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 Strawberry.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef LASTFMSCROBBLER_H
#define LASTFMSCROBBLER_H

#include "config.h"

#include <QVariant>
#include <QByteArray>
#include <QString>

#include "includes/shared_ptr.h"
#include "core/song.h"
#include "scrobblerservice.h"
#include "scrobblercache.h"
#include "scrobblercacheitem.h"

class QTimer;
class QNetworkReply;

class ScrobblerSettingsService;
class NetworkAccessManager;
class LocalRedirectServer;

class LastFMScrobbler : public ScrobblerService {
  Q_OBJECT

 public:
  explicit LastFMScrobbler(const SharedPtr<ScrobblerSettingsService> settings, const SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
  ~LastFMScrobbler() override;

  static const char *kName;
  static const char *kSettingsGroup;
  static const char *kApiUrl;
  static const char *kApiKey;

  void ReloadSettings() override;
  void LoadSession();
  void ClearSession();

  bool enabled() const override { return enabled_; }
  bool authentication_required() const override { return true; }
  bool authenticated() const override { return !username_.isEmpty() && !session_key_.isEmpty(); }
  bool use_authorization_header() const override { return false; }
  QByteArray authorization_header() const override { return QByteArray(); }

  bool subscriber() const { return subscriber_; }
  bool submitted() const override { return submitted_; }
  QString username() const { return username_; }

  void Authenticate();
  void UpdateNowPlaying(const Song &song) override;
  void ClearPlaying() override;
  void Scrobble(const Song &song) override;
  void Submit() override;
  void Love() override;

 Q_SIGNALS:
  void AuthenticationComplete(const bool success, const QString &error = QString());

 public Q_SLOTS:
  void WriteCache() override { cache_->WriteCache(); }

 private Q_SLOTS:
  void RedirectArrived();
  void AuthenticateReplyFinished(QNetworkReply *reply);
  void UpdateNowPlayingRequestFinished(QNetworkReply *reply);
  void ScrobbleRequestFinished(QNetworkReply *reply, ScrobblerCacheItemPtrList cache_items);
  void SingleScrobbleRequestFinished(QNetworkReply *reply, ScrobblerCacheItemPtr cache_item);
  void LoveRequestFinished(QNetworkReply *reply);

 private:
  enum class ScrobbleErrorCode {
    NoError = 1,
    InvalidService = 2,
    InvalidMethod = 3,
    AuthenticationFailed = 4,
    InvalidFormat = 5,
    InvalidParameters = 6,
    InvalidResourceSpecified = 7,
    OperationFailed = 8,
    InvalidSessionKey = 9,
    InvalidApiKey = 10,
    ServiceOffline = 11,
    SubscribersOnly = 12,
    InvalidMethodSignature = 13,
    UnauthorizedToken = 14,
    ItemUnavailable = 15,
    TemporarilyUnavailable = 16,
    LoginRequired = 17,
    TrialExpired = 18,
    ErrorDoesNotExist = 19,
    NotEnoughContent = 20,
    NotEnoughMembers = 21,
    NotEnoughFans = 22,
    NotEnoughNeighbours = 23,
    NoPeakRadio = 24,
    RadioNotFound = 25,
    APIKeySuspended = 26,
    Deprecated = 27,
    RateLimitExceeded = 29,
  };

  QNetworkReply *CreateRequest(const ParamList &request_params);
  JsonObjectResult ParseJsonObject(QNetworkReply *reply);
  void RequestSession(const QString &token);
  void AuthError(const QString &error);
  void SendSingleScrobble(ScrobblerCacheItemPtr item);
  void Error(const QString &error, const QVariant &debug = QVariant()) override;
  static QString ErrorString(const ScrobbleErrorCode error);
  void StartSubmit(const bool initial = false) override;
  void CheckScrobblePrevSong();

 protected:
  const SharedPtr<NetworkAccessManager> network_;
  ScrobblerCache *cache_;
  LocalRedirectServer *local_redirect_server_;

  bool enabled_;
  bool prefer_albumartist_;

  bool subscriber_;
  QString username_;
  QString session_key_;

  bool submitted_;
  Song song_playing_;
  bool scrobbled_;
  quint64 timestamp_;
  bool submit_error_;

  QTimer *timer_submit_;
};

#endif  // LASTFMSCROBBLER_H
