#include "comic_vine_dialog.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QStackedWidget>
#include <QTableView>
#include <QVBoxLayout>
#include <QtWidgets>
#include <QtConcurrent/QtConcurrentRun>
#include "data_base_management.h"
#include <QSqlDatabase>

#include "yacreader_busy_widget.h"
#include "comic_vine_client.h"
#include "comic_vine_json_parser.h"
#include "scraper_lineedit.h"
#include "title_header.h"
#include "series_question.h"
#include "search_single_comic.h"
#include "search_volume.h"
#include "select_comic.h"
#include "select_volume.h"
#include "selected_volume_info.h"
#include "sort_volume_comics.h"
#include "db_helper.h"
#include "response_parser.h"

#include "QsLog.h"

ComicVineDialog::ComicVineDialog(QWidget *parent)
    : QDialog(parent)
{
    setWindowFlags(Qt::Window);
    setModal(true);

    doLayout();
    doStackedWidgets();
    doConnections();
}

void ComicVineDialog::closeEvent(QCloseEvent *event)
{
    QDialog::closeEvent(event);

    clearState();
}

void ComicVineDialog::doLayout()
{
    setStyleSheet(""
                  "QDialog {background-color: #404040; }"
                  "");

    QString dialogButtonsStyleSheet = "QPushButton {border: 1px solid #242424; background: #2e2e2e; color:white; padding: 5px 26px 5px 26px; font-size:12px;font-family:Arial; font-weight:bold;}";

    skipButton = new QPushButton(tr("skip"));
    backButton = new QPushButton(tr("back"));
    nextButton = new QPushButton(tr("next"));
    searchButton = new QPushButton(tr("search"));
    closeButton = new QPushButton(tr("close"));

    backButton->setShortcut(QKeySequence(Qt::Key_Backspace));

    closeButton->setDefault(false);
    closeButton->setAutoDefault(false);

    skipButton->setStyleSheet(dialogButtonsStyleSheet);
    backButton->setStyleSheet(dialogButtonsStyleSheet);
    nextButton->setStyleSheet(dialogButtonsStyleSheet);
    searchButton->setStyleSheet(dialogButtonsStyleSheet);
    closeButton->setStyleSheet(dialogButtonsStyleSheet);

    content = new QStackedWidget(this);

    auto mainLayout = new QVBoxLayout;

    auto buttonLayout = new QHBoxLayout;

    buttonLayout->addStretch();
    buttonLayout->addWidget(skipButton);
    buttonLayout->addWidget(backButton);
    buttonLayout->addWidget(nextButton);
    buttonLayout->addWidget(searchButton);
    buttonLayout->addWidget(closeButton);
    buttonLayout->setContentsMargins(0, 0, 0, 0);

    mainLayout->addWidget(titleHeader = new TitleHeader, 0);
    mainLayout->addWidget(content, 1);
    mainLayout->addLayout(buttonLayout, 0);

    mainLayout->setContentsMargins(26, 16, 26, 11);

    setLayout(mainLayout);

    setWindowTitle("Comic Vine Scraper");
}

void ComicVineDialog::doStackedWidgets()
{
    doLoading();
    content->addWidget(seriesQuestionWidget = new SeriesQuestion);
    content->addWidget(searchSingleComicWidget = new SearchSingleComic);
    content->addWidget(searchVolumeWidget = new SearchVolume);
    content->addWidget(selectVolumeWidget = new SelectVolume);
    content->addWidget(selectComicWidget = new SelectComic);
    content->addWidget(sortVolumeComicsWidget = new SortVolumeComics);
}

void ComicVineDialog::doConnections()
{
    connect(closeButton, &QAbstractButton::clicked, this, &QWidget::close);
    connect(nextButton, &QAbstractButton::clicked, this, &ComicVineDialog::goNext);
    connect(backButton, &QAbstractButton::clicked, this, &ComicVineDialog::goBack);
    connect(searchButton, &QAbstractButton::clicked, this, &ComicVineDialog::search);
    connect(skipButton, &QAbstractButton::clicked, this, &ComicVineDialog::goToNextComic);

    connect(selectVolumeWidget, &SelectVolume::loadPage, this, &ComicVineDialog::searchVolume);
    connect(selectComicWidget, &SelectComic::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);
    connect(sortVolumeComicsWidget, &SortVolumeComics::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);

    connect(this, &QDialog::accepted, this, &QWidget::close, Qt::QueuedConnection);
}

