/*
 * iDescriptor: A free and open-source idevice management tool.
 *
 * Copyright (C) 2025 Uncore <https://github.com/uncor3>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "toolboxwidget.h"
#include "airplaywidget.h"
#include "appcontext.h"
#include "cableinfowidget.h"
#include "devdiskimageswidget.h"
#include "devdiskmanager.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#ifndef __APPLE__
#include "ifusewidget.h"
#endif
#include "livescreenwidget.h"
#include "querymobilegestaltwidget.h"
#include "virtuallocationwidget.h"
#include "wirelessgalleryimportwidget.h"
#include <QApplication>
#include <QDebug>
#include <QMessageBox>
#include <QStyle>

#ifdef WIN32
#include <QGraphicsOpacityEffect>
#endif

struct iDescriptorToolWidget {
    iDescriptorTool tool;
    QString description;
    bool requiresDevice;
    QString iconName;
};

ToolboxWidget *ToolboxWidget::sharedInstance()
{
    static ToolboxWidget *instance = new ToolboxWidget();
    return instance;
}

ToolboxWidget::ToolboxWidget(QWidget *parent) : QWidget{parent}
{
    setupUI();
    updateDeviceList();
    updateToolboxStates();

    connect(AppContext::sharedInstance(), &AppContext::deviceChange, this,
            &ToolboxWidget::updateUI);
    connect(AppContext::sharedInstance(),
            &AppContext::currentDeviceSelectionChanged, this,
            &ToolboxWidget::onCurrentDeviceChanged);
}

void ToolboxWidget::setupUI()
{
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(0, 0, 0, 0);

    // Device selection section
    QHBoxLayout *deviceLayout = new QHBoxLayout();
    m_deviceLabel = new QLabel("Device:");
    m_deviceCombo = new QComboBox();
    m_deviceCombo->setMinimumWidth(200);

    deviceLayout->addWidget(m_deviceLabel);
    deviceLayout->addWidget(m_deviceCombo);
    deviceLayout->setContentsMargins(15, 5, 15, 5);
    deviceLayout->addStretch();

    mainLayout->addLayout(deviceLayout);

    // Scroll area for toolboxes
    m_scrollArea = new QScrollArea();
    m_scrollArea->setWidgetResizable(true);
    m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_scrollArea->setStyleSheet(
        "QScrollArea { background: transparent; border: none; }");
    m_scrollArea->viewport()->setStyleSheet("background: transparent;");

    m_contentWidget = new QWidget();
    QVBoxLayout *contentLayout = new QVBoxLayout(m_contentWidget);
    contentLayout->setSpacing(20);
    contentLayout->setContentsMargins(0, 0, 0, 0);

    // Main Tools Section
    QLabel *mainToolsLabel = new QLabel("Tools");
    mainToolsLabel->setStyleSheet(
        "font-weight: bold; font-size: 14px; margin-left: 10px");
    contentLayout->addWidget(mainToolsLabel);

    QWidget *mainToolsWidget = new QWidget();
    m_gridLayout = new QGridLayout(mainToolsWidget);
    m_gridLayout->setSpacing(10);

    QList<iDescriptorToolWidget> mainToolWidgets;
    mainToolWidgets.append(
        {iDescriptorTool::Airplayer, "Cast your device screen ", false, ""});
    mainToolWidgets.append({iDescriptorTool::VirtualLocation,
                            "Simulate GPS location on your device", true, ""});
    mainToolWidgets.append({iDescriptorTool::LiveScreen,
                            "View device screen in real-time", true, ""});
    mainToolWidgets.append({iDescriptorTool::QueryMobileGestalt,
                            "Query device hardware information", true, ""});
    mainToolWidgets.append({iDescriptorTool::DeveloperDiskImages,
                            "Manage developer disk images", false, ""});
    mainToolWidgets.append(
        {iDescriptorTool::WirelessGalleryImport,
         "Import photos wirelessly to your iDevice (requires Shortcuts app)",
         false, ""});
#ifndef __APPLE__
    mainToolWidgets.append({iDescriptorTool::iFuse,
                            "Mount your iPhone's filesystem on your PC", true,
                            ""});
#endif
    mainToolWidgets.append({iDescriptorTool::CableInfoWidget,
                            "View detailed cable and connection info", true,
                            ""});
    mainToolWidgets.append({iDescriptorTool::NetworkDevices,
                            "Discover and monitor devices on your network",
                            false, ""});

    for (int i = 0; i < mainToolWidgets.size(); ++i) {
        const auto &tool = mainToolWidgets[i];
        ClickableWidget *toolbox =
            createToolbox(tool.tool, tool.description, tool.requiresDevice);
        int row = i / 3;
        int col = i % 3;
        m_gridLayout->addWidget(toolbox, row, col);
    }

    contentLayout->addWidget(mainToolsWidget);

    // More Tools Section
    QLabel *moreToolsLabel = new QLabel("More Tools");
    moreToolsLabel->setStyleSheet(
        "font-weight: bold; font-size: 14px; margin-left: 10px");
    contentLayout->addWidget(moreToolsLabel);

    QWidget *moreToolsWidget = new QWidget();
    QGridLayout *moreGridLayout = new QGridLayout(moreToolsWidget);
    moreGridLayout->setSpacing(10);

    QList<iDescriptorToolWidget> moreToolWidgets;
    moreToolWidgets.append(
        {iDescriptorTool::MountDevImage,
         "Mount a compatible device image with a single click", true, ""});
    moreToolWidgets.append(
        {iDescriptorTool::Restart, "Restart device services", true, ""});
    moreToolWidgets.append(
        {iDescriptorTool::Shutdown, "Shut down the device", true, ""});
    moreToolWidgets.append({iDescriptorTool::RecoveryMode,
                            "Enter device recovery mode", true, ""});
    moreToolWidgets.append({iDescriptorTool::EnableWifiConnections,
                            "Make device connectable via Wi-Fi", true, ""});

    for (int i = 0; i < moreToolWidgets.size(); ++i) {
        const auto &tool = moreToolWidgets[i];
        ClickableWidget *toolbox =
            createToolbox(tool.tool, tool.description, tool.requiresDevice);
        int row = i / 3;
        int col = i % 3;
        moreGridLayout->addWidget(toolbox, row, col);
    }

    contentLayout->addWidget(moreToolsWidget);
    contentLayout->addStretch();

    m_scrollArea->setWidget(m_contentWidget);
    mainLayout->addWidget(m_scrollArea);

    connect(m_deviceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &ToolboxWidget::onDeviceSelectionChanged);
}

ToolboxItemWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
                                                const QString &description,
                                                bool requiresDevice)
{
    QString title;
    QString iconName;
    bool iconThemable = true;
    switch (tool) {
    case iDescriptorTool::Airplayer:
        title = "Airplayer";
        iconName = ":/resources/icons/MaterialSymbolsLightAirplayOutline.png";
        break;
    case iDescriptorTool::LiveScreen:
        title = "Live Screen";
        iconName = ":/resources/icons/PepiconsPrintCellphoneEye.png";
        break;
    case iDescriptorTool::MountDevImage:
        title = "Mount Dev Image";
        iconName = ":/resources/icons/MdiDisk.png";
        break;
    case iDescriptorTool::VirtualLocation:
        title = "Virtual Location";
        iconName = ":/resources/icons/MaterialSymbolsLocationOnOutline.png";
        break;
    case iDescriptorTool::Restart:
        title = "Restart";
        iconName = ":/resources/icons/IcTwotoneRestartAlt.png";
        break;
    case iDescriptorTool::Shutdown:
        title = "Shutdown";
        iconName = ":/resources/icons/IcOutlinePowerSettingsNew.png";
        break;
    case iDescriptorTool::RecoveryMode:
        title = "Recovery Mode";
        iconName = ":/resources/icons/HugeiconsWrench01.png";
        break;
    case iDescriptorTool::QueryMobileGestalt:
        title = "Query Mobile Gestalt";
        iconName = ":/resources/icons/"
                   "StreamlineProgrammingBrowserSearchSearchWindowGlassAppCod"
                   "eProgrammingQueryFindMagnifyingApps.png";
        break;
    case iDescriptorTool::DeveloperDiskImages:
        title = "Dev Disk Images";
        iconName = ":/resources/icons/TablerDatabaseExport.png";
        break;
    case iDescriptorTool::WirelessGalleryImport:
        title = "Wireless Gallery Import";
        iconName = ":/resources/icons/MaterialSymbolsAndroidWifi3BarPlus.png";
        break;
    case iDescriptorTool::iFuse:
        title = "iFuse Mount";
        iconName = ":/resources/icons/fuse.png";
        iconThemable = false;
        break;
    case iDescriptorTool::CableInfoWidget:
        title = "Cable Info";
        iconName = ":/resources/icons/MaterialSymbolsLightCableRounded.png";
        break;
    case iDescriptorTool::NetworkDevices:
        title = "Network Devices";
        iconName =
            ":/resources/icons/StreamlineUltimateMultipleUsersNetwork.png";
        break;
    case iDescriptorTool::EnableWifiConnections:
        title = "Enable Wi-Fi Connections";
        iconName =
            ":/resources/icons/StreamlineFreehandChargingFlashWireless.png";
        break;
    default:
        title = "Unknown Tool";
        break;
    }

    ToolboxItemWidget *b = new ToolboxItemWidget(
        tool, description, iconName, title, requiresDevice, iconThemable);

    m_toolboxes.append(b);
    b->setProperty("requiresDevice", requiresDevice);
    connect(b, &ToolboxItemWidget::clicked, [this, tool, requiresDevice]() {
        onToolboxClicked(tool, requiresDevice);
    });
    return b;
}

void ToolboxWidget::updateDeviceList()
{
    m_deviceCombo->blockSignals(true);
    m_deviceCombo->clear();

    QList<std::shared_ptr<iDescriptorDevice>> devices =
        AppContext::sharedInstance()->getAllDevices();

    if (devices.isEmpty()) {
        m_deviceCombo->addItem("No device connected");
        m_deviceCombo->setEnabled(false);
        m_uuid.clear();
    } else {
        m_deviceCombo->setEnabled(true);
        for (const std::shared_ptr<iDescriptorDevice> device : devices) {
            QString shortUdid = device->udid.left(8) + "...";
            m_deviceCombo->addItem(
                QString::fromStdString(device->deviceInfo.productType) + " / " +
                    shortUdid +
                    (device->deviceInfo.isWireless ? " (Wi-Fi)" : ""),
                device->udid);
        }
    }

    onCurrentDeviceChanged(
        AppContext::sharedInstance()->getCurrentDeviceSelection());

    m_deviceCombo->blockSignals(false);

    if (m_deviceCombo->count() > 0 && m_deviceCombo->currentIndex() >= 0) {
        QString currentUdid = m_deviceCombo->currentData().toString();
        if (!currentUdid.isEmpty()) {
            m_uuid = currentUdid;
            qDebug() << "[toolboxwidget] Initialized m_uuid to:" << currentUdid;
        }
    }
}

void ToolboxWidget::updateToolboxStates()
{
    bool hasDevice = !AppContext::sharedInstance()->getAllDevices().isEmpty();

    for (int i = 0; i < m_toolboxes.size(); ++i) {
        ToolboxItemWidget *toolbox = m_toolboxes[i];
        bool requiresDevice = toolbox->property("requiresDevice").toBool();

        bool enabled = !requiresDevice || hasDevice;
        toolbox->updateStyles(enabled);
    }
}

void ToolboxWidget::updateUI()
{
    updateDeviceList();
    updateToolboxStates();
}

void ToolboxWidget::onDeviceSelectionChanged()
{
    QString selectedUdid = m_deviceCombo->currentData().toString();

    // Clear m_uuid if no valid selection
    if (selectedUdid.isEmpty()) {
        m_uuid.clear();
        return;
    }

    if (AppContext::sharedInstance()->getDevice(selectedUdid) == nullptr) {
        QMessageBox::warning(this, "Device Not Found",
                             "The selected device is no longer connected.");
        m_uuid.clear(); // Clear stale UUID
        updateDeviceList();
        return;
    }

    m_uuid = selectedUdid;
    qDebug() << "[toolboxwidget] Selected device UDID:" << selectedUdid;
    // Update the selected device in main menu
    AppContext::sharedInstance()->setCurrentDeviceSelection(
        DeviceSelection(selectedUdid));
}

void ToolboxWidget::onCurrentDeviceChanged(const DeviceSelection &selection)
{
    if (selection.valid() && selection.type == DeviceSelection::Normal) {
        int index = m_deviceCombo->findData(selection.udid);
        if (index != -1) {
            // Block signals to prevent recursive calls when we update the UI
            m_deviceCombo->blockSignals(true);
            m_deviceCombo->setCurrentIndex(index);
            m_deviceCombo->blockSignals(false);

            m_uuid = selection.udid;
        }
    } else {
        // Clear m_uuid when selection is invalid
        m_uuid.clear();
    }
}

void ToolboxWidget::onToolboxClicked(iDescriptorTool tool, bool requiresDevice)
{
    // final check to make sure device is connected if required
    auto device = AppContext::sharedInstance()->getDevice(m_uuid);
    if (!device && requiresDevice) {
        QMessageBox::warning(
            this, "Device Disconnected ?",
            "Device just disconnected, please select a device.");
        return;
    }

    qDebug() << "idevice exists:" << (device != nullptr) << m_uuid;
    switch (tool) {
    case iDescriptorTool::Airplayer: {
        if (!m_airplayWidget) {
            m_airplayWidget = new AirPlayWidget();
            connect(m_airplayWidget, &QObject::destroyed, this,
                    [this]() { m_airplayWidget = nullptr; });
            m_airplayWidget->setAttribute(Qt::WA_DeleteOnClose);
            m_airplayWidget->setWindowFlag(Qt::Window);
            m_airplayWidget->resize(400, 300);
            m_airplayWidget->show();
        } else {
            m_airplayWidget->raise();
            m_airplayWidget->activateWindow();
        }
    } break;

    case iDescriptorTool::LiveScreen: {
        LiveScreenWidget *liveScreen = new LiveScreenWidget(device);
        liveScreen->show();
    } break;
    case iDescriptorTool::RecoveryMode: {
        enterRecoveryMode(device);
    } break;
    case iDescriptorTool::MountDevImage: {

        if (!DevDiskImageHelper::canMountForDevice(device)) {
            QMessageBox::warning(this, "Unsupported iOS Version",
                                 "You don't need to mount a developer disk "
                                 "image on devices running iOS 17 or later");
            return;
        }
        DevDiskImageHelper *devDiskImageHelper =
            new DevDiskImageHelper(device, this);

        connect(devDiskImageHelper, &DevDiskImageHelper::mountingCompleted,
                this, [this, devDiskImageHelper](bool success) {
                    if (success) {
                        QMessageBox::information(
                            this, "Success",
                            "Developer image mounted successfully.");
                    } else {
                        QMessageBox::warning(
                            this, "Failure",
                            "Failed to mount developer image.");
                    }
                });
        devDiskImageHelper->start();
    } break;
    case iDescriptorTool::VirtualLocation: {
        VirtualLocation *virtualLocation = new VirtualLocation(device);
        virtualLocation->setAttribute(Qt::WA_DeleteOnClose);
        virtualLocation->show();
    } break;
    case iDescriptorTool::Restart: {
        restartDevice(device);
    } break;
    case iDescriptorTool::Shutdown: {
        shutdownDevice(device);
    } break;
    case iDescriptorTool::QueryMobileGestalt: {
        if (!QueryMobileGestaltWidget::canOpenForDevice(device)) {
            QMessageBox::warning(this, "Unsupported iOS Version",
                                 "Apple deprecated this protocol for Devices "
                                 "running iOS 17 or later");
            return;
        }

        QueryMobileGestaltWidget *queryMobileGestaltWidget =
            new QueryMobileGestaltWidget(device);
        queryMobileGestaltWidget->setAttribute(Qt::WA_DeleteOnClose);
        queryMobileGestaltWidget->show();
    } break;
    case iDescriptorTool::DeveloperDiskImages: {
        if (!m_devDiskImagesWidget) {
            m_devDiskImagesWidget =
                new DevDiskImagesWidget(device ? device->udid : QString());
            m_devDiskImagesWidget->setAttribute(Qt::WA_DeleteOnClose);
            connect(m_devDiskImagesWidget, &QObject::destroyed, this,
                    [this]() { m_devDiskImagesWidget = nullptr; });
            m_devDiskImagesWidget->show();
        } else {
            m_devDiskImagesWidget->raise();
            m_devDiskImagesWidget->activateWindow();
        }
    } break;
    case iDescriptorTool::WirelessGalleryImport: {
        if (!m_wirelessGalleryImportWidget) {
            m_wirelessGalleryImportWidget = new WirelessGalleryImportWidget();
            connect(m_wirelessGalleryImportWidget, &QObject::destroyed, this,
                    [this]() { m_wirelessGalleryImportWidget = nullptr; });
            m_wirelessGalleryImportWidget->setAttribute(Qt::WA_DeleteOnClose);
            m_wirelessGalleryImportWidget->show();
        } else {
            m_wirelessGalleryImportWidget->raise();
            m_wirelessGalleryImportWidget->activateWindow();
        }
    } break;
#ifndef __APPLE__
    case iDescriptorTool::iFuse: {
        bool canOpen = iFuseWidget::canOpenForDevice(device);
        if (!canOpen) {
            QMessageBox::warning(this, "Unsupported Device",
                                 "iFuse mounting is not supported for wireless "
                                 "devices.");
            return;
        }

        if (!m_ifuseWidget) {
            m_ifuseWidget = new iFuseWidget(device);
            m_ifuseWidget->setAttribute(Qt::WA_DeleteOnClose);
            connect(m_ifuseWidget, &QObject::destroyed, this,
                    [this]() { m_ifuseWidget = nullptr; });
            m_ifuseWidget->setWindowFlag(Qt::Window);
            m_ifuseWidget->resize(600, 400);
            m_ifuseWidget->show();
        } else {
            m_ifuseWidget->raise();
            m_ifuseWidget->activateWindow();
        }
    } break;
#endif
    case iDescriptorTool::CableInfoWidget: {
        CableInfoWidget *cableInfoWidget = new CableInfoWidget(device);
        cableInfoWidget->show();
    } break;
    case iDescriptorTool::NetworkDevices: {
        if (!m_networkDevicesWidget) {
            m_networkDevicesWidget = new NetworkDevicesWidget();
            m_networkDevicesWidget->setAttribute(Qt::WA_DeleteOnClose);
            connect(m_networkDevicesWidget, &QObject::destroyed, this,
                    [this]() { m_networkDevicesWidget = nullptr; });
            m_networkDevicesWidget->show();
        } else {
            m_networkDevicesWidget->raise();
            m_networkDevicesWidget->activateWindow();
        }
    } break;

    case iDescriptorTool::EnableWifiConnections: {
        connect(
            device->service_manager,
            &CXX::ServiceManager::enable_wifi_connections_result, this,
            [this](bool success) {
                if (success) {
                    QMessageBox::information(
                        this, "Success",
                        "Wi-Fi connections enabled successfully. You can now "
                        "connect to this device wirelessly.");
                } else {
                    QMessageBox::warning(this, "Failure",
                                         "Failed to enable Wi-Fi connections.");
                }
            },
            Qt::SingleShotConnection);

        device->service_manager->enable_wifi_connections();
    } break;
    default:
        qDebug() << "Clicked on unimplemented tool";
        break;
    }
}

void ToolboxWidget::restartDevice(
    const std::shared_ptr<iDescriptorDevice> device)
{
    QMessageBox msgBox;
    msgBox.setWindowTitle("Restart Device");
    msgBox.setText("Are you sure you want to restart the device?");
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    msgBox.setDefaultButton(QMessageBox::No);
    int ret = msgBox.exec();
    if (ret != QMessageBox::Yes)
        return;

    bool res = device->service_manager->restart();

    if (res) {
        QMessageBox::information(nullptr, "Restart Initiated",
                                 "Device will restart shortly...");
        qDebug() << "Restarting device";
    } else {
        QMessageBox::warning(nullptr, "Restart Failed",
                             "Failed to restart device.");
    }
}

void ToolboxWidget::shutdownDevice(
    const std::shared_ptr<iDescriptorDevice> device)
{

    QMessageBox msgBox;
    msgBox.setWindowTitle("Shutdown Device");
    msgBox.setText("Are you sure you want to shutdown the device?");
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    msgBox.setDefaultButton(QMessageBox::No);

    int ret = msgBox.exec();
    if (ret != QMessageBox::Yes)
        return;

    bool res = device->service_manager->shutdown();

    if (res) {
        QMessageBox::information(nullptr, "Shutdown Initiated",
                                 "Device will shutdown shortly..");
        qDebug() << "Shutting down device";
    } else {
        QMessageBox::warning(nullptr, "Shutdown Failed",
                             "Failed to shutdown device.");
    }
}

void ToolboxWidget::enterRecoveryMode(
    const std::shared_ptr<iDescriptorDevice> device)
{
    QMessageBox msgBox;
    msgBox.setWindowTitle("Enter Recovery Mode");
    msgBox.setText("Are you sure you want to enter recovery mode?");
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    msgBox.setDefaultButton(QMessageBox::No);

    int ret = msgBox.exec();
    if (ret != QMessageBox::Yes)
        return;

    bool res = device->service_manager->enter_recovery_mode();

    if (res) {
        QMessageBox::information(nullptr, "Enter Recovery Mode Initiated",
                                 "Device will enter recovery mode.");
        qDebug() << "Entering recovery mode";
    } else {
        QMessageBox::warning(nullptr, "Enter Recovery Mode Failed",
                             "Failed to enter recovery mode.");
    }
}

void ToolboxWidget::restartAirPlayWidget()
{
    if (!m_airplayWidget) {
        onToolboxClicked(iDescriptorTool::Airplayer, false);
        return;
    }

    connect(
        m_airplayWidget, &QObject::destroyed, this,
        [this]() {
            m_airplayWidget = nullptr;
            // give some time for cleanup
            QTimer::singleShot(100, this, [this]() {
                onToolboxClicked(iDescriptorTool::Airplayer, false);
            });
        },
        Qt::SingleShotConnection);

    m_airplayWidget->close();
}