#include "tray_ui_logic.h"

#include <QtTest/QtTest>

class TrayUILogicTest : public QObject {
    Q_OBJECT

private slots:
    void emptyControllerInputComparesAsDefault();
    void blankControllerResetDoesNotCountAsChangeWhenAlreadyOnDefault();
    void differentControllerCountsAsChange();
    void changedControllerOnRunningBackendNeedsLogoutPrompt();
    void changedControllerOnNeedsLoginDoesNotNeedLogoutPrompt();
    void enablingAdvertiseExitNeedsConfirmation();
    void disablingAdvertiseExitDoesNotNeedConfirmation();
    void authenticatedMenusVisibleForRunning();
    void authenticatedMenusVisibleForStopped();
    void authenticatedMenusHiddenForNeedsLogin();
    void loginActionShowsForNeedsMachineAuth();
    void reauthActionStaysHiddenForNeedsMachineAuth();
    void connectActionShowsForStoppedStartingAndRunning();
    void disconnectActionShowsForRunningAndStarting();
    void runningOnlyLaunchersEnableForRunning();
    void peersLauncherOnlyShowsWhenAuthenticatedAndNonEmpty();
    void runningBackendRequiresReloginPrompt();
    void noStateDoesNotRequireReloginPrompt();
    void accessDeniedErrorSuggestsOperatorGrant();
    void unrelatedErrorDoesNotSuggestOperatorGrant();
    void snapshotAuthURLIsReturnedWhenPresent();
    void snapshotAuthURLEmptyWhenMissing();
    void advertiseExitActionShowsRunningCopy();
    void advertiseExitActionShowsIdleCopy();
    void exitMenuTitleIncludesSelectedNode();
    void exitMenuTitleFallsBackToDefault();
    void exitListTitleMatchesAvailability();
    void syntheticNoExitChoiceIsRecognized();
    void submenuLauncherTextAddsArrow();
    void peersLauncherTextStaysStatic();
    void accountLauncherTextIncludesAccountSummary();
    void selfRootActionTextIncludesPrimaryIP();
    void visibleMenuDefersRefresh();
    void visibleSubmenuDefersRefresh();
    void hiddenMenuRefreshesImmediately();
    void rootHideDoesNotCloseVisibleSidePopup();
    void rootHideRespectsSuppressionGuard();
    void rootHideClosesWhenNothingVisible();
    void rootPopupPrefersAboveAnchorNearScreenBottom();
    void rootPopupClampsInsideRightEdge();
    void sidePopupPlacementLocksPreferredRightEdge();
    void sidePopupPlacementLocksPreferredLeftEdge();
    void sidePopupPlacementFallsBackWhenLockedEdgeNoLongerFits();
    void sidePopupUsesRightSideWhenSpaceAllows();
    void sidePopupFallsBackToLeftSideWhenNeeded();
    void sidePopupPinsToRightEdgeWhenRightSideHasMoreRoom();
    void sidePopupPinsToLeftEdgeWhenLeftSideHasMoreRoom();
    void sidePopupClampsVerticallyIntoScreen();
};

void TrayUILogicTest::emptyControllerInputComparesAsDefault()
{
    QCOMPARE(normalizedControllerForCompare(QStringLiteral("   "), QStringLiteral("https://sdp.mm.md")),
             QStringLiteral("https://sdp.mm.md"));
}

void TrayUILogicTest::blankControllerResetDoesNotCountAsChangeWhenAlreadyOnDefault()
{
    QVERIFY(!controllerInputChanged(QStringLiteral("https://sdp.mm.md"),
                                    QStringLiteral("   "),
                                    QStringLiteral("https://sdp.mm.md")));
}

void TrayUILogicTest::differentControllerCountsAsChange()
{
    QVERIFY(controllerInputChanged(QStringLiteral("https://sdp.mm.md"),
                                   QStringLiteral("corp-a"),
                                   QStringLiteral("https://sdp.mm.md")));
}

void TrayUILogicTest::changedControllerOnRunningBackendNeedsLogoutPrompt()
{
    QVERIFY(controllerSwitchNeedsLogoutPrompt(QStringLiteral("https://sdp.mm.md"),
                                              QStringLiteral("corp-a"),
                                              QStringLiteral("https://sdp.mm.md"),
                                              QStringLiteral("Running")));
}

