import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:iop_client/main.dart';
import 'package:iop_client/client_config.dart';
import 'package:iop_client/iop_wire/client_wire_client.dart';
import 'package:iop_client/gen/proto/iop/control.pb.dart';
import 'package:iop_client/control_plane_status_client.dart';
import 'package:iop_client/widgets/nodes_panel.dart';
import 'package:protobuf/protobuf.dart';
import 'package:fixnum/fixnum.dart';

class FakeWebSocket implements WebSocket {
  final _controller = StreamController<dynamic>();

  @override
  StreamSubscription<dynamic> listen(
    void Function(dynamic event)? onData, {
    Function? onError,
    void Function()? onDone,
    bool? cancelOnError,
  }) {
    return _controller.stream.listen(
      onData,
      onError: onError,
      onDone: onDone,
      cancelOnError: cancelOnError,
    );
  }

  @override
  dynamic noSuchMethod(Invocation invocation) {
    return null;
  }
}

class FakeClientWireClient extends ClientWireClient {
  final bool shouldSuccess;
  final String mockMessage;

  FakeClientWireClient({
    this.shouldSuccess = true,
    this.mockMessage = 'Welcome to Toki CP!',
  }) : super(FakeWebSocket(), heartbeatIntervalTime: 0);

  @override
  bool get isAlive => true;

  @override
  Future<Res> sendRequest<
    Req extends GeneratedMessage,
    Res extends GeneratedMessage
  >(Req data, {Duration timeout = const Duration(seconds: 30)}) async {
    if (data is ClientHelloRequest) {
      final response = ClientHelloResponse()
        ..ready = shouldSuccess
        ..protocol = 'iop-wire-v1'
        ..serverTimeUnixNano = Int64(1716584400)
        ..message = mockMessage;
      return response as Res;
    }
    throw UnimplementedError();
  }
}

class FakeControlPlaneStatusRepository implements ControlPlaneStatusRepository {
  String nextCommandStatus = 'accepted';
  String nextCommandError = '';
  String nextCommandSummary = 'Command accepted by control plane';

  String? lastCommandOperation;
  String? lastCommandTargetSelector;
  Map<String, String>? lastCommandParameters;
  String? lastCommandEdgeId;
  Completer<EdgeOperationsResponseView>? edgeBOperationsCompleter;

  List<EdgeRegistryView> mockEdges = [
    EdgeRegistryView(
      edgeId: 'edge-a',
      edgeName: 'Edge Alpha',
      version: '1.2.0',
      capabilities: ['Serving', 'Automation'],
      protocol: 'iop-wire-v1',
      connected: true,
      lastSeen: DateTime.now(),
    ),
    EdgeRegistryView(
      edgeId: 'edge-b',
      edgeName: 'Edge Beta',
      version: '1.2.1',
      capabilities: ['Serving'],
      protocol: 'iop-wire-v1',
      connected: false,
      lastSeen: DateTime.now().subtract(const Duration(minutes: 5)),
    ),
  ];

  @override
  Future<List<EdgeRegistryView>> fetchEdges() async {
    return mockEdges;
  }

  @override
  Future<EdgeStatusResponseView> fetchEdgeStatus(String edgeId) async {
    return EdgeStatusResponseView(
      requestId: 'req-123',
      edgeId: edgeId,
      edgeName: edgeId == 'edge-a' ? 'Edge Alpha' : 'Edge Beta',
      observedTimeUnixNano: DateTime.now().microsecondsSinceEpoch * 1000,
      nodes: [
        EdgeNodeSnapshotView(
          nodeId: 'node-1',
          alias: 'Node One',
          label: 'GPU-T4',
          connected: true,
          config: NodeConfigSummaryView(
            adapters: [
              AdapterSummaryView(type: 'ollama', enabled: true),
              AdapterSummaryView(type: 'custom', enabled: false),
            ],
            concurrency: 4,
          ),
          providerSnapshots: [
            ProviderSnapshotView(
              id: 'provider-ollama',
              adapter: 'ollama',
              type: 'llm',
              category: 'inference',
              status: 'active',
              health: 'healthy',
              capacity: 10,
              inFlight: 3,
              queued: 1,
              loadRatio: 0.3,
              servedModels: ['llama-3.1', 'mistral'],
              lifecycleCapabilities: ['start', 'stop', 'restart'],
            ),
            ProviderSnapshotView(
              id: 'provider-vllm',
              adapter: 'vllm',
              type: 'llm',
              category: 'inference',
              status: 'active',
              health: 'healthy',
              capacity: 8,
              inFlight: 2,
              queued: 0,
              loadRatio: 0.25,
              servedModels: ['vicuna'],
              lifecycleCapabilities: ['start', 'stop'],
            ),
          ],
        ),
        EdgeNodeSnapshotView(
          nodeId: 'node-2',
          alias: 'Node Two',
          label: 'CPU-only',
          connected: false,
          config: NodeConfigSummaryView(
            adapters: [AdapterSummaryView(type: 'python-cli', enabled: true)],
            concurrency: 2,
          ),
          providerSnapshots: [
            ProviderSnapshotView(
              id: 'provider-openai',
              adapter: 'openai',
              type: 'llm',
              category: 'external',
              status: 'active',
              health: 'degraded',
              capacity: 100,
              inFlight: 50,
              queued: 5,
              loadRatio: 0.5,
              servedModels: ['gpt-4', 'gpt-3.5-turbo'],
              lifecycleCapabilities: [],
            ),
          ],
        ),
      ],
      capabilities: [
        EdgeCapabilitySummaryView(
          kind: 'Serving',
          available: true,
          status: 'ready',
          summary: 'Active',
        ),
        EdgeCapabilitySummaryView(
          kind: 'Automation',
          available: true,
          status: 'ready',
          summary: 'Active',
        ),
      ],
      domainAgents: edgeId == 'edge-a'
          ? [
              EdgeDomainAgentSummaryView(
                agentKind: 'deployer',
                available: true,
                lifecycleState: 'ready',
                activeCommandId: 'cmd-alpha-active',
                summary: 'Active',
              ),
              EdgeDomainAgentSummaryView(
                agentKind: 'build-deploy',
                available: true,
                lifecycleState: 'ready',
                activeCommandId: '',
                summary: 'Idle',
              ),
            ]
          : [
              EdgeDomainAgentSummaryView(
                agentKind: 'deployer',
                available: true,
                lifecycleState: 'busy',
                activeCommandId: 'cmd-beta-active',
                summary: 'Processing',
              ),
              EdgeDomainAgentSummaryView(
                agentKind: 'build-deploy',
                available: true,
                lifecycleState: 'ready',
                activeCommandId: '',
                summary: 'Idle',
              ),
            ],
      metadata: {'region': 'us-west'},
      error: '',
    );
  }

  @override
  Future<List<EdgeNodeEventView>> fetchEdgeEvents(String edgeId) async {
    return [
      EdgeNodeEventView(
        edgeId: edgeId,
        eventId: 'evt-1',
        type: 'online',
        source: 'node-register',
        nodeId: 'node-1',
        alias: 'Node One',
        reason: 'Node connected and registered successfully',
        timestamp: DateTime.now().subtract(const Duration(minutes: 2)),
        receivedAt: DateTime.now().subtract(const Duration(minutes: 2)),
        metadata: {},
      ),
      EdgeNodeEventView(
        edgeId: edgeId,
        eventId: 'evt-2',
        type: 'warn',
        source: 'runtime-dispatch',
        nodeId: 'node-2',
        alias: 'Node Two',
        reason: 'Execution concurrency limit reached',
        timestamp: DateTime.now().subtract(const Duration(minutes: 1)),
        receivedAt: DateTime.now().subtract(const Duration(minutes: 1)),
        metadata: {},
      ),
    ];
  }

  @override
  Future<FleetStatusResponseView> fetchFleetStatus() async {
    return FleetStatusResponseView(
      generatedAt: DateTime.now(),
      edges: mockEdges
          .map(
            (e) => FleetEdgeView(
              edgeId: e.edgeId,
              edgeName: e.edgeName,
              version: e.version,
              protocol: e.protocol,
              connected: e.connected,
              health: e.connected ? 'healthy' : 'unhealthy',
              lastSeen: e.lastSeen,
              nodeCount: 2,
              capabilities: e.capabilities
                  .map(
                    (c) => EdgeCapabilitySummaryView(
                      kind: c,
                      available: true,
                      status: 'ready',
                      summary: '',
                    ),
                  )
                  .toList(),
              domainAgents: e.edgeId == 'edge-a'
                  ? [
                      EdgeDomainAgentSummaryView(
                        agentKind: 'deployer',
                        available: true,
                        lifecycleState: 'ready',
                        activeCommandId: 'cmd-alpha-active',
                        summary: 'Active',
                      ),
                      EdgeDomainAgentSummaryView(
                        agentKind: 'build-deploy',
                        available: true,
                        lifecycleState: 'ready',
                        activeCommandId: '',
                        summary: 'Idle',
                      ),
                    ]
                  : [
                      EdgeDomainAgentSummaryView(
                        agentKind: 'deployer',
                        available: true,
                        lifecycleState: 'busy',
                        activeCommandId: 'cmd-beta-active',
                        summary: 'Processing',
                      ),
                      EdgeDomainAgentSummaryView(
                        agentKind: 'build-deploy',
                        available: true,
                        lifecycleState: 'ready',
                        activeCommandId: '',
                        summary: 'Idle',
                      ),
                    ],
              error: '',
            ),
          )
          .toList(),
    );
  }

  @override
  Future<EdgeOperationsResponseView> fetchEdgeOperations(String edgeId) async {
    if (edgeId == 'edge-a') {
      return EdgeOperationsResponseView(
        edgeId: edgeId,
        operations: [
          EdgeCommandRecordView(
            edgeId: edgeId,
            commandId: 'cmd-alpha-active',
            operation: 'deployer.sync',
            targetSelector: '',
            status: 'success',
            summary: 'Synced 5 models',
            error: '',
          ),
        ],
      );
    } else {
      if (edgeBOperationsCompleter != null) {
        return edgeBOperationsCompleter!.future;
      }
      return EdgeOperationsResponseView(
        edgeId: edgeId,
        operations: [
          EdgeCommandRecordView(
            edgeId: edgeId,
            commandId: 'cmd-beta-active',
            operation: 'build-deploy.deploy',
            targetSelector: '',
            status: 'success',
            summary: 'Deploy queued for edge beta',
            error: '',
          ),
        ],
      );
    }
  }