void ComicVineDialog::goNext()
{
    clearState();

    //
    if (content->currentWidget() == seriesQuestionWidget) {
        if (seriesQuestionWidget->getYes()) {
            QString volumeSearchString = comics[0].getParentFolderName();
            mode = ScraperMode::Volume;

            showSearchVolume(volumeSearchString);
        } else {
            status = ScraperStatus::AutoSearching;
            mode = ScraperMode::SingleComicInList;
            ComicDB comic = comics[currentIndex];
            QString title = comic.getTitleOrFileName();
            titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title));

            showLoading(tr("Looking for volume..."));

            searchVolume({ volumeSearchStringFromComic(comic), 1, true });
        }
    } else if (content->currentWidget() == selectVolumeWidget) {
        currentVolumeId = selectVolumeWidget->getSelectedVolumeInfo().id;
        getVolumeComicsInfo(currentVolumeId);

    } else if (content->currentWidget() == sortVolumeComicsWidget) {
        showLoading();

        // ComicDB-ComicVineID
        QList<QPair<ComicDB, QString>> matchingInfo = sortVolumeComicsWidget->getMatchingInfo();
        auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo();

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QtConcurrent::run(&ComicVineDialog::getComicsInfo, this, matchingInfo, volumeInfo);
#else
        QtConcurrent::run(this, &ComicVineDialog::getComicsInfo, matchingInfo, volumeInfo);
#endif

    } else if (content->currentWidget() == selectComicWidget) {
        showLoading();
        QString comicId = selectComicWidget->getSelectedComicId();
        auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo();

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QtConcurrent::run(&ComicVineDialog::getComicInfo, this, comicId, volumeInfo);
#else
        QtConcurrent::run(this, &ComicVineDialog::getComicInfo, comicId, volumeInfo);
#endif
    }
}

void ComicVineDialog::goBack()
{
    clearState();

    switch (status) {
    case ScraperStatus::SelectingSeries:
        if (mode == ScraperMode::Volume)
            showSearchVolume(currentVolumeSearchQuery.volume);
        else
            showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;
    case ScraperStatus::SortingComics:
        showSelectVolume();
        break;
    case ScraperStatus::SelectingComic:
        if (mode == ScraperMode::SingleComic)
            showSelectVolume();
        break;
    case ScraperStatus::AutoSearching:
        if (mode == ScraperMode::Volume)
            showSearchVolume(currentVolumeSearchQuery.volume);
        else
            showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;

    case ScraperStatus::AskingForInfo:
    case ScraperStatus::SearchingSingleComic:
    case ScraperStatus::SearchingVolume:
    case ScraperStatus::SearchingExactVolume:
    case ScraperStatus::GettingVolumeComics:
        if (mode == ScraperMode::Volume)
            showSearchVolume(currentVolumeSearchQuery.volume);
        else
            showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;
    }
}

void ComicVineDialog::setComics(const QList<ComicDB> &comics)
{
    this->comics = comics;
}

QSize ComicVineDialog::sizeHint() const
{
    QScreen *screen = window()->screen();
    if (screen == nullptr) {
        screen = QApplication::screens().constFirst();
    }

    int heightDesktopResolution = screen->geometry().height();
    int widthDesktopResolution = screen->geometry().width();

    int height, width;
    height = qMax(529, static_cast<int>(heightDesktopResolution * 0.5));
    width = height * 1.65;

    if (width > widthDesktopResolution)
        return minimumSizeHint();

    return QSize(width, height);
}

QSize ComicVineDialog::minimumSizeHint() const
{
    return QSize(872, 529);
}