void TrayUILogicTest::changedControllerOnNeedsLoginDoesNotNeedLogoutPrompt()
{
    QVERIFY(!controllerSwitchNeedsLogoutPrompt(QStringLiteral("https://sdp.mm.md"),
                                               QStringLiteral("corp-a"),
                                               QStringLiteral("https://sdp.mm.md"),
                                               QStringLiteral("NeedsLogin")));
}

void TrayUILogicTest::enablingAdvertiseExitNeedsConfirmation()
{
    QVERIFY(advertiseExitEnableNeedsConfirmation(true));
}

void TrayUILogicTest::disablingAdvertiseExitDoesNotNeedConfirmation()
{
    QVERIFY(!advertiseExitEnableNeedsConfirmation(false));
}

void TrayUILogicTest::authenticatedMenusVisibleForRunning()
{
    QVERIFY(shouldShowAuthenticatedMenus(QStringLiteral("Running")));
    QVERIFY(shouldShowAuthenticatedMenus(QStringLiteral("Starting")));
}

void TrayUILogicTest::authenticatedMenusVisibleForStopped()
{
    QVERIFY(shouldShowAuthenticatedMenus(QStringLiteral("Stopped")));
}

void TrayUILogicTest::authenticatedMenusHiddenForNeedsLogin()
{
    QVERIFY(!shouldShowAuthenticatedMenus(QStringLiteral("NeedsLogin")));
    QVERIFY(!shouldShowAuthenticatedMenus(QStringLiteral("NoState")));
    QVERIFY(!shouldShowAuthenticatedMenus(QStringLiteral("NeedsMachineAuth")));
}

void TrayUILogicTest::loginActionShowsForNeedsMachineAuth()
{
    QVERIFY(shouldShowLoginAction(QStringLiteral("NeedsMachineAuth")));
    QVERIFY(shouldShowLoginAction(QStringLiteral("NeedsLogin")));
    QVERIFY(!shouldShowLoginAction(QStringLiteral("Running")));
}

void TrayUILogicTest::reauthActionStaysHiddenForNeedsMachineAuth()
{
    QVERIFY(!shouldShowReauthAction(QStringLiteral("NeedsMachineAuth")));
    QVERIFY(shouldShowReauthAction(QStringLiteral("Stopped")));
    QVERIFY(shouldShowReauthAction(QStringLiteral("Running")));
}

void TrayUILogicTest::connectActionShowsForStoppedStartingAndRunning()
{
    QVERIFY(shouldShowConnectAction(QStringLiteral("Stopped")));
    QVERIFY(shouldShowConnectAction(QStringLiteral("Running")));
    QVERIFY(shouldShowConnectAction(QStringLiteral("Starting")));
    QVERIFY(!shouldShowConnectAction(QStringLiteral("NeedsLogin")));
}

void TrayUILogicTest::disconnectActionShowsForRunningAndStarting()
{
    QVERIFY(shouldShowDisconnectAction(QStringLiteral("Running")));
    QVERIFY(shouldShowDisconnectAction(QStringLiteral("Starting")));
    QVERIFY(!shouldShowDisconnectAction(QStringLiteral("Stopped")));
    QVERIFY(!shouldShowDisconnectAction(QStringLiteral("NeedsLogin")));
}

void TrayUILogicTest::runningOnlyLaunchersEnableForRunning()
{
    QVERIFY(shouldEnableRunningOnlyLauncher(QStringLiteral("Running")));
    QVERIFY(!shouldEnableRunningOnlyLauncher(QStringLiteral("Starting")));
    QVERIFY(!shouldEnableRunningOnlyLauncher(QStringLiteral("Stopped")));
    QVERIFY(!shouldEnableRunningOnlyLauncher(QStringLiteral("NeedsMachineAuth")));
}

void TrayUILogicTest::peersLauncherOnlyShowsWhenAuthenticatedAndNonEmpty()
{
    QVERIFY(shouldShowPeersLauncher(true, true));
    QVERIFY(!shouldShowPeersLauncher(true, false));
    QVERIFY(!shouldShowPeersLauncher(false, true));
}

void TrayUILogicTest::runningBackendRequiresReloginPrompt()
{
    QVERIFY(backendStateRequiresReloginPrompt(QStringLiteral("Running")));
    QVERIFY(backendStateRequiresReloginPrompt(QStringLiteral("Stopped")));
}

