#include "EventHandler.h"

#include <QMap>
#include <QThread>
#include <QTimer>

#include "dao/AccountDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/EventDAO.h"
#include "rshb/CommonScript.h"
#include "rshb/PayByPhoneScript.h"

EventHandler::EventHandler(QObject *parent) : QObject(parent) {
    m_checkTimer = new QTimer(this);
    m_checkTimer->setInterval(1000);
    connect(m_checkTimer, &QTimer::timeout, this, &EventHandler::checkAndRestart);
}

EventHandler::~EventHandler() = default;

void EventHandler::start() {
    m_devices = DeviceDAO::getOnlineDeviceCodes();

    clearAll();
    // Запустим сразу потоки для всех ключей
    for (auto &key: m_devices) {
        startThreadForKey(key);
    }

    m_checkTimer->start();
}

void EventHandler::checkAndRestart() {
    if (!m_running) {
        return;
    }
    m_devices = DeviceDAO::getOnlineDeviceCodes();

    for (const auto &key: m_devices) {
        const QThread *thr = m_threads.value(key, nullptr);
        if (!thr || !thr->isRunning()) {
            startThreadForKey(key);
        }
    }
}

void EventHandler::startThreadForKey(const QString &key) {
    const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key);
    if (event.id == -1) {
        return;
    }
    if (!EventDAO::updateEvent(event.id, EventStatus::InProgress)) {
        qCritical() << "Cant update event: " << event.id;
        return;
    }

    qDebug() << "Thread for key" << key << "running";
    // FIXME пока это только оплата,
    const AccountInfo account = AccountDAO::getAccountById(event.accountId);
    const auto thr = new QThread(this);
    auto *worker = new PayByPhoneScript(account, event.phone, event.bankName, event.amount);

    worker->moveToThread(thr);
    connect(thr, &QThread::started, worker, &PayByPhoneScript::start);
    connect(worker, &PayByPhoneScript::finished, thr, &QThread::quit);
    connect(worker, &PayByPhoneScript::finished, worker, &QObject::deleteLater);
    connect(thr, &QThread::finished, thr, &QObject::deleteLater);

    // При нормальном завершении — перезапустить новый поток
    connect(worker, &PayByPhoneScript::finished, this,
            [this, key]() {
                // qDebug() << "Thread finished for key" << key << "- restarting.";
                if (const auto tm = m_timers.take(key)) {
                    tm->stop();
                    tm->deleteLater();
                }
                m_threads.remove(key);
            });

    // Таймер на 5 минут — если не успел finish(), убить и перезапустить
    const auto timer = new QTimer(this);
    timer->setSingleShot(true);
    connect(timer, &QTimer::timeout, this, [this, key]() {
        qWarning() << "Timeout for key" << key << "- killing stuck thread.";
        if (QThread *thread = m_threads.value(key); thread && thread->isRunning()) {
            thread->requestInterruption(); // мягкое прерывание
            if (!thread->wait(500)) {
                qWarning() << "Hard terminate for key" << key;
                thread->terminate();
                thread->wait();
            }
        }
        if (const auto tm = m_timers.take(key)) {
            tm->deleteLater();
        }
        m_threads.remove(key);
    });

    // Сохраняем и запускаем
    m_threads[key] = thr;
    m_timers[key] = timer;

    timer->start(5 * 60 * 1000);
    thr->start();
}

void EventHandler::clearAll() {
    // Останавливаем и удаляем старые
    for (QThread *thread: m_threads) {
        if (thread->isRunning()) {
            thread->requestInterruption();
            thread->terminate();
            thread->wait();
        }
        thread->deleteLater();
    }
    m_threads.clear();

    for (QTimer *timer: m_timers) {
        timer->stop();
        timer->deleteLater();
    }
    m_timers.clear();
}

void EventHandler::stop() {
    m_running = false;
    m_checkTimer->stop();
    clearAll();
}