void ComicVineDialog::show()
{
    QDialog::show();

    currentIndex = 0;

    seriesQuestionWidget->setYes(true);
    searchSingleComicWidget->clean();
    searchVolumeWidget->clean();

    if (comics.length() == 1) {
        status = ScraperStatus::AskingForInfo;
        mode = ScraperMode::SingleComic;

        ComicDB singleComic = comics[0];

        QString title = singleComic.getTitleOrFileName();
        titleHeader->setSubTitle(title);

        showSearchSingleComic(volumeSearchStringFromComic(singleComic));
    } else if (comics.length() > 1) {
        titleHeader->setSubTitle(tr("%1 comics selected").arg(comics.length()));
        showSeriesQuestion();
    }
}

void ComicVineDialog::doLoading()
{
    QWidget *w = new QWidget;
    auto l = new QVBoxLayout;

    auto bw = new YACReaderBusyWidget;
    loadingMessage = new QLabel;

    loadingMessage->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");

    l->addStretch();
    l->addWidget(bw, 0, Qt::AlignHCenter);
    l->addStretch();
    l->addWidget(loadingMessage);

    l->setContentsMargins(0, 0, 0, 0);
    w->setLayout(l);
    w->setContentsMargins(0, 0, 0, 0);

    content->addWidget(w);
}

void ComicVineDialog::processClientResults(const QString &string)
{
    ResponseParser p;
    p.loadJSONResponse(string);
    // QMessageBox::information(0,"Result", QString("Number of results : %1").arg(p.getNumResults()));
    if (p.responseError()) {
        QMessageBox::critical(0, tr("Error connecting to ComicVine"), p.errorDescription());
        goBack();
    } else {

        switch (mode) {
        case ScraperMode::SingleComic:
        case ScraperMode::SingleComicInList:
            if (p.getNumResults() == 0)
                showSearchSingleComic(currentVolumeSearchQuery.volume);
            else if (status == ScraperStatus::SearchingVolume || status == ScraperStatus::SearchingExactVolume)
                showSelectVolume(string);
            else
                showSelectComic(string);
            break;
        case ScraperMode::Volume:
            if (p.getNumResults() == 0)
                showSearchVolume(currentVolumeSearchQuery.volume);
            else
                showSelectVolume(string);
            break;
        }
    }
}

