#pragma once

#include <QJsonObject>
#include <QObject>
#include <QString>
#include <QStringList>

class QThread;
class BuildArchiveWorker;

// Смена активной сборки: содержимое .minecraft уезжает в архив своей сборки,
// папка чистится, на её место разворачивается архив выбранной.
//
// Порядок шагов подчинён одному правилу: пока новый архив не записан целиком и
// не переименован на место, из .minecraft не удаляется ничего. Отметка о
// начатом переключении пишется в builds/index.json до первого разрушающего
// действия, поэтому обрыв питания или kill -9 всегда обнаружим на следующем
// запуске — см. recoverIfNeeded().
class BuildSwitcher : public QObject
{
    Q_OBJECT

public:
    explicit BuildSwitcher(QObject *parent = nullptr);
    ~BuildSwitcher() override;

    bool isRunning() const { return m_state != Idle; }
    QString stage() const { return m_stage; }
    QString status() const { return m_status; }
    double fraction() const;   // 0..1, либо -1 пока итог неизвестен

    // Общие для всех сборок каталоги: они не архивируются и не чистятся.
    static QStringList sharedTopLevel();

    void switchTo(int fromBuildId,
                  const QString &fromName,
                  int toBuildId,
                  const QString &toName,
                  const QString &gameDir);
    void cancel();

    // Сборку удалили: убираем её архив и запись о нём. Возвращает false, если
    // папку архива не удалось удалить целиком.
    bool forgetBuild(int buildId);

    // Есть ли у сборки сохранённый архив (для текста предупреждения об удалении).
    bool hasArchive(int buildId) const { return !archivePathOf(buildId).isEmpty(); }

    // Незавершённое переключение с прошлого запуска. Возвращает пустую строку,
    // если всё в порядке, иначе — текст для пользователя.
    QString interruptedSwitchWarning() const;
    // Доигрывает прерванное переключение: очистка и распаковка целевой сборки.
    void resumeInterrupted(const QString &gameDir);

signals:
    void progressChanged();
    void finished(int toBuildId);
    void failed(int toBuildId, const QString &message, bool gameDirIntact);

private:
    enum State { Idle, Archiving, Clearing, Restoring };

    // Сигналы к рабочему объекту в другом потоке.
    void requestArchive(const QString &gameDir, const QString &tempZip, const QStringList &exclude);
    void requestClear(const QString &gameDir, const QStringList &keep);
    void requestRestore(const QString &zipPath, const QString &gameDir);

    void onWorkerFinished(bool ok, const QString &error);
    void onWorkerProgress(int done, int total, const QString &currentPath);

    void commitArchive();
    void beginClearing();
    void beginRestoring();
    void finish();
    void fail(const QString &message, bool gameDirIntact);

    QString buildDir(int buildId) const;
    QString archivePathOf(int buildId) const;
    void setStage(const QString &stage);

    QJsonObject readIndex() const;
    void writeIndex(const QJsonObject &index) const;
    void sweepTempArchives() const;

    QThread *m_thread;
    BuildArchiveWorker *m_worker;

    State m_state = Idle;
    QString m_stage;
    QString m_status;
    int m_done = 0;
    int m_total = 0;

    int m_fromId = -1;
    int m_toId = -1;
    QString m_fromName;
    QString m_toName;
    QString m_gameDir;
    QString m_tempZip;
};
