// Copyright (c) 2026 Sebastian Palencsar
// SPDX-License-Identifier: GPL-3.0-or-later

#include "bearplayer.h"

#include <QDebug>
#include <QMediaMetaData>
#include <QUrl>

BearPlayer::BearPlayer(QObject *parent) : QObject(parent) {
  m_audioOutput = new QAudioOutput(this);
  m_mediaPlayer = new QMediaPlayer(this);
  m_mediaPlayer->setAudioOutput(m_audioOutput);

  m_coverArtFetcher = new CoverArtFetcher(this);
  connect(m_coverArtFetcher, &CoverArtFetcher::coverUrlReady, this,
          &BearPlayer::onCoverUrlReady);

  m_icyReader = new IcyReader(this);
  connect(m_icyReader, &IcyReader::metaDataReceived, this,
          &BearPlayer::onIcyMetaDataReceived);

  connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this,
          &BearPlayer::onPlaybackStateChanged);
  connect(m_mediaPlayer, &QMediaPlayer::mediaStatusChanged, this,
          &BearPlayer::onMediaStatusChanged);
  connect(m_mediaPlayer, &QMediaPlayer::metaDataChanged, this,
          &BearPlayer::onMetaDataChanged);

  m_retryTimer.setSingleShot(true);
  m_retryTimer.setInterval(1200);
  connect(&m_retryTimer, &QTimer::timeout, this, [this]() {
    if (m_lastUrl.isEmpty() || m_playing || m_retryAttempts >= 2) {
      return;
    }
    ++m_retryAttempts;
    m_mediaPlayer->setSource(QUrl(m_lastUrl));
    m_mediaPlayer->play();
    qDebug() << "Retry stream" << m_retryAttempts << m_lastName;
  });
}

BearPlayer::~BearPlayer() { stop(); }

void BearPlayer::playUrl(const QString &url, const QString &name) {
  if (url.isEmpty()) {
    return;
  }

  m_currentStationName = name;
  m_lastName = name;
  m_lastUrl = url;
  m_retryAttempts = 0;
  emit currentStationChanged(m_currentStationName);
  clearTrackInfo();

  m_mediaPlayer->setSource(QUrl(url));
  m_mediaPlayer->play();
  m_icyReader->start(url);

  qDebug() << "Playing:" << name << url;
}

void BearPlayer::stop() {
  m_mediaPlayer->stop();
  m_icyReader->stop();
  m_currentStationName.clear();
  m_lastName.clear();
  m_lastUrl.clear();
  m_retryAttempts = 0;
  emit currentStationChanged(QString());
  clearTrackInfo();
}

void BearPlayer::togglePlayPause() {
  if (m_playing) {
    m_mediaPlayer->pause();
  } else {
    m_mediaPlayer->play();
  }
}

void BearPlayer::setVolume(qreal vol) {
  m_audioOutput->setVolume(qBound(0.0, vol, 1.0));
  emit volumeChanged(m_audioOutput->volume());
}

void BearPlayer::onPlaybackStateChanged(QMediaPlayer::PlaybackState state) {
  m_playing = (state == QMediaPlayer::PlayingState);
  emit playingChanged(m_playing);

  if (state == QMediaPlayer::StoppedState) {
    scheduleRetry();
  }
}

void BearPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status) {
  if (status == QMediaPlayer::InvalidMedia) {
    m_playing = false;
    emit playingChanged(m_playing);
    scheduleRetry();
  }
}

void BearPlayer::scheduleRetry() {
  if (m_lastUrl.isEmpty() || m_retryAttempts >= 2 || m_playing) {
    return;
  }
  if (!m_retryTimer.isActive()) {
    m_retryTimer.start();
  }
}

void BearPlayer::onMetaDataChanged() {
  QMediaMetaData meta = m_mediaPlayer->metaData();

  QString artist = meta.stringValue(QMediaMetaData::ContributingArtist);
  if (artist.isEmpty()) {
    artist = meta.stringValue(QMediaMetaData::Author);
  }

  QString title = meta.stringValue(QMediaMetaData::Title);

  // Ignore completely empty metadata from QMediaPlayer so we don't overwrite
  // IcyReader
  if (artist.isEmpty() && title.isEmpty()) {
    return;
  }

  if (m_currentTrackArtist == artist && m_currentTrackTitle == title) {
    return;
  }

  m_currentTrackArtist = artist;
  m_currentTrackTitle = title;
  m_currentCoverArtUrl.clear();
  m_coverArtFetcher->fetch(artist, title);
  emit trackInfoChanged();
}

void BearPlayer::onCoverUrlReady(const QString &url) {
  if (m_currentCoverArtUrl != url) {
    m_currentCoverArtUrl = url;
    emit trackInfoChanged();
  }
}

void BearPlayer::onIcyMetaDataReceived(const QString &artist,
                                       const QString &title) {
  if (m_currentTrackArtist == artist && m_currentTrackTitle == title) {
    return;
  }

  m_currentTrackArtist = artist;
  m_currentTrackTitle = title;
  m_currentCoverArtUrl.clear();
  m_coverArtFetcher->fetch(artist, title);
  emit trackInfoChanged();
}

QString BearPlayer::currentNowPlaying() const {
  if (!m_currentTrackArtist.isEmpty() && !m_currentTrackTitle.isEmpty()) {
    return m_currentTrackArtist + QStringLiteral(" - ") + m_currentTrackTitle;
  }
  if (!m_currentTrackTitle.isEmpty()) {
    return m_currentTrackTitle;
  }
  if (!m_currentTrackArtist.isEmpty()) {
    return m_currentTrackArtist;
  }
  return QString();
}

void BearPlayer::clearTrackInfo() {
  if (m_currentTrackArtist.isEmpty() && m_currentTrackTitle.isEmpty()) {
    return;
  }
  m_currentTrackArtist.clear();
  m_currentTrackTitle.clear();
  m_currentCoverArtUrl.clear();
  m_coverArtFetcher->fetch(QString(), QString());
  emit trackInfoChanged();
}
