#include "ProxySettings.h"

#include "DialogOpener.h"
#include "megaapi.h"
#include "ServiceUrls.h"
#include "ui_ProxySettings.h"

#include <QNetworkProxy>

using namespace mega;

ProxySettings::ProxySettings(MegaApplication* app, QWidget* parent):
    QDialog(parent),
    mUi(new Ui::ProxySettings),
    mApp(app),
    mPreferences(Preferences::instance()),
    mConnectivityChecker(new ConnectivityChecker(ServiceUrls::getProxyTestUrl())),
    mProgressDialog(nullptr)
{
    mUi->setupUi(this);

    mUi->eProxyPort->setValidator(new QIntValidator(0, std::numeric_limits<uint16_t>::max(), this));

    mUi->wErrorBanner->setType(BannerWidget::Type::BANNER_ERROR);

    initialize();

    connect(mConnectivityChecker, &ConnectivityChecker::testFinished,
            this, &ProxySettings::onProxyTestFinished);

    connect(mUi->rProxyManual, &QRadioButton::clicked, this, [this]{setManualMode(true);});
    connect(mUi->cProxyRequiresPassword,
            &QCheckBox::toggled,
            this,
            [this](bool checked)
            {
                setManualMode(true);
                if (checked)
                {
                    mUi->eProxyUsername->setFocus();
                }
            });
    connect(mUi->rNoProxy,
            &QRadioButton::clicked,
            this,
            [this]
            {
                setManualMode(false);
            });
    connect(mUi->rProxyAuto,
            &QRadioButton::clicked,
            this,
            [this]
            {
                setManualMode(false);
#ifdef Q_OS_LINUX
                std::unique_ptr<MegaProxy> proxySettings(
                    mApp->getMegaApi()->getAutoProxySettings());
                if (proxySettings->getProxyType() != MegaProxy::PROXY_CUSTOM)
                {
                    auto errorMessage =
                        tr("Your system doesn’t have a proxy set. To connect, set a valid "
                           "http_proxy or https_proxy value in your environment.");
                    mUi->wErrorBanner->setTitle(errorMessage);
                    mUi->wErrorBanner->setVisible(true);
                }
                else
                {
                    mUi->wErrorBanner->setTitle(QString());
                    mUi->wErrorBanner->setVisible(false);
                }
#endif
            });
}

ProxySettings::~ProxySettings()
{
    delete mConnectivityChecker;
    delete mUi;
}

void ProxySettings::initialize()
{
    mUi->rNoProxy->setChecked(mPreferences->proxyType() == Preferences::PROXY_TYPE_NONE);
    mUi->rProxyAuto->setChecked(mPreferences->proxyType() == Preferences::PROXY_TYPE_AUTO);
    mUi->rProxyManual->setChecked(mPreferences->proxyType() == Preferences::PROXY_TYPE_CUSTOM);
    mUi->cProxyType->setCurrentIndex(mPreferences->proxyProtocol());
    mUi->eProxyServer->setText(mPreferences->proxyServer());
    mUi->eProxyPort->setText(QString::number(mPreferences->proxyPort()));

    mUi->cProxyRequiresPassword->setChecked(mPreferences->proxyRequiresAuth());
    mUi->eProxyUsername->setText(mPreferences->getProxyUsername());
    mUi->eProxyPassword->setText(mPreferences->getProxyPassword());

    setManualMode(mUi->rProxyManual->isChecked());
}

void ProxySettings::setManualMode(bool enabled)
{
    mUi->cProxyType->setEnabled(enabled);
    mUi->lProxyType->setEnabled(enabled);
    mUi->eProxyServer->setEnabled(enabled);
    mUi->lProxyServer->setEnabled(enabled);
    mUi->eProxyPort->setEnabled(enabled);
    mUi->lProxyPortSeparator->setEnabled(enabled);
    mUi->cProxyRequiresPassword->setEnabled(enabled);
    mUi->wErrorBanner->setTitle(QString());
    mUi->wErrorBanner->setVisible(false);
    if (mUi->cProxyRequiresPassword->isEnabled())
    {
        mUi->eProxyUsername->setEnabled(mUi->cProxyRequiresPassword->isChecked());
        mUi->eProxyPassword->setEnabled(mUi->cProxyRequiresPassword->isChecked());
    }
    else
    {
        mUi->eProxyUsername->setEnabled(false);
        mUi->eProxyPassword->setEnabled(false);
    }
    mUi->lProxyUsername->setEnabled(mUi->eProxyUsername->isEnabled());
    mUi->lProxyPassword->setEnabled(mUi->eProxyPassword->isEnabled());
}

