#pragma once

#include <QDate>
#include <QDateTime>
#include <QHash>
#include <QList>
#include <QObject>
#include <QString>
#include <QUrl>

#include <functional>
#include <optional>

class QNetworkAccessManager;
class QJsonObject;

// Одна готовая сборка с сервера сезонных сборок.
struct SeasonalBuildEntry
{
    QString id;                 // season-5 — он же ключ, по которому сборка
                                // узнаётся среди локальных записей
    QString name;               // «Сезон 5: Пустоши»
    int revision = 0;           // растёт при каждой публикации
    QString minecraftVersion;   // 1.20.1
    QString loader;             // "" | forge | fabric | neoforge | quilt
    QString loaderVersion;      // 47.4.0
    int modCount = 0;
    QDate seasonStart;
    QDate seasonEnd;            // невалидная — сезон ещё не закончен
    QString serverUrl;          // адрес игрового сервера, не файлового
    int javaMajor = 0;          // 0 — определять по версии игры
    QString description;

    QUrl archiveUrl;
    qint64 archiveSize = 0;
    QString archiveSha256;

    bool isValid() const { return !id.isEmpty() && revision > 0 && archiveUrl.isValid(); }
};

// Каталог сезонных сборок: скачивает index.json с файлового сервера, кэширует
// его в папке лаунчера и отдаёт из кэша, пока тот не устарел. Устройство
// повторяет VersionManifestService — включая то, что пустой разбор считается
// испорченным ответом и хороший кэш им не затирается.
class SeasonalBuildService : public QObject
{
    Q_OBJECT

public:
    // ok = true и непустой warning — данные отдали из устаревшего кэша.
    using Callback = std::function<void(bool ok, const QString &warning)>;

    explicit SeasonalBuildService(QObject *parent = nullptr);

    QList<SeasonalBuildEntry> builds() const { return m_builds; }
    bool hasData() const { return !m_builds.isEmpty(); }
    bool isRefreshing() const { return m_refreshing; }
    // Последняя ошибка обращения к серверу; пусто — всё в порядке.
    QString lastError() const { return m_lastError; }

    std::optional<SeasonalBuildEntry> find(const QString &id) const;

    // Адрес сервера сборок. Меняется из настроек, поэтому при смене хоста
    // накопленные данные и кэш сбрасываются: ссылки в них указывают на старый.
    void setBaseUrl(const QUrl &baseUrl);
    QUrl baseUrl() const { return m_baseUrl; }

    void ensureLoaded(Callback callback, bool forceRefresh = false);

signals:
    void buildsChanged();
    void refreshingChanged();

private:
    bool readCache();
    void writeCache();
    void startRefresh();
    void finishRefresh(bool ok, const QString &warning);
    void applyBuilds(QList<SeasonalBuildEntry> list);
    QList<SeasonalBuildEntry> parseCatalog(const QJsonObject &root, QString *error) const;

    QNetworkAccessManager *m_network;
    QUrl m_baseUrl;
    QList<SeasonalBuildEntry> m_builds;
    QHash<QString, int> m_indexById;
    QDateTime m_fetchedAt;
    QString m_lastError;
    bool m_refreshing = false;
    bool m_cacheRead = false;
    QList<Callback> m_pending;
};
