import Foundation
import Testing

extension RootTabsSourceGuardTests {
    static func assertGatewaySettingsSurfaceGuards() throws {
        let settingsSource = try String(contentsOf: self.settingsProTabSourceURL(), encoding: .utf8)
        let sectionsSource = try String(contentsOf: self.settingsProTabSectionsSourceURL(), encoding: .utf8)
        let actionsSource = try String(contentsOf: self.settingsProTabActionsSourceURL(), encoding: .utf8)
        let trustSource = try String(contentsOf: self.gatewayTrustPromptAlertSourceURL(), encoding: .utf8)
        let controllerSource = try self.gatewayConnectionControllerSource()
        let rootSource = try String(contentsOf: self.rootTabsSourceURL(), encoding: .utf8)
        let scannerSource = try String(contentsOf: self.qrScannerSourceURL(), encoding: .utf8)
        let settingsScannerSheet = try self.extract(
            settingsSource,
            from: "isPresented: self.$showQRScanner,",
            to: ".sheet(isPresented: self.$showNotificationRelayDisclosure)")
        let settingsOnDismiss = try #require(settingsScannerSheet.range(of: "onDismiss: {"))
        let settingsProcessing = try #require(settingsScannerSheet.range(of: "self.processQueuedScannerResult()"))
        let settingsContent = try #require(settingsScannerSheet.range(of: "content: {"))
        let settingsPendingSetupHandler = try self.extract(
            actionsSource,
            from: "func applyGatewaySetupLink(_ link: GatewayConnectDeepLink)",
            to: "@discardableResult\n    func applySetupCode(attemptID: UUID)")
        let settingsScannerCancel = try #require(
            settingsPendingSetupHandler.range(of: "self.scannerResultHandoff.cancel()"))
        let settingsSetupStaging = try #require(
            settingsPendingSetupHandler.range(of: "self.stagedGatewaySetupLink = link"))
        let scannerMake = try self.extract(
            scannerSource,
            from: "func makeUIViewController",
            to: "func updateUIViewController")
        let scannerLifecycle = try self.extract(
            scannerSource,
            from: "final class QRScannerContainerViewController",
            to: "final class Coordinator")
        let scannerDelivery = try self.extract(
            scannerSource,
            from: "private func deliver(_ result: QRScannerResult",
            to: "func dataScanner(_: DataScannerViewController, didRemove")
        let stopScanning = try #require(scannerDelivery.range(of: "scanner.stopScanning()"))
        let deliverResult = try #require(scannerDelivery.range(of: "self.parent.onResult(result)"))
        let activeProblemToast = try self.extract(
            rootSource,
            from: "private var activeGatewayProblemToast: GatewayConnectionProblem?",
            to: "private var gatewayToastAnimation: Animation?")
        let gatewaySetupSource = try self.extract(
            rootSource,
            from: "private func maybeOpenSettingsForGatewaySetup()",
            to: "private func maybeRequestLocalNetworkAccess")
        let consumedGatewaySetup = try #require(
            gatewaySetupSource.range(of: "appModel.consumePendingGatewaySetupLink()"))
        let onboardingSetupOwnerGuard = try #require(
            gatewaySetupSource.range(of: "guard !self.showOnboarding else { return }"))
        let deliveredGatewaySetup = try #require(
            gatewaySetupSource.range(of: "self.gatewaySetupRequest = GatewaySetupRequest"))

        #expect(scannerSource.contains("static let defaultSettlingNanoseconds: UInt64 = 1_200_000_000"))
        #expect(scannerSource.contains("QRScannerContainerViewController(coordinator: context.coordinator)"))
        #expect(!scannerMake.contains("startScanning()"))
        #expect(scannerLifecycle.contains("override func viewDidAppear"))
        #expect(scannerLifecycle.contains("try self.scanner.startScanning()"))
        #expect(scannerLifecycle.contains("override func viewWillDisappear"))
        #expect(scannerLifecycle.contains("self.stopScannerCapture()"))

        #expect(sectionsSource.contains("var gatewayDestination: some View"))
        #expect(sectionsSource.contains("This phone has limited Gateway access."))
        #expect(sectionsSource.contains("Use a secure wss:// or Tailscale Serve Gateway"))
        #expect(sectionsSource.contains("Label(\"Scan Full-Access Code\""))
        #expect(sectionsSource.contains("self.gatewayActions"))
        #expect(sectionsSource.contains("self.manualGatewayCard"))
        #expect(sectionsSource.contains("self.gatewaySetupCard"))
        #expect(sectionsSource.contains("self.gatewayAdvancedCard"))
        // Pairing stays reachable without scrolling: nav-bar scanner button on the
        // gateway route plus a status-card hero while nothing is paired. The hero
        // honors the same connect lock as the other scanner entry points.
        #expect(sectionsSource.contains("if route == .gateway {"))
        #expect(sectionsSource.contains(
            "let showScanHero = self.gatewayNeedsPairing && self.connectingGateway == nil"))
        #expect(sectionsSource.contains("actionTitle: showScanHero ? \"Scan QR to Pair\" : nil"))
        #expect(sectionsSource.contains("title: \"Reconnect\""))
        #expect(sectionsSource.contains("Task { await self.reconnectGateway() }"))
        #expect(sectionsSource.contains("title: \"Diagnose\""))
        #expect(sectionsSource.contains("Task { await self.runDiagnostics() }"))
        #expect(sectionsSource.contains("title: \"Scan QR\""))
        #expect(sectionsSource.contains("self.openGatewayQRScanner()"))
        #expect(sectionsSource.contains("title: \"Connect\""))
        #expect(sectionsSource.contains("Task { await self.applySetupCodeAndConnect() }"))
        #expect(sectionsSource.contains("Task { await self.connect(gateway) }"))
        #expect(sectionsSource.contains("tailnetWarningText"))
        // Gateway problems surface once, as the root toast; the settings page must not
        // embed a second copy of the banner.
        #expect(!sectionsSource.contains("GatewayProblemBanner("))
        // Sections compare gateway owners byte-exact, not with raw string equality.
        #expect(!sectionsSource.contains("entry.stableID == self.gatewayRegistry.activeStableID"))
        #expect(sectionsSource.components(separatedBy: "GatewayStableIdentifier.matches(").count >= 4)
        #expect(rootSource.contains("GatewayProblemBanner("))
        #expect(rootSource.contains(".gesture(self.gatewayToastSwipeGesture)"))
        // Operator auth/pairing problems can coexist with a connected node, so the
        // root's only remediation surface must not depend on aggregate status.
        #expect(activeProblemToast.contains("appModel.lastGatewayProblem"))
        #expect(!activeProblemToast.contains("gatewayStatus"))
        // Every problem report re-surfaces a swiped-away toast. Visible problem
        // banners stay stationary when reconnects re-report the same failure.
        #expect(rootSource.contains("self.appModel.gatewayProblemReportCount"))
        #expect(rootSource.contains("guard self.isGatewayToastSwipeDismissed else { return }"))
        #expect(!rootSource.contains("GatewayToastShakeEffect"))

        #expect(actionsSource.contains("await self.gatewayController.connectActiveGateway()"))
        #expect(actionsSource.contains("self.gatewayController.refreshActiveGatewayRegistrationFromSettings()"))
        #expect(actionsSource.contains("self.gatewayController.restartDiscovery()"))
        #expect(actionsSource.contains("await self.appModel.refreshGatewayOverviewIfConnected()"))
        #expect(actionsSource
            .contains("self.gatewayController.requestLocalNetworkAccess(reason: \"settings_preflight\")"))
        #expect(controllerSource.contains("await self.tcpReachabilityProbe("))
        #expect(controllerSource.contains("Check Tailscale or LAN."))
        #expect(actionsSource.contains("Tailscale is off on this device. Turn it on, then try again."))
        #expect(actionsSource.contains("Run /pair approve in your OpenClaw chat"))
        #expect(settingsSource.contains("self.resetOnboarding()"))
        #expect(settingsSource.contains(".onChange(of: self.onboardingRequestID)"))
        #expect(settingsSource.contains("self.syncAfterOnboardingReset()"))
        #expect(settingsSource.contains("let acceptsGatewaySetupRequests: Bool"))
        #expect(settingsSource.contains("guard self.acceptsGatewaySetupRequests else { return }"))
        #expect(settingsSource.contains(".onChange(of: self.acceptsGatewaySetupRequests)"))
        #expect(rootSource.matches(of: /acceptsGatewaySetupRequests: !self\.showOnboarding/).count == 1)
        #expect(actionsSource.contains("func syncAfterOnboardingReset()"))
        #expect(actionsSource.contains("self.pendingManualAuthOverride = nil"))
        // The root toast is the only gateway problem surface outside covers, so it
        // must keep the reset-onboarding primary action the settings banner had.
        #expect(rootSource.contains("resetTitle: \"Reset onboarding\""))
        #expect(rootSource.contains("GatewayOnboardingReset.reset(appModel: self.appModel, instanceId: instanceId)"))
        #expect(rootSource.contains("self.gatewayController.trustRotatedGatewayCertificate(from: problem)"))
        #expect(rootSource.contains("GatewayProblemPrimaryAction.handleProtocolMismatchIfNeeded(problem)"))
        #expect(rootSource.contains("await self.gatewayController.connectActiveGateway()"))

        #expect(rootSource.contains("GatewayProblemDetailsSheet("))
        #expect(onboardingSetupOwnerGuard.lowerBound < consumedGatewaySetup.lowerBound)
        #expect(consumedGatewaySetup.lowerBound < deliveredGatewaySetup.lowerBound)
        #expect(settingsSource.contains("QRScannerView("))
        #expect(settingsOnDismiss.lowerBound < settingsProcessing.lowerBound)
        #expect(settingsProcessing.lowerBound < settingsContent.lowerBound)
        #expect(settingsPendingSetupHandler.contains("self.showQRScanner = false"))
        #expect(settingsScannerCancel.lowerBound < settingsSetupStaging.lowerBound)
        #expect(settingsPendingSetupHandler.contains(
            "self.gatewayController.cancelPendingConnectionAttempts()"))
        #expect(!settingsSource.contains(".onChange(of: self.showQRScanner)"))
        #expect(actionsSource.contains("case let .gatewayLink(link):"))
        #expect(actionsSource.contains("case let .setupCode(code):"))
        #expect(actionsSource.contains(
            "self.stagedGatewaySetupLink = nil\n        self.setupCode = \"\"\n        await self.applyGatewayLink(link)"))
        #expect(stopScanning.lowerBound < deliverResult.lowerBound)
        #expect(trustSource.contains("Trust this gateway?"))
        #expect(trustSource.contains("Trust and connect"))
        #expect(trustSource.contains("let isEnabled: Bool"))
        #expect(rootSource.contains(".gatewayTrustPromptAlert(isEnabled: !self.showOnboarding)"))
    }

    static func assertGatewayOnboardingFlowGuards() throws {
        let onboardingSource = try self.onboardingWizardSource()
        let actionsSource = try String(contentsOf: self.settingsProTabActionsSourceURL(), encoding: .utf8)
        let controllerSource = try self.gatewayConnectionControllerSource()
        let pendingSetupHandler = try self.extract(
            onboardingSource,
            from: "private func applyPendingGatewaySetupLinkIfNeeded()",
            to: "private func connectStagedGatewaySetupLink()")
        let stagedSetupConnect = try self.extract(
            onboardingSource,
            from: "private func connectStagedGatewaySetupLink()",
            to: "private func clearStagedGatewaySetupLink()")
        let stagedSetupClear = try self.extract(
            onboardingSource,
            from: "private func clearStagedGatewaySetupLink()",
            to: "private func applyGatewayLink(")
        let connectionFailure = try self.extract(
            onboardingSource,
            from: "private func setConnectionFailure(_ message: String)",
            to: "var body: some View")
        let stagedValidation = try #require(stagedSetupConnect.range(of: "guard link.isValidEndpoint"))
        let stagedConsumption = try #require(stagedSetupConnect.range(of: "self.setupLinkStaging.take()"))
        let stagedReset = try #require(
            stagedSetupConnect.range(of: "await self.appModel.resetGatewaySessionsForTargetSwitch()"))
        let onboardingGatewayLink = try self.extract(
            onboardingSource,
            from: "private func applyGatewayLink(",
            to: "private func handleScannedSetupCode(")
        let settingsGatewayLink = try self.extract(
            actionsSource,
            from: "func applyGatewayLink(",
            to: "func openGatewayQRScanner()")
        let onboardingManualConnect = try self.extract(
            onboardingSource,
            from: "private func connectCurrentManualGateway(",
            to: "private func retryLastAttempt(")
        let onboardingRetry = try self.extract(
            onboardingSource,
            from: "private func retryLastAttempt(",
            to: "private func gatewayProblemPrimaryActionTitle(")
        let settingsManualConnect = try self.extract(
            actionsSource,
            from: "func connectManual(setupAttemptID: UUID? = nil) async",
            to: "func preflightGateway(host: String)")

        #expect(onboardingSource.contains(".gatewayTrustPromptAlert()"))
        #expect(onboardingSource.contains("self.applyPendingGatewaySetupLinkIfNeeded()"))
        #expect(onboardingSource.contains(".onChange(of: self.appModel.gatewaySetupRequestID)"))
        #expect(onboardingSource.contains("self.appModel.consumePendingGatewaySetupLink()"))
        #expect(onboardingSource.contains("self.scannerResultHandoff.cancel()"))
        #expect(!onboardingSource.contains("pendingScannerResult"))
        #expect(onboardingSource.contains("self.setupLinkStaging.stage(link)"))
        #expect(pendingSetupHandler.contains("self.gatewayController.cancelPendingConnectionAttempts()"))
        #expect(pendingSetupHandler.contains("if self.selectedMode == nil"))
        #expect(onboardingSource.contains("Tap Connect to apply."))
        #expect(onboardingSource.contains("self.connectStagedGatewaySetupLink()"))
        #expect(onboardingSource.contains("Credentials are applied only after you tap Connect."))
        #expect(onboardingSource.contains("Plaintext (local network)"))
        #expect(onboardingSource.contains("self.statusLine = message"))
        #expect(!pendingSetupHandler.contains("self.manualHost ="))
        #expect(!pendingSetupHandler.contains("self.manualPort ="))
        #expect(!pendingSetupHandler.contains("self.manualTLS ="))
        #expect(!pendingSetupHandler.contains("self.applyGatewayLink(link)"))
        #expect(!pendingSetupHandler.contains("self.handleScannedLink(link)"))
        #expect(!pendingSetupHandler.contains("self.connectManual()"))
        #expect(stagedValidation.lowerBound < stagedConsumption.lowerBound)
        #expect(stagedReset.lowerBound < stagedConsumption.lowerBound)
        #expect(!stagedSetupConnect.contains("self.appModel.disconnectGateway()"))
        #expect(stagedSetupConnect.contains(
            "self.applyGatewayLink(link, disconnectExistingGatewayForBootstrap: false)"))
        #expect(stagedSetupConnect.contains("guard self.connectingGateway == nil else { return }"))
        #expect(onboardingSource.contains("case gateway(GatewayStableIdentifier.Key)"))
        #expect(onboardingSource.contains("self.connectingGateway = .gateway(gateway.id)"))
        #expect(!onboardingSource.contains("connectingGatewayID"))
        #expect(stagedSetupConnect.contains("self.setConnectionFailure(message)"))
        #expect(connectionFailure.contains("self.localConnectionFailure = message"))
        #expect(!connectionFailure.contains("self.connectMessage = message"))
        #expect(connectionFailure.contains("self.statusLine = message"))
        #expect(onboardingSource.contains(".failedStatus(message: localFailure, allowsRetry: false)"))
        #expect(onboardingSource.contains(
            "primaryActionTitle: allowsRetry ? OpenClawTextValue.localized(\"Retry\") : nil"))
        #expect(onboardingSource.contains("onPrimaryAction: allowsRetry ? self.onRetry : nil"))
        #expect(stagedSetupClear.contains("self.localConnectionFailure = nil"))
        #expect(onboardingRetry.contains("self.localConnectionFailure = nil"))
        #expect(onboardingRetry.contains(
            "self.setConnectionFailure(\"No connection to retry. Check the gateway host and port.\")"))
        #expect(onboardingSource.contains("self.setupLinkStaging.link == nil else { return }"))
        #expect(onboardingGatewayLink.contains("self.gatewayToken = setupAuth.token"))
        #expect(onboardingGatewayLink.contains("self.gatewayPassword = setupAuth.password"))
        #expect(settingsGatewayLink.contains("self.gatewayToken = setupAuth.token"))
        #expect(settingsGatewayLink.contains("self.gatewayPassword = setupAuth.password"))
        #expect(onboardingManualConnect.contains("nodeOptions.allowStoredDeviceAuth == true"))
        #expect(onboardingManualConnect.contains("self.pendingManualAuthOverride = nil"))
        #expect(onboardingManualConnect.contains("targetStableID: stableID"))
        #expect(settingsManualConnect.contains("nodeOptions.allowStoredDeviceAuth == true"))
        #expect(settingsManualConnect.contains("self.pendingManualAuthOverride = nil"))
        #expect(settingsManualConnect.contains("targetStableID: stableID"))
        #expect(!controllerSource.contains("shouldApplyTokenField"))
        #expect(!controllerSource.contains("shouldApplyPasswordField"))
        #expect(controllerSource.contains("allowStoredDeviceAuth: !suppressStoredDeviceAuth"))
        #expect(controllerSource.contains(
            "deviceAuthGatewayID: GatewaySettingsStore.authenticationOwnerID("))
        #expect(controllerSource.contains("DeviceAuthStore.migrateUnscopedToken("))
        #expect(controllerSource.contains("DeviceAuthStore.discardUnscopedTokens("))
        #expect(onboardingSource.contains(
            "self.selectGatewayCredentialTarget(gateway.stableID, allowManualOverride: false)"))
        #expect(actionsSource.contains(
            "self.selectGatewayCredentialTarget(gateway.stableID, allowManualOverride: false)"))
        #expect(onboardingSource.contains(
            "self.gatewayCredentialFieldStableID ?? self.currentManualGatewayStableID"))
        #expect(actionsSource.contains(
            "self.gatewayCredentialFieldStableID ?? self.currentManualGatewayStableID"))
        // Gateway stable IDs compare byte-exact via GatewayStableIdentifier, never
        // via trimmed/string equality; a regressed comparison silently reuses
        // credentials across distinct gateway owners.
        #expect(onboardingSource.contains(
            "if !GatewayStableIdentifier.matches(self.gatewayCredentialFieldStableID, stableID)"))
        #expect(actionsSource.contains(
            "if !GatewayStableIdentifier.matches(self.gatewayCredentialFieldStableID, stableID)"))
        #expect(!onboardingSource.contains("gatewayCredentialFieldStableID == stableID"))
        #expect(!actionsSource.contains("gatewayCredentialFieldStableID == stableID"))
        #expect(onboardingSource.contains("GatewayStableIdentifier.key(previousStableID) !="))
        #expect(actionsSource.contains("GatewayStableIdentifier.key(previousStableID) !="))
    }

    static func assertGatewayReconnectGuards() throws {
        let modelSource = try String(contentsOf: self.nodeAppModelSourceURL(), encoding: .utf8)
        let controllerSource = try self.gatewayConnectionControllerSource()
        let backgroundReconnect = try self.extract(
            modelSource,
            from: "private func performBackgroundAliveBeaconIfNeeded(",
            to: "private func publishBackgroundAliveBeacon(")
        let disconnectGateway = try self.extract(
            modelSource,
            from: "func disconnectGateway()",
            to: "private func disableGatewayAutoReconnect()")
        let operatorGatewayLoop = try self.extract(
            modelSource,
            from: "private func startOperatorGatewayLoop(",
            to: "private func startNodeGatewayLoop(")
        let nodeGatewayLoop = try self.extract(
            modelSource,
            from: "private func startNodeGatewayLoop(",
            to: "private func makeOperatorConnectOptions(")
        let wakeWordRefresh = try self.extract(
            modelSource,
            from: "private func refreshWakeWordsFromGateway(",
            to: "private func isGatewayHealthMonitorDisabled()")

        #expect(disconnectGateway.contains("self.beginGatewaySessionReset(chainingAfterExisting: true)"))
        #expect(!disconnectGateway.contains("Task {"))
        #expect(modelSource.contains(
            "private func isCurrentGatewayRoute(generation: UInt64, stableID: String) -> Bool"))
        #expect(modelSource.matches(
            of: /self\.isCurrentGatewayRoute\(generation: routeGeneration, stableID: stableID\)/).count >= 2)
        #expect(operatorGatewayLoop.contains("gatewayReconnectLoopDelay(source: \"operator_loop\")"))
        #expect(nodeGatewayLoop.contains("gatewayReconnectLoopDelay(source: \"node_loop\")"))
        #expect(modelSource.contains("refreshWakeWordsFromGateway(shouldApply: shouldContinue)"))
        #expect(wakeWordRefresh.matches(of: /guard shouldApply\(\) else \{ return \}/).count >= 2)
        #expect(modelSource.contains("if !self.gatewayAutoReconnectEnabled || self.gatewayPairingPaused"))
        #expect(controllerSource.contains("acceptPendingTrustPrompt()"))
        #expect(controllerSource.contains("trustRotatedGatewayCertificate(from problem: GatewayConnectionProblem)"))
        #expect(controllerSource.contains("allowAutoReconnect: false"))
        #expect(controllerSource.contains("guard allowAutoReconnect else { return }"))
        #expect(controllerSource.contains("guard self.autoConnectSuppressionGeneration == nil else { return }"))
        #expect(backgroundReconnect.contains("let generation = self.gatewayConnectGeneration"))
        #expect(backgroundReconnect.contains("await self.resetGatewaySessionsForForcedReconnect()"))
        #expect(backgroundReconnect.contains("expectedGeneration: generation"))
        #expect(modelSource.contains("expectedGeneration: UInt64)"))
        #expect(!modelSource.contains("expectedGeneration: UInt64?"))
        #expect(modelSource.contains("GatewayStableIdentifier.exact(self.connectedGatewayID)"))
    }
}