  @override
  Future<EdgeCommandResponseView> sendEdgeCommand(
    String edgeId,
    String operation, {
    String? targetSelector,
    Map<String, String>? parameters,
  }) async {
    lastCommandOperation = operation;
    lastCommandTargetSelector = targetSelector;
    lastCommandParameters = parameters;
    lastCommandEdgeId = edgeId;

    if (operation == 'agent.status' &&
        (targetSelector == null || targetSelector.isEmpty)) {
      return EdgeCommandResponseView(
        requestId: 'req-111',
        commandId: 'cmd-failed',
        edgeId: edgeId,
        status: 'error',
        summary: 'Failed: agent.status requires target_selector',
        error: 'target_selector is required',
      );
    }
    if (operation == 'agent.command') {
      final cmd = parameters?['command'];
      if (targetSelector == null ||
          targetSelector.isEmpty ||
          cmd == null ||
          cmd.isEmpty) {
        return EdgeCommandResponseView(
          requestId: 'req-111',
          commandId: 'cmd-failed',
          edgeId: edgeId,
          status: 'error',
          summary:
              'Failed: agent.command requires target_selector and parameters.command',
          error: 'target_selector and parameters.command are required',
        );
      }
    }

    return EdgeCommandResponseView(
      requestId: 'req-111',
      commandId: 'cmd-new',
      edgeId: edgeId,
      status: nextCommandStatus,
      summary: nextCommandSummary,
      error: nextCommandError,
    );
  }
}