void TrayUILogicTest::noStateDoesNotRequireReloginPrompt()
{
    QVERIFY(!backendStateRequiresReloginPrompt(QStringLiteral("NoState")));
    QVERIFY(!backendStateRequiresReloginPrompt(QStringLiteral("NeedsLogin")));
}

void TrayUILogicTest::accessDeniedErrorSuggestsOperatorGrant()
{
    QVERIFY(errorSuggestsOperatorGrant(
        QStringLiteral("check prefs failed: Access denied: checkprefs access denied")));
}

void TrayUILogicTest::unrelatedErrorDoesNotSuggestOperatorGrant()
{
    QVERIFY(!errorSuggestsOperatorGrant(QStringLiteral("backend unavailable")));
}

void TrayUILogicTest::snapshotAuthURLIsReturnedWhenPresent()
{
    const QJsonObject snapshot{{QStringLiteral("authURL"), QStringLiteral("https://sdp.mm.md/a/login-flow")}};
    QCOMPARE(loginAuthURLFromSnapshot(snapshot), QStringLiteral("https://sdp.mm.md/a/login-flow"));
}

void TrayUILogicTest::snapshotAuthURLEmptyWhenMissing()
{
    const QJsonObject snapshot{{QStringLiteral("backendState"), QStringLiteral("NeedsLogin")}};
    QVERIFY(loginAuthURLFromSnapshot(snapshot).isEmpty());
}

void TrayUILogicTest::advertiseExitActionShowsRunningCopy()
{
    QCOMPARE(advertiseExitActionText(true), QStringLiteral("正用作出口节点"));
}

void TrayUILogicTest::advertiseExitActionShowsIdleCopy()
{
    QCOMPARE(advertiseExitActionText(false), QStringLiteral("用作出口节点…"));
}

void TrayUILogicTest::exitMenuTitleIncludesSelectedNode()
{
    QCOMPARE(exitMenuTitleText(QStringLiteral("edge-gw")), QStringLiteral("出口节点(edge-gw)"));
}

void TrayUILogicTest::exitMenuTitleFallsBackToDefault()
{
    QCOMPARE(exitMenuTitleText(QStringLiteral("不使用")), QStringLiteral("出口节点"));
    QCOMPARE(exitMenuTitleText(QString()), QStringLiteral("出口节点"));
}

void TrayUILogicTest::exitListTitleMatchesAvailability()
{
    QCOMPARE(exitListTitleText(true), QStringLiteral("出口节点"));
    QCOMPARE(exitListTitleText(false), QStringLiteral("无可用出口节点"));
}

void TrayUILogicTest::syntheticNoExitChoiceIsRecognized()
{
    QVERIFY(isSyntheticNoExitNodeChoice(QString(), QStringLiteral("不使用")));
    QVERIFY(!isSyntheticNoExitNodeChoice(QStringLiteral("node-123"), QStringLiteral("不使用")));
    QVERIFY(!isSyntheticNoExitNodeChoice(QString(), QStringLiteral("edge-gw")));
}

void TrayUILogicTest::submenuLauncherTextAddsArrow()
{
    QCOMPARE(submenuLauncherText(QStringLiteral("配置项")), QStringLiteral("配置项 ▸"));
}

void TrayUILogicTest::peersLauncherTextStaysStatic()
{
    QCOMPARE(peersLauncherText(), QStringLiteral("网内设备 ▸"));
}

void TrayUILogicTest::accountLauncherTextIncludesAccountSummary()
{
    QCOMPARE(accountLauncherText(QStringLiteral("admin@odata.cc")),
             QStringLiteral("账号: admin@odata.cc ▸"));
    QCOMPARE(accountLauncherText(QString()), QStringLiteral("账号 ▸"));
}

void TrayUILogicTest::selfRootActionTextIncludesPrimaryIP()
{
    QCOMPARE(selfRootActionText(QStringLiteral("hao-PC"), QStringLiteral("100.64.0.7/32")),
             QStringLiteral("本设备: hao-PC (100.64.0.7/32)"));
    QCOMPARE(selfRootActionText(QStringLiteral("hao-PC"), QString()),
             QStringLiteral("本设备: hao-PC"));
    QCOMPARE(selfRootActionText(QString(), QString()), QStringLiteral("本设备"));
}