void ProxySettings::onProxyTestFinished(bool success)
{
    if(success)
    {
        MegaApi::log(MegaApi::LOG_LEVEL_INFO, "Proxy test OK");

        if (mUi->rNoProxy->isChecked())
        {
            mPreferences->setProxyType(Preferences::PROXY_TYPE_NONE);
        }
        else if (mUi->rProxyAuto->isChecked())
        {
            mPreferences->setProxyType(Preferences::PROXY_TYPE_AUTO);
        }
        else if (mUi->rProxyManual->isChecked())
        {
            mPreferences->setProxyType(Preferences::PROXY_TYPE_CUSTOM);
        }

        mPreferences->setProxyProtocol(mUi->cProxyType->currentIndex());
        mPreferences->setProxyServer(mUi->eProxyServer->text().trimmed());
        mPreferences->setProxyPort(mUi->eProxyPort->text().toUShort());
        mPreferences->setProxyRequiresAuth(mUi->cProxyRequiresPassword->isChecked());
        mPreferences->setProxyUsername(mUi->eProxyUsername->text());
        mPreferences->setProxyPassword(mUi->eProxyPassword->text());

        if(mProgressDialog)
        {
            mProgressDialog->close();
        }

        accept();
    }
    else
    {
        MegaApi::log(MegaApi::LOG_LEVEL_WARNING, "Proxy test failed");
        if(mProgressDialog)
        {
            mProgressDialog->close();
        }
        mUi->wErrorBanner->setTitle(tr("We couldn’t connect using your proxy settings. Check your "
                                       "proxy details or try a different network."));
        mUi->wErrorBanner->setVisible(true);
        adjustSize();
    }
}


void ProxySettings::on_bUpdate_clicked()
{
    QNetworkProxy proxy;
    proxy.setType(QNetworkProxy::NoProxy);
    if (mUi->rProxyManual->isChecked())
    {
        switch(mUi->cProxyType->currentIndex())
        {
        case Preferences::PROXY_PROTOCOL_SOCKS5H:
            proxy.setType(QNetworkProxy::Socks5Proxy);
            break;
        default:
            proxy.setType(QNetworkProxy::HttpProxy);
            break;
        }

        proxy.setHostName(mUi->eProxyServer->text().trimmed());
        proxy.setPort(mUi->eProxyPort->text().trimmed().toUShort());
        if (mUi->cProxyRequiresPassword->isChecked())
        {
            proxy.setUser(mUi->eProxyUsername->text());
            proxy.setPassword(mUi->eProxyPassword->text());
        }
    }
    else if (mUi->rProxyAuto->isChecked())
    {
        std::unique_ptr<MegaProxy> proxySettings(mApp->getMegaApi()->getAutoProxySettings());
        if (proxySettings->getProxyType() == MegaProxy::PROXY_CUSTOM)
        {
            std::string sProxyURL = proxySettings->getProxyURL();
            QString proxyURL = QString::fromUtf8(sProxyURL.data());

            QStringList parts = proxyURL.split(QString::fromUtf8("://"));
            if (parts.size() == 2 && parts[0].startsWith(QString::fromUtf8("socks")))
            {
                proxy.setType(QNetworkProxy::Socks5Proxy);
            }
            else
            {
                proxy.setType(QNetworkProxy::HttpProxy);
            }

            QStringList arguments = parts[parts.size() - 1].split(QString::fromUtf8(":"));
            if (arguments.size() == 2)
            {
                proxy.setHostName(arguments[0]);
                proxy.setPort(arguments[1].toUShort());
            }
        }
#ifdef Q_OS_LINUX
        else
        {
            auto errorMessage = tr("Your system doesn’t have a proxy set. To connect, set a valid "
                                   "http_proxy or https_proxy value in your environment.");
            mUi->wErrorBanner->setTitle(errorMessage);
            mUi->wErrorBanner->setVisible(true);
            return;
        }
#endif
    }

    mProgressDialog = new MegaProgressCustomDialog(this);
    mProgressDialog->setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
    DialogOpener::showDialog(mProgressDialog);

    mConnectivityChecker->setProxy(proxy);
    mConnectivityChecker->setTestString(Preferences::PROXY_TEST_SUBSTRING);
    mConnectivityChecker->setTimeout(Preferences::PROXY_TEST_TIMEOUT_MS);
    mConnectivityChecker->startCheck();
    MegaApi::log(MegaApi::LOG_LEVEL_INFO, "Testing proxy settings...");
}

void ProxySettings::on_bCancel_clicked()
{
    reject();
}