void ComicVineDialog::showSeriesQuestion()
{
    status = ScraperStatus::AskingForInfo;
    content->setCurrentWidget(seriesQuestionWidget);
    backButton->setHidden(true);
    skipButton->setHidden(true);
    nextButton->setVisible(true);
    searchButton->setHidden(true);
    closeButton->setVisible(true);

    nextButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::showSearchSingleComic(const QString &volume)
{
    searchSingleComicWidget->setVolumeInfo(volume);

    status = ScraperStatus::AskingForInfo;
    content->setCurrentWidget(searchSingleComicWidget);
    backButton->setHidden(true);
    skipButton->setHidden(true);
    nextButton->setHidden(true);
    searchButton->setVisible(true);
    closeButton->setVisible(true);

    searchButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::showSearchVolume(const QString &volume)
{
    searchVolumeWidget->setVolumeInfo(volume);

    status = ScraperStatus::AskingForInfo;
    content->setCurrentWidget(searchVolumeWidget);
    backButton->setHidden(true);
    nextButton->setHidden(true);
    searchButton->setVisible(true);
    closeButton->setVisible(true);

    searchButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::showSelectVolume(const QString &json)
{
    showSelectVolume();
    selectVolumeWidget->load(json, currentVolumeSearchQuery);
}

void ComicVineDialog::showSelectVolume()
{
    status = ScraperStatus::SelectingSeries;

    content->setCurrentWidget(selectVolumeWidget);

    backButton->setVisible(true);
    nextButton->setVisible(true);
    searchButton->setHidden(true);
    closeButton->setVisible(true);

    nextButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::showSelectComic(const QString &json)
{
    status = ScraperStatus::SelectingComic;

    content->setCurrentWidget(selectComicWidget);
    selectComicWidget->load(json, currentVolumeId);

    backButton->setVisible(true);
    nextButton->setVisible(true);
    searchButton->setHidden(true);
    closeButton->setVisible(true);

    nextButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::showSortVolumeComics(const QString &json)
{
    status = ScraperStatus::SortingComics;

    content->setCurrentWidget(sortVolumeComicsWidget);

    sortVolumeComicsWidget->setData(comics, json, currentVolumeId);

    backButton->setVisible(true);
    nextButton->setVisible(true);
    searchButton->setHidden(true);
    closeButton->setVisible(true);

    nextButton->setDefault(true);

    toggleSkipButton();
}

void ComicVineDialog::queryTimeOut()
{
    QMessageBox::warning(this, "Comic Vine error", "Time out connecting to Comic Vine");

    switch (status) {
    case ScraperStatus::AutoSearching:
        if (mode == ScraperMode::Volume)
            showSearchVolume(currentVolumeSearchQuery.volume);
        else
            showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;
    case ScraperStatus::SearchingVolume:
    case ScraperStatus::SearchingExactVolume:
        if (mode == ScraperMode::Volume)
            showSearchVolume(currentVolumeSearchQuery.volume);
        else
            showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;
    case ScraperStatus::SearchingSingleComic:
        showSearchSingleComic(currentVolumeSearchQuery.volume);
        break;
    case ScraperStatus::GettingVolumeComics:
        showSelectVolume();
        break;

    case ScraperStatus::AskingForInfo:
    case ScraperStatus::SelectingComic:
    case ScraperStatus::SelectingSeries:
    case ScraperStatus::SortingComics:
        break;
    }
}

void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo, const SelectedVolumeInfo &volumeInfo)
{
    QPair<ComicDB, QString> p;
    QList<ComicDB> comics;
    foreach (p, matchingInfo) {
        auto comicVineClient = new ComicVineClient;
        // connect(comicVineClient,SIGNAL(searchResult(QString)),this,SLOT(debugClientResults(QString)));
        // connect(comicVineClient,SIGNAL(timeOut()),this,SLOT(queryTimeOut()));
        // connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
        bool error;
        bool timeout;
        QByteArray result = comicVineClient->getComicDetail(p.second, error, timeout); // TODO check timeOut or Connection error
        if (error || timeout)
            continue; // TODO
        ComicDB comic = YACReader::parseCVJSONComicInfo(p.first, result, volumeInfo); // TODO check result error
        comic.info.comicVineID = p.second;
        comics.push_back(comic);

        setLoadingMessage(tr("Retrieving tags for : %1").arg(p.first.getFileName()));
    }

    DBHelper::updateComicsInfo(comics, databasePath);

    emit accepted();
}

void ComicVineDialog::getComicInfo(const QString &comicId, const SelectedVolumeInfo &volumeInfo)
{

    auto comicVineClient = new ComicVineClient;
    bool error;
    bool timeout;
    QByteArray result = comicVineClient->getComicDetail(comicId, error, timeout); // TODO check timeOut or Connection error
    if (error || timeout) {
        // TODO
        if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
            emit accepted();
        } else {
            goToNextComic();
        }
    }

    ComicDB comic = YACReader::parseCVJSONComicInfo(comics[currentIndex], result, volumeInfo); // TODO check result error
    comic.info.comicVineID = comicId;
    setLoadingMessage(tr("Retrieving tags for : %1").arg(comics[currentIndex].getFileName()));
    QString connectionName = "";
    {
        QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
        db.open();
        db.transaction();

        DBHelper::update(&(comic.info), db);

        db.commit();
        connectionName = db.connectionName();
    }
    QSqlDatabase::removeDatabase(connectionName);

    if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
        emit accepted();
    } else {
        goToNextComic();
    }
}

void ComicVineDialog::toggleSkipButton()
{
    if (mode == ScraperMode::SingleComicInList)
        skipButton->setVisible(true);
    else
        skipButton->setHidden(true);
}

QString ComicVineDialog::volumeSearchStringFromComic(const ComicDB &comic)
{
    auto volume = comic.info.volume.toString().trimmed();
    if (!volume.isEmpty())
        return volume;

    auto series = comic.info.series.toString().trimmed();
    if (!series.isEmpty())
        return series;

    auto alternateSeries = comic.info.alternateSeries.toString().trimmed();
    if (!alternateSeries.isEmpty())
        return alternateSeries;

    // extract information from file name
    auto parentFolderName = comic.getParentFolderName();
    return parentFolderName;
}

void ComicVineDialog::goToNextComic()
{
    if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
        emit accepted();
        return;
    }

    currentIndex++;

    ComicDB comic = comics[currentIndex];

    QString title = comic.getTitleOrFileName();
    titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title));

    showSearchSingleComic(volumeSearchStringFromComic(comic));
}