void main() {
  testWidgets('Client App basic rendering and success handshake test', (
    WidgetTester tester,
  ) async {
    final fakeClient = FakeClientWireClient(shouldSuccess: true);
    final fakeStatusRepo = FakeControlPlaneStatusRepository();

    await tester.pumpWidget(
      IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
    );
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    expect(find.text('IOP CONTROL PLANE'), findsOneWidget);
    expect(find.text('Operations Overview'), findsOneWidget);

    expect(
      find.text(ClientConfig.controlPlaneHttpUrl, findRichText: true),
      findsOneWidget,
    );
    expect(
      find.text(ClientConfig.controlPlaneWireUrl, findRichText: true),
      findsOneWidget,
    );

    expect(find.text('CONNECTED'), findsOneWidget);
  });

  testWidgets('Client App connection error state test', (
    WidgetTester tester,
  ) async {
    final fakeClient = FakeClientWireClient(
      shouldSuccess: false,
      mockMessage: 'Invalid Version',
    );
    final fakeStatusRepo = FakeControlPlaneStatusRepository();

    await tester.pumpWidget(
      IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
    );
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    expect(find.text('ERROR'), findsOneWidget);
  });

  testWidgets('Client App opens IOP agent panel from the left rail', (
    WidgetTester tester,
  ) async {
    final fakeClient = FakeClientWireClient(shouldSuccess: true);
    final fakeStatusRepo = FakeControlPlaneStatusRepository();

    await tester.pumpWidget(
      IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
    );
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    await tester.tap(find.byTooltip('Agent'));
    await tester.pump();

    expect(find.text('Ask about IOP operations'), findsOneWidget);
    expect(find.textContaining('IOP agent surface is ready'), findsOneWidget);
    expect(find.textContaining('Edge Control'), findsOneWidget);
    expect(find.textContaining('Node Management'), findsOneWidget);
  });

  testWidgets('Client App opens Edges panel and displays Edge details', (
    WidgetTester tester,
  ) async {
    final fakeClient = FakeClientWireClient(shouldSuccess: true);
    final fakeStatusRepo = FakeControlPlaneStatusRepository();

    await tester.pumpWidget(
      IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
    );
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    await tester.tap(find.byTooltip('Edges'));
    await tester.pump();

    // Verify list of edges
    expect(find.text('Edge Alpha'), findsNWidgets(2));
    expect(find.text('Edge Beta'), findsOneWidget);

    // Verify detail pane for the first auto-selected edge (Edge Alpha)
    expect(find.text('Edge ID'), findsOneWidget);
    expect(find.text('edge-a'), findsOneWidget);
    expect(find.text('1.2.0'), findsOneWidget);
    expect(find.text('Serving'), findsOneWidget);
    expect(find.text('Automation'), findsOneWidget);

    // Verify selecting another Edge switches the detail pane instead of
    // reusing the first Edge's status view.
    await tester.tap(find.text('Edge Beta'));
    await tester.pump();

    expect(find.text('Edge Alpha'), findsOneWidget);
    expect(find.text('Edge Beta'), findsNWidgets(2));
    expect(find.text('edge-b'), findsOneWidget);
    expect(find.text('1.2.1'), findsOneWidget);
    expect(find.text('DISCONNECTED'), findsOneWidget);
    expect(find.text('unhealthy'), findsOneWidget);
  });

  testWidgets(
    'Client App opens Nodes panel and displays active Nodes and configurations',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Nodes'));
      await tester.pumpAndSettle();

      // Verify Nodes view header
      expect(find.text('Nodes View'), findsOneWidget);

      // Verify nodes listed
      expect(find.text('Node One'), findsOneWidget);
      expect(find.text('Label: GPU-T4'), findsOneWidget);
      expect(find.text('Concurrency Limit: 4'), findsOneWidget);
      expect(find.text('ollama'), findsOneWidget);
      expect(find.text('custom'), findsOneWidget);
    },
  );

  testWidgets(
    'Client App Nodes panel displays Provider Catalog for nodes with snapshots',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Nodes'));
      await tester.pumpAndSettle();

      // Scroll to reveal second node's provider catalog before assertions
      await tester.drag(
        find.byType(ListView),
        const Offset(0.0, -1500.0),
      );
      await tester.pumpAndSettle();

      // Verify Provider Catalog sections are present on both nodes
      expect(find.textContaining('Provider Catalog'), findsNWidgets(2));

      // Verify Provider Snapshot display for node-1 (ollama provider)
      expect(find.text('provider-ollama'), findsOneWidget);
      expect(find.textContaining('llm / inference'), findsNWidgets(2)); // both providers on node-1
      expect(find.textContaining('HEALTHY'), findsNWidgets(2)); // both ollama and vllm are healthy
      expect(find.textContaining('Load: 3/10'), findsOneWidget);
      expect(find.textContaining('Q: 1'), findsOneWidget);
      expect(find.textContaining('30.0%'), findsOneWidget);
      expect(find.textContaining('Models: llama-3.1, mistral'), findsOneWidget);

      // Verify Provider Snapshot display for node-2 (degraded state)
      expect(find.text('provider-openai'), findsOneWidget);
      expect(find.textContaining('Load: 50/100'), findsOneWidget);
      expect(find.textContaining('Q: 5'), findsOneWidget);
      expect(find.textContaining('50.0%'), findsOneWidget);
    },
  );

  testWidgets(
    'Client App opens Execution/Logs panel and displays lifecycle events',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Execution & Logs'));
      await tester.pump();

      // Verify logs header
      expect(find.text('Lifecycle Events & Logs'), findsOneWidget);

      // Verify captured events
      expect(find.text('ONLINE'), findsOneWidget);
      expect(find.text('WARN'), findsOneWidget);
      expect(
        find.text('Node connected and registered successfully'),
        findsOneWidget,
      );
      expect(find.text('Execution concurrency limit reached'), findsOneWidget);
    },
  );

  testWidgets('Client App refresh behavior when selected edge disappears', (
    WidgetTester tester,
  ) async {
    final fakeClient = FakeClientWireClient(shouldSuccess: true);
    final fakeStatusRepo = FakeControlPlaneStatusRepository();

    await tester.pumpWidget(
      IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
    );
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    // 1. Initial State: edge-a is selected, EdgesPanel has Edge Alpha and Edge Beta
    await tester.tap(find.byTooltip('Edges'));
    await tester.pump();
    expect(find.text('Edge Alpha'), findsNWidgets(2)); // list & details

    // 2. Change mockEdges so 'edge-a' and 'edge-b' disappear, only 'edge-c' is returned.
    fakeStatusRepo.mockEdges = [
      EdgeRegistryView(
        edgeId: 'edge-c',
        edgeName: 'Edge Gamma',
        version: '1.2.2',
        capabilities: ['Automation'],
        protocol: 'iop-wire-v1',
        connected: true,
        lastSeen: DateTime.now(),
      ),
    ];

    // Trigger refresh in overview
    await tester.tap(find.byTooltip('Overview'));
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    // Verify route has navigated back to Overview successfully
    expect(find.text('Operations Overview'), findsOneWidget);

    // Drag the ListView upward to scroll down and mount the bottom refresh button
    final listViewFinder = find.byType(ListView);
    await tester.drag(listViewFinder.first, const Offset(0.0, -400.0));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 200));

    // Tap the refresh button safely by its text label
    await tester.tap(find.text('Refresh Connection'));
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    // 3. Switch to Edges panel, verify edge-a disappears, edge-c appears and auto-selected
    await tester.tap(find.byTooltip('Edges'));
    await tester.pump();

    expect(find.text('Edge Alpha'), findsNothing);
    expect(
      find.text('Edge Gamma'),
      findsNWidgets(2),
    ); // list & details (auto-selected)

    // 4. Switch to Nodes panel, verify dropdown Value works without assert crash
    await tester.tap(find.byTooltip('Nodes'));
    await tester.pump();
    expect(find.text('Nodes View'), findsOneWidget);
    // dropdown button value should be edge-c now
    expect(find.text('Edge Gamma'), findsOneWidget);

    // 5. Switch to Logs panel, verify dropdown Value works without assert crash
    await tester.tap(find.byTooltip('Execution & Logs'));
    await tester.pump();
    expect(find.text('Lifecycle Events & Logs'), findsOneWidget);
    expect(find.text('Edge Gamma'), findsOneWidget);
  });

  testWidgets(
    'Client App opens Operations & Domain Agents panel and verifies agents, operations history, and command triggering',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Runtime'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // Verify panel header and sections
      expect(find.text('Operations & Domain Agents'), findsOneWidget);
      expect(find.text('Domain Agents'), findsOneWidget);
      expect(find.text('Operations Execution History'), findsOneWidget);
      expect(find.text('System Gated Operations'), findsOneWidget);

      // Verify domain agents summary listed
      expect(find.text('DEPLOYER'), findsOneWidget);
      expect(find.text('BUILD-DEPLOY'), findsOneWidget);
      expect(
        find.text('READY'),
        findsNWidgets(2),
      ); // deployer and build-deploy are both ready

      // [REVIEW_API-2] Verify domain agents active command summary for Edge Alpha (presence)
      expect(find.text('Active Command: cmd-alpha-active'), findsOneWidget);

      // [REVIEW_API-2] Verify beta active command summary is NOT showing for Edge Alpha
      expect(find.text('Active Command: cmd-beta-active'), findsNothing);
      expect(find.text('build-deploy.deploy'), findsNothing);
      expect(find.textContaining('Deploy queued for edge beta'), findsNothing);

      // Verify operations history item
      expect(find.text('deployer.sync'), findsOneWidget);
      expect(find.text('SUCCESS'), findsOneWidget);
      expect(find.textContaining('Synced 5 models'), findsOneWidget);

      // Verify Trigger Sync button is not present
      expect(find.text('Trigger Sync'), findsNothing);

      // Verify System Gated buttons are present
      expect(find.text('Health Check'), findsOneWidget);
      expect(find.text('Agent Status'), findsOneWidget);
      expect(find.text('Agent Command'), findsOneWidget);

      // Verify Health Check button works and shows success banner
      await tester.tap(find.text('Health Check'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // Check for success status banner message
      expect(find.textContaining('Success: Command accepted'), findsOneWidget);

      // Verify command target edge ID was edge-a
      expect(fakeStatusRepo.lastCommandEdgeId, equals('edge-a'));

      // Setup delayed fetch for edge-b
      fakeStatusRepo.edgeBOperationsCompleter = Completer<EdgeOperationsResponseView>();

      // Switch to Edge Beta via Dropdown
      await tester.tap(find.byType(DropdownButton<String>));
      await tester.pumpAndSettle();
      await tester.tap(find.text('Edge Beta').last);
      // Pump one frame to trigger edge selection but wait before operations fetch resolves
      await tester.pump();

      // [REVIEW_API-1] Verify that before the operations fetch completes:
      // 1. The domain agents section has updated immediately (so cmd-alpha-active is gone, cmd-beta-active is present)
      expect(find.text('Active Command: cmd-alpha-active'), findsNothing);
      expect(find.text('Active Command: cmd-beta-active'), findsOneWidget);
      // 2. The operations history of the previous edge (edge-a) is cleared immediately upon fetch start.
      expect(find.text('deployer.sync'), findsNothing);
      expect(find.textContaining('Synced 5 models'), findsNothing);
      // 3. The loading indicator is shown since _operations is null and _isLoading is true
      expect(find.byType(CircularProgressIndicator), findsOneWidget);

      // Now complete the operations fetch for edge-b
      fakeStatusRepo.edgeBOperationsCompleter!.complete(EdgeOperationsResponseView(
        edgeId: 'edge-b',
        operations: [
          EdgeCommandRecordView(
            edgeId: 'edge-b',
            commandId: 'cmd-beta-active',
            operation: 'build-deploy.deploy',
            targetSelector: '',
            status: 'success',
            summary: 'Deploy queued for edge beta',
            error: '',
          ),
        ],
      ));
      await tester.pumpAndSettle();

      // Verify domain agents for Edge Beta (deployer shows BUSY, build-deploy shows READY)
      expect(find.text('BUSY'), findsOneWidget);
      expect(find.text('READY'), findsOneWidget);

      // [REVIEW_API-2] Verify presence of beta active command and operations history
      expect(find.text('Active Command: cmd-beta-active'), findsOneWidget);
      expect(find.text('build-deploy.deploy'), findsOneWidget);
      expect(find.textContaining('Deploy queued for edge beta'), findsOneWidget);

      // [REVIEW_API-2] Verify absence of alpha-only active command and operations history
      expect(find.text('Active Command: cmd-alpha-active'), findsNothing);
      expect(find.text('deployer.sync'), findsNothing);
      expect(find.textContaining('Synced 5 models'), findsNothing);

      // Verify command triggering on Edge Beta sends command to edge-b
      await tester.tap(find.text('Health Check'));
      await tester.pumpAndSettle();
      expect(fakeStatusRepo.lastCommandEdgeId, equals('edge-b'));
    },
  );

  testWidgets(
    'Client App handles unsupported or error command responses and shows error banner',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      // Configure repository to simulate a failed/unsupported command response
      fakeStatusRepo.nextCommandStatus = 'unsupported';
      fakeStatusRepo.nextCommandError = 'Operation not supported by Edge';
      fakeStatusRepo.nextCommandSummary = 'Error summary';

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Runtime'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // Verify Agent Status button works, opens dialog, and then shows error banner
      await tester.tap(find.text('Agent Status'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      expect(find.text('Get Agent Status'), findsOneWidget);
      await tester.enterText(find.byType(TextField), 'test-node');
      await tester.tap(find.text('Send'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // Check for failure status banner message
      expect(
        find.textContaining(
          'Failed: unsupported - Operation not supported by Edge',
        ),
        findsOneWidget,
      );
    },
  );

  testWidgets(
    'Client App gates agent.status and agent.command without required inputs',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Runtime'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // 1. Verify agent.status gating
      await tester.tap(find.text('Agent Status'));
      await tester.pump();
      expect(find.text('Get Agent Status'), findsOneWidget);

      // Try to send with empty target selector
      await tester.enterText(find.byType(TextField), '');
      await tester.tap(find.text('Send'));
      await tester.pump();

      // The dialog should still be open (Send was gated/no-op)
      expect(find.text('Get Agent Status'), findsOneWidget);
      expect(fakeStatusRepo.lastCommandOperation, isNull);

      // Cancel dialog
      await tester.tap(find.text('Cancel'));
      await tester.pump();

      // 2. Verify agent.command gating
      await tester.tap(find.text('Agent Command'));
      await tester.pump();
      expect(find.text('Send Agent Command'), findsOneWidget);

      // Try to send with empty command name and empty selector
      await tester.tap(find.text('Send'));
      await tester.pump();
      expect(find.text('Send Agent Command'), findsOneWidget);
      expect(fakeStatusRepo.lastCommandOperation, isNull);

      // Enter only selector
      await tester.enterText(find.byType(TextField).at(0), 'node-1');
      await tester.tap(find.text('Send'));
      await tester.pump();
      expect(find.text('Send Agent Command'), findsOneWidget);
      expect(fakeStatusRepo.lastCommandOperation, isNull);

      // Enter only command name (clear selector)
      await tester.enterText(find.byType(TextField).at(0), '');
      await tester.enterText(find.byType(TextField).at(1), 'sync');
      await tester.tap(find.text('Send'));
      await tester.pump();
      expect(find.text('Send Agent Command'), findsOneWidget);
      expect(fakeStatusRepo.lastCommandOperation, isNull);

      // Cancel
      await tester.tap(find.text('Cancel'));
      await tester.pump();
    },
  );

  // [REVIEW_API-1] Regression: health=degraded, status=active -> yellow state text/color
  testWidgets(
    'Provider with health=degraded and status=active shows DEGRADED text with yellow color',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      // Override the OpenAI provider to have health=degraded, status=active
      // This is already set in the fake repo, but verify via the widget
      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      await tester.tap(find.byTooltip('Nodes'));
      await tester.pumpAndSettle();

      // Scroll to reveal node-2's provider catalog
      await tester.drag(
        find.byType(ListView),
        const Offset(0.0, -1500.0),
      );
      await tester.pumpAndSettle();

      // The degraded provider should show DEGRADED text (not ACTIVE)
      expect(find.textContaining('DEGRADED'), findsOneWidget);
      // Also confirm the provider id is shown
      expect(find.text('provider-openai'), findsOneWidget);
    },
  );

  // [REVIEW_API-2] DTO JSON parsing test
  testWidgets(
    'EdgeStatusResponseView.fromJson parses raw JSON and validates provider_snapshots',
    (WidgetTester tester) async {
      // This test verifies the HTTP JSON DTO parsing path used by
      // HttpControlPlaneStatusRepository.fetchEdgeStatus.
      final rawJson = {
        'request_id': 'test-req-001',
        'edge_id': 'edge-test',
        'edge_name': 'Edge Test Node',
        'observed_time_unix_nano': 1716584400000000000,
        'nodes': [
          {
            'node_id': 'node-1',
            'alias': 'Test Node',
            'label': 'GPU-A100',
            'connected': true,
            'config': {
              'adapters': [
                {'type': 'ollama', 'enabled': true},
              ],
              'concurrency': 8,
            },
            'provider_snapshots': [
              {
                'id': 'provider-ollama',
                'adapter': 'ollama',
                'type': 'llm',
                'category': 'inference',
                'status': 'active',
                'health': 'degraded',
                'capacity': 10,
                'in_flight': 3,
                'queued': 1,
                'load_ratio': 0.3,
                'served_models': ['llama-3.1', 'mistral'],
                'lifecycle_capabilities': ['start', 'stop', 'restart'],
              },
              {
                'id': 'provider-vllm',
                'adapter': 'vllm',
                'type': 'llm',
                'category': 'inference',
                'status': 'ready',
                'health': 'healthy',
                'capacity': 20,
                'in_flight': 0,
                'queued': 0,
                'load_ratio': 0.0,
                'served_models': ['vicuna'],
                'lifecycle_capabilities': ['start', 'stop'],
              },
            ],
          },
        ],
        'capabilities': [
          {
            'kind': 'Serving',
            'available': true,
            'status': 'ready',
            'summary': 'Active',
          },
        ],
        'domain_agents': [],
        'metadata': {'region': 'us-west'},
        'error': '',
      };

      // Parse the raw JSON using the DTO from
      // apps/client/lib/control_plane_status_dto.dart
      final parsed = EdgeStatusResponseView.fromJson(rawJson);

      // Validate top-level fields
      expect(parsed.requestId, equals('test-req-001'));
      expect(parsed.edgeId, equals('edge-test'));
      expect(parsed.edgeName, equals('Edge Test Node'));
      expect(parsed.observedTimeUnixNano, equals(1716584400000000000));
      expect(parsed.nodes.length, equals(1));
      expect(parsed.error, equals(''));

      // Validate node fields
      final node = parsed.nodes.first;
      expect(node.nodeId, equals('node-1'));
      expect(node.alias, equals('Test Node'));
      expect(node.label, equals('GPU-A100'));
      expect(node.connected, isTrue);
      expect(node.config, isNotNull);
      expect(node.config!.adapters.length, equals(1));
      expect(node.config!.adapters.first.type, equals('ollama'));
      expect(node.config!.adapters.first.enabled, isTrue);
      expect(node.config!.concurrency, equals(8));

      // Validate provider_snapshots fields
      final prov0 = node.providerSnapshots[0];
      expect(prov0.id, equals('provider-ollama'));
      expect(prov0.adapter, equals('ollama'));
      expect(prov0.type, equals('llm'));
      expect(prov0.category, equals('inference'));
      expect(prov0.status, equals('active'));
      expect(prov0.health, equals('degraded'));
      expect(prov0.capacity, equals(10));
      expect(prov0.inFlight, equals(3));
      expect(prov0.queued, equals(1));
      expect(prov0.loadRatio, equals(0.3));
      expect(prov0.servedModels, equals(['llama-3.1', 'mistral']));
      expect(
        prov0.lifecycleCapabilities,
        equals(['start', 'stop', 'restart']),
      );

      // Validate second provider snapshot
      final prov1 = node.providerSnapshots[1];
      expect(prov1.id, equals('provider-vllm'));
      expect(prov1.adapter, equals('vllm'));
      expect(prov1.status, equals('ready'));
      expect(prov1.health, equals('healthy'));
      expect(prov1.capacity, equals(20));
      expect(prov1.inFlight, equals(0));
      expect(prov1.queued, equals(0));
      expect(prov1.loadRatio, equals(0.0));
      expect(prov1.servedModels, equals(['vicuna']));
      expect(prov1.lifecycleCapabilities, equals(['start', 'stop']));

      // Validate capabilities and metadata
      expect(parsed.capabilities.length, equals(1));
      expect(parsed.capabilities.first.kind, equals('Serving'));
      expect(parsed.capabilities.first.available, isTrue);
      expect(parsed.metadata, equals({'region': 'us-west'}));

      // Verify that the same raw JSON can also be rendered in the widget.
      // This connects the DTO parsing path to the UI path.
      final statusView = EdgeStatusResponseView.fromJson(rawJson);
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: NodesPanel(
              edges: [
                FleetEdgeView(
                  edgeId: 'edge-test',
                  edgeName: 'Edge Test Node',
                  version: '1.0.0',
                  protocol: 'iop-wire-v1',
                  connected: true,
                  health: 'healthy',
                  lastSeen: DateTime.now(),
                  nodeCount: 1,
                  capabilities: [],
                  domainAgents: [],
                  error: '',
                ),
              ],
              selectedEdgeId: 'edge-test',
              selectedEdgeStatus: statusView,
              isLoading: false,
              error: null,
              onSelectEdge: (_) {},
              onRefresh: () {},
            ),
          ),
        ),
      );
      await tester.pump();

      // Verify degraded provider shows correctly via _stateColor semantics
      expect(find.textContaining('DEGRADED'), findsOneWidget);
      expect(find.textContaining('HEALTHY'), findsOneWidget);
      expect(find.textContaining('provider-ollama'), findsOneWidget);
      expect(find.textContaining('provider-vllm'), findsOneWidget);
    },
  );

  // [G03] Control Plane predecessor values: health=available/status=available
  testWidgets(
    'Provider with health=available and status=available shows AVAILABLE text with green color',
    (WidgetTester tester) async {
      // Test uses standalone NodesPanel with available providers.
      // The DTO test below covers JSON parsing for all predecessor values.
      final statusView = EdgeStatusResponseView.fromJson({
        'request_id': 'test-req-g03-solo',
        'edge_id': 'edge-g03',
        'edge_name': 'Edge G03 Test',
        'observed_time_unix_nano': 1716584400000000000,
        'nodes': [
          {
            'node_id': 'node-g03',
            'alias': 'G03 Test Node',
            'label': 'GPU-Test',
            'connected': true,
            'config': {
              'adapters': [
                {'type': 'ollama', 'enabled': true},
              ],
              'concurrency': 4,
            },
            'provider_snapshots': [
              {
                'id': 'provider-available',
                'adapter': 'ollama',
                'type': 'llm',
                'category': 'inference',
                'status': 'available',
                'health': 'available',
                'capacity': 10,
                'in_flight': 2,
                'queued': 0,
                'load_ratio': 0.2,
                'served_models': ['llama-3.1'],
                'lifecycle_capabilities': ['start', 'stop'],
              },
            ],
          },
        ],
        'capabilities': [],
        'domain_agents': [],
        'metadata': {},
        'error': '',
      });

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: NodesPanel(
              edges: [
                FleetEdgeView(
                  edgeId: 'edge-g03',
                  edgeName: 'Edge G03 Test',
                  version: '1.0.0',
                  protocol: 'iop-wire-v1',
                  connected: true,
                  health: 'healthy',
                  lastSeen: DateTime.now(),
                  nodeCount: 1,
                  capabilities: [],
                  domainAgents: [],
                  error: '',
                ),
              ],
              selectedEdgeId: 'edge-g03',
              selectedEdgeStatus: statusView,
              isLoading: false,
              error: null,
              onSelectEdge: (_) {},
              onRefresh: () {},
            ),
          ),
        ),
      );
      await tester.pump();

      // health=available/status=available should show AVAILABLE text in green (#10B981)
      expect(find.text('AVAILABLE'), findsOneWidget);
      expect(find.text('provider-available'), findsOneWidget);

      // [G02] Status label color assertions
      // Find the AVAILABLE status label Text widget (inside Container) and verify its color
      final availableLabelFinder = find.byWidgetPredicate((Widget widget) {
        if (widget is Text) {
          return widget.data == 'AVAILABLE' && widget.style != null;
        }
        return false;
      });
      expect(availableLabelFinder, findsOneWidget);
      final availableLabel = availableLabelFinder.evaluate().first.widget as Text;
      expect(availableLabel.style?.color, equals(const Color(0xFF10B981)));
    },
  );

  // [G03] DTO JSON parsing test for Control Plane predecessor values
  testWidgets(
    'EdgeStatusResponseView parses health=available/status=available and health=unavailable/status=backlog',
    (WidgetTester tester) async {
      final rawJson = {
        'request_id': 'test-req-g03',
        'edge_id': 'edge-g03',
        'edge_name': 'Edge G03 Test',
        'observed_time_unix_nano': 1716584400000000000,
        'nodes': [
          {
            'node_id': 'node-g03',
            'alias': 'G03 Test Node',
            'label': 'GPU-Test',
            'connected': true,
            'config': {
              'adapters': [
                {'type': 'ollama', 'enabled': true},
              ],
              'concurrency': 4,
            },
            'provider_snapshots': [
              {
                // health=available, status=available -> green
                'id': 'provider-available',
                'adapter': 'ollama',
                'type': 'llm',
                'category': 'inference',
                'status': 'available',
                'health': 'available',
                'capacity': 10,
                'in_flight': 2,
                'queued': 0,
                'load_ratio': 0.2,
                'served_models': ['llama-3.1'],
                'lifecycle_capabilities': ['start', 'stop'],
              },
              {
                // health=unavailable, status=backlog -> red
                'id': 'provider-unavailable',
                'adapter': 'vllm',
                'type': 'llm',
                'category': 'inference',
                'status': 'backlog',
                'health': 'unavailable',
                'capacity': 20,
                'in_flight': 0,
                'queued': 0,
                'load_ratio': 0.0,
                'served_models': [],
                'lifecycle_capabilities': ['start', 'stop'],
              },
              {
                // health=unknown, status=unknown -> neutral grey
                'id': 'provider-unknown',
                'adapter': 'openai',
                'type': 'llm',
                'category': 'external',
                'status': 'unknown',
                'health': '',
                'capacity': 100,
                'in_flight': 0,
                'queued': 0,
                'load_ratio': 0.0,
                'served_models': ['gpt-4'],
                'lifecycle_capabilities': [],
              },
            ],
          },
        ],
        'capabilities': [],
        'domain_agents': [],
        'metadata': {},
        'error': '',
      };

      // Parse the raw JSON
      final parsed = EdgeStatusResponseView.fromJson(rawJson);
      expect(parsed.requestId, equals('test-req-g03'));
      expect(parsed.edgeId, equals('edge-g03'));
      expect(parsed.nodes.length, equals(1));

      // Validate provider snapshots
      final prov0 = parsed.nodes[0].providerSnapshots[0];
      expect(prov0.id, equals('provider-available'));
      expect(prov0.health, equals('available'));
      expect(prov0.status, equals('available'));

      final prov1 = parsed.nodes[0].providerSnapshots[1];
      expect(prov1.id, equals('provider-unavailable'));
      expect(prov1.health, equals('unavailable'));
      expect(prov1.status, equals('backlog'));

      final prov2 = parsed.nodes[0].providerSnapshots[2];
      expect(prov2.id, equals('provider-unknown'));
      expect(prov2.health, equals(''));
      expect(prov2.status, equals('unknown'));

      // Verify that the same raw JSON can be rendered in the widget.
      final statusView = EdgeStatusResponseView.fromJson(rawJson);
      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: NodesPanel(
              edges: [
                FleetEdgeView(
                  edgeId: 'edge-g03',
                  edgeName: 'Edge G03 Test',
                  version: '1.0.0',
                  protocol: 'iop-wire-v1',
                  connected: true,
                  health: 'healthy',
                  lastSeen: DateTime.now(),
                  nodeCount: 1,
                  capabilities: [],
                  domainAgents: [],
                  error: '',
                ),
              ],
              selectedEdgeId: 'edge-g03',
              selectedEdgeStatus: statusView,
              isLoading: false,
              error: null,
              onSelectEdge: (_) {},
              onRefresh: () {},
            ),
          ),
        ),
      );
      await tester.pump();

      // Verify available provider shows AVAILABLE text
      expect(find.text('AVAILABLE'), findsWidgets);
      expect(find.textContaining('provider-available'), findsOneWidget);

      // [G02] UNAVAILABLE status label color assertion (#EF4444 red)
      final unavailableLabelFinder = find.byWidgetPredicate((Widget widget) {
        if (widget is Text) {
          return widget.data == 'UNAVAILABLE' && widget.style != null;
        }
        return false;
      });
      expect(unavailableLabelFinder, findsOneWidget);
      final unavailableLabel = unavailableLabelFinder.evaluate().first.widget as Text;
      expect(unavailableLabel.style?.color, equals(const Color(0xFFEF4444)));

      // [G02] UNKNOWN status label color assertion (#64748B grey)
      // For provider-unknown, health='' so status='unknown' -> _stateColor returns Color(0xFF64748B)
      // displayState = p.health.isNotEmpty ? p.health : p.status = 'unknown'
      // displayState.toUpperCase() = 'UNKNOWN'
      final unknownLabelFinder = find.byWidgetPredicate((Widget widget) {
        if (widget is Text) {
          return widget.data == 'UNKNOWN' && widget.style != null;
        }
        return false;
      });
      expect(unknownLabelFinder, findsOneWidget);
      final unknownLabel = unknownLabelFinder.evaluate().first.widget as Text;
      expect(unknownLabel.style?.color, equals(const Color(0xFF64748B)));

      // Verify unavailable provider shows UNAVAILABLE text
      expect(find.text('UNAVAILABLE'), findsWidgets);
      expect(find.textContaining('provider-unavailable'), findsOneWidget);

      // Verify unknown provider shows UNKNOWN text (from status)
      expect(find.text('UNKNOWN'), findsWidgets);
      expect(find.textContaining('provider-unknown'), findsOneWidget);
    },
  );

  testWidgets(
    'Client App mobile screen layout verification for layout and overflow',
    (WidgetTester tester) async {
      // Set a small mobile screen size
      tester.view.physicalSize = const Size(360, 640);
      tester.view.devicePixelRatio = 1.0;

      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();

      await tester.pumpWidget(
        IopClientApp(testClient: fakeClient, statusRepository: fakeStatusRepo),
      );
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      // Overview
      expect(find.text('Operations Overview'), findsOneWidget);

      // Switch to Edges panel
      await tester.tap(find.byTooltip('Edges'));
      await tester.pump();
      expect(find.text('Edge Alpha'), findsNWidgets(2));

      // Switch to Runtime panel
      await tester.tap(find.byTooltip('Runtime'));
      await tester.pump();
      expect(find.text('Operations & Domain Agents'), findsOneWidget);

      // Reset view size after test
      addTearDown(() {
        tester.view.resetPhysicalSize();
        tester.view.resetDevicePixelRatio();
      });
    },
  );
}