void TrayUILogicTest::visibleMenuDefersRefresh()
{
    QVERIFY(shouldDeferTrayMenuRefresh(true, false));
}

void TrayUILogicTest::visibleSubmenuDefersRefresh()
{
    QVERIFY(shouldDeferTrayMenuRefresh(false, true));
}

void TrayUILogicTest::hiddenMenuRefreshesImmediately()
{
    QVERIFY(!shouldDeferTrayMenuRefresh(false, false));
}

void TrayUILogicTest::rootHideDoesNotCloseVisibleSidePopup()
{
    QVERIFY(!shouldCloseSidePopupAfterRootHide(false, true, false));
}

void TrayUILogicTest::rootHideRespectsSuppressionGuard()
{
    QVERIFY(!shouldCloseSidePopupAfterRootHide(false, false, true));
}

void TrayUILogicTest::rootHideClosesWhenNothingVisible()
{
    QVERIFY(shouldCloseSidePopupAfterRootHide(false, false, false));
}

void TrayUILogicTest::rootPopupPrefersAboveAnchorNearScreenBottom()
{
    const QPoint pos = rootPopupPosition(QPoint(1800, 1040), QSize(320, 420), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(1600, 620));
}

void TrayUILogicTest::rootPopupClampsInsideRightEdge()
{
    const QPoint pos = rootPopupPosition(QPoint(1910, 40), QSize(320, 420), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(1600, 40));
}

void TrayUILogicTest::sidePopupPlacementLocksPreferredRightEdge()
{
    const SidePopupPlacement placement =
        sidePopupPlacement(QRect(100, 200, 120, 32),
                           QSize(220, 260),
                           QRect(0, 0, 1920, 1080),
                           SidePopupEdge::Right);
    QCOMPARE(placement.edge, SidePopupEdge::Right);
    QCOMPARE(placement.position, QPoint(222, 200));
}

void TrayUILogicTest::sidePopupPlacementLocksPreferredLeftEdge()
{
    const SidePopupPlacement placement =
        sidePopupPlacement(QRect(1780, 200, 120, 32),
                           QSize(220, 260),
                           QRect(0, 0, 1920, 1080),
                           SidePopupEdge::Left);
    QCOMPARE(placement.edge, SidePopupEdge::Left);
    QCOMPARE(placement.position, QPoint(1558, 200));
}

void TrayUILogicTest::sidePopupPlacementFallsBackWhenLockedEdgeNoLongerFits()
{
    const SidePopupPlacement placement =
        sidePopupPlacement(QRect(100, 200, 120, 32),
                           QSize(220, 260),
                           QRect(0, 0, 320, 1080),
                           SidePopupEdge::Left);
    QCOMPARE(placement.edge, SidePopupEdge::Right);
    QCOMPARE(placement.position, QPoint(100, 200));
}

void TrayUILogicTest::sidePopupUsesRightSideWhenSpaceAllows()
{
    const QPoint pos = sidePopupPosition(QRect(100, 200, 120, 32), QSize(220, 260), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(222, 200));
}

void TrayUILogicTest::sidePopupFallsBackToLeftSideWhenNeeded()
{
    const QPoint pos = sidePopupPosition(QRect(1780, 200, 120, 32), QSize(220, 260), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(1558, 200));
}

void TrayUILogicTest::sidePopupPinsToRightEdgeWhenRightSideHasMoreRoom()
{
    const QPoint pos = sidePopupPosition(QRect(300, 200, 120, 32), QSize(1700, 260), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(220, 200));
}

void TrayUILogicTest::sidePopupPinsToLeftEdgeWhenLeftSideHasMoreRoom()
{
    const QPoint pos = sidePopupPosition(QRect(1600, 200, 120, 32), QSize(1700, 260), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(0, 200));
}

void TrayUILogicTest::sidePopupClampsVerticallyIntoScreen()
{
    const QPoint pos = sidePopupPosition(QRect(100, 980, 120, 32), QSize(220, 260), QRect(0, 0, 1920, 1080));
    QCOMPARE(pos, QPoint(222, 820));
}

QTEST_MAIN(TrayUILogicTest)

#include "tray_ui_logic_test.moc"