void ComicVineDialog::clearState()
{
    selectVolumeWidget->clearFilter();
}

void ComicVineDialog::showLoading(const QString &message)
{
    content->setCurrentIndex(0);
    loadingMessage->setText(message);
    backButton->setHidden(true);
    skipButton->setHidden(true);
    nextButton->setHidden(true);
    searchButton->setHidden(true);
    closeButton->setVisible(true);
}

void ComicVineDialog::setLoadingMessage(const QString &message)
{
    loadingMessage->setText(message);
}

void ComicVineDialog::search()
{
    switch (mode) {
    case ScraperMode::Volume:
        launchSearchVolume();
        break;

    case ScraperMode::SingleComic:
    case ScraperMode::SingleComicInList:
        launchSearchComic();
        break;
    }
}

void ComicVineDialog::searchVolume(const VolumeSearchQuery &query)
{
    showLoading(tr("Looking for volume..."));

    currentVolumeSearchQuery = query;

    auto comicVineClient = new ComicVineClient;
    connect(comicVineClient, &ComicVineClient::searchResult, this, &ComicVineDialog::processClientResults);
    connect(comicVineClient, &ComicVineClient::timeOut, this, &ComicVineDialog::queryTimeOut);
    connect(comicVineClient, &ComicVineClient::finished, comicVineClient, &QObject::deleteLater);
    if (query.exactMatch) {
        status = ScraperStatus::SearchingExactVolume;
        comicVineClient->searchExactVolume(query.volume, query.page);
    } else {
        status = ScraperStatus::SearchingVolume;
        comicVineClient->search(query.volume, query.page);
    }
}

void ComicVineDialog::getVolumeComicsInfo(const QString &vID, int /* page */)
{
    showLoading(tr("Retrieving volume info..."));

    status = ScraperStatus::GettingVolumeComics;

    auto comicVineClient = new ComicVineClient;
    if (mode == ScraperMode::Volume)
        connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSortVolumeComics);
    else
        connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSelectComic);
    connect(comicVineClient, &ComicVineClient::timeOut, this, &ComicVineDialog::queryTimeOut);
    connect(comicVineClient, &ComicVineClient::finished, comicVineClient, &QObject::deleteLater);

    QLOG_TRACE() << vID;

    comicVineClient->getAllVolumeComicsInfo(vID);
}

// TODO: get the search configuration for exact match or not
void ComicVineDialog::launchSearchVolume()
{
    showLoading(tr("Looking for volume..."));
    // TODO: check if volume info is empty.

    QString volumeInfo = searchVolumeWidget->getVolumeInfo();
    bool exactMatch = searchVolumeWidget->getExactMatch();

    searchVolume({ volumeInfo, 1, exactMatch });
}

// TODO: get the search configuration for exact match or not
void ComicVineDialog::launchSearchComic()
{
    showLoading(tr("Looking for comic..."));

    QString volumeInfo = searchSingleComicWidget->getVolumeInfo();
    bool exactMatch = searchSingleComicWidget->getExactMatch();
    // QString comicInfo = searchSingleComicWidget->getComicInfo();
    // int comicNumber = searchSingleComicWidget->getComicNumber();

    // if(comicInfo.isEmpty() && comicNumber == -1)
    searchVolume({ volumeInfo, 1, exactMatch });
}
