import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:iop_client/control_plane_status_client.dart';
import 'package:iop_client/main.dart';
import 'support/client_test_harness.dart';

void main() {
  _registerPanelRenderingScenario();
  _registerCommandErrorScenario();
  _registerCommandGatingScenario();
  _registerProviderCommandAllowlistScenario();
  _registerOllamaApiPathScenario();
  _registerPendingEmptyHistoryScenario();
  _registerOperationsFetchStateScenario();
}

void _registerPanelRenderingScenario() {
  testWidgets(
    'Client App opens Operations panel and verifies history and provider commands',
    (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));

      _verifyPanelHeaderAndSections(tester);
      _verifyOperationsHistoryItem(tester);
      _verifyTriggerSyncAbsent(tester);
      _verifySystemGatedButtons(tester);

      await _verifyHealthCheckSuccess(tester, fakeStatusRepo);

      fakeStatusRepo.edgeBOperationsCompleter =
          Completer<EdgeOperationsResponseView>();

      await tester.tap(find.byType(DropdownButton<String>));
      await tester.pumpAndSettle();
      await tester.tap(find.text('Edge Beta').last);
      await tester.pump();

      _verifyEdgeSwitchNotLoaded(tester);

      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();

      _verifyEdgeBetaActiveCommand(tester);
      _verifyEdgeBetaOperationsHistory(tester);
      _verifyEdgeBetaOperationsAbsence(tester);

      await tester.tap(find.text('Health Check'));
      await tester.pumpAndSettle();
      expect(fakeStatusRepo.lastCommandEdgeId, equals('edge-b'));
    },
  );
}

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

      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));

      await tester.tap(find.text('Node Status'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      expect(find.text('Get Node 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));

      expect(
        find.textContaining(
          'Failed: unsupported - Operation not supported by Edge',
        ),
        findsOneWidget,
      );
    },
  );
}

void _registerCommandGatingScenario() {
  testWidgets(
    'Client App gates node.status and provider.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));

      await _verifyNodeStatusGating(tester, fakeStatusRepo);
      await _verifyProviderCommandGating(tester, fakeStatusRepo);
    },
  );
}

void _registerPendingEmptyHistoryScenario() {
  testWidgets(
    'RuntimePanel keeps loaded empty history visible while a command is pending',
    (WidgetTester tester) async {
      final fakeClient = FakeClientWireClient(shouldSuccess: true);
      final fakeStatusRepo = FakeControlPlaneStatusRepository();
      fakeStatusRepo.emptyOperationsEdgeId = 'edge-a';

      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));

      expect(find.text('No operation executions recorded.'), findsOneWidget);

      fakeStatusRepo.commandResponseCompleter =
          Completer<EdgeCommandResponseView>();

      await tester.tap(find.text('Health Check'));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 100));

      expect(find.text('No operation executions recorded.'), findsOneWidget);
      expect(find.byType(CircularProgressIndicator), findsNothing);
      expect(fakeStatusRepo.lastCommandOperation, equals('health.check'));
      expect(fakeStatusRepo.lastCommandEdgeId, equals('edge-a'));

      fakeStatusRepo.commandResponseCompleter!.complete(
        EdgeCommandResponseView(
          requestId: 'req-111',
          commandId: 'cmd-pending-test',
          edgeId: 'edge-a',
          status: 'accepted',
          summary: 'Command accepted by control plane',
          error: '',
        ),
      );
      await tester.pumpAndSettle();
    },
  );
}

void _registerOperationsFetchStateScenario() {
  testWidgets('RuntimePanel renders operations empty and fetch error states', (
    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));

    fakeStatusRepo.emptyOperationsEdgeId = 'edge-a';
    fakeStatusRepo.fetchOperationsError = null;

    await _reFetchOperations(tester);

    expect(find.text('No operation executions recorded.'), findsOneWidget);

    fakeStatusRepo.fetchOperationsError = Exception('repository failure');
    fakeStatusRepo.emptyOperationsEdgeId = null;

    await _reFetchOperations(tester);

    expect(find.textContaining('Error:'), findsOneWidget);
    expect(find.textContaining('repository failure'), findsOneWidget);
  });
}

void _verifyPanelHeaderAndSections(WidgetTester tester) {
  expect(find.text('Operations'), findsOneWidget);
  expect(find.text('Operations Execution History'), findsOneWidget);
  expect(find.text('System Gated Operations'), findsOneWidget);
}

void _verifyOperationsHistoryItem(WidgetTester tester) {
  expect(find.text('deployer.sync'), findsOneWidget);
  expect(find.text('SUCCESS'), findsOneWidget);
  expect(find.textContaining('Synced 5 models'), findsOneWidget);
}

void _verifyTriggerSyncAbsent(WidgetTester tester) {
  expect(find.text('Trigger Sync'), findsNothing);
}

void _verifySystemGatedButtons(WidgetTester tester) {
  expect(find.text('Health Check'), findsOneWidget);
  expect(find.text('Node Status'), findsOneWidget);
  expect(find.text('Provider Command'), findsOneWidget);
}

Future<void> _verifyHealthCheckSuccess(
  WidgetTester tester,
  FakeControlPlaneStatusRepository fakeStatusRepo,
) async {
  await tester.tap(find.text('Health Check'));
  await tester.pump();
  await tester.pump(const Duration(milliseconds: 100));

  expect(find.textContaining('Success: Command accepted'), findsOneWidget);
  expect(fakeStatusRepo.lastCommandEdgeId, equals('edge-a'));
}

void _verifyEdgeSwitchNotLoaded(WidgetTester tester) {
  expect(find.text('deployer.sync'), findsNothing);
  expect(find.textContaining('Synced 5 models'), findsNothing);
  expect(find.byType(CircularProgressIndicator), findsOneWidget);
}

void _verifyEdgeBetaActiveCommand(WidgetTester tester) {
  expect(find.text('build-deploy.deploy'), findsOneWidget);
  expect(find.textContaining('Deploy queued for edge beta'), findsOneWidget);
}

void _verifyEdgeBetaOperationsHistory(WidgetTester tester) {
  expect(find.text('build-deploy.deploy'), findsOneWidget);
}

void _verifyEdgeBetaOperationsAbsence(WidgetTester tester) {
  expect(find.text('deployer.sync'), findsNothing);
  expect(find.textContaining('Synced 5 models'), findsNothing);
}

Future<void> _verifyNodeStatusGating(
  WidgetTester tester,
  FakeControlPlaneStatusRepository fakeStatusRepo,
) async {
  await tester.tap(find.text('Node Status'));
  await tester.pump();
  expect(find.text('Get Node Status'), findsOneWidget);

  await tester.enterText(find.byType(TextField), '');
  await tester.tap(find.text('Send'));
  await tester.pump();

  expect(find.text('Get Node Status'), findsOneWidget);
  expect(fakeStatusRepo.lastCommandOperation, isNull);

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

Future<void> _verifyProviderCommandGating(
  WidgetTester tester,
  FakeControlPlaneStatusRepository fakeStatusRepo,
) async {
  await tester.tap(find.text('Provider Command'));
  await tester.pump();
  expect(find.text('Send Provider Command'), findsOneWidget);

  // The dialog exposes only the selector as a free-form field; the command is
  // constrained to an allowlisted dropdown, so no arbitrary command can be typed.
  expect(find.byType(TextField), findsOneWidget);
  expect(find.byType(DropdownButtonFormField<String>), findsOneWidget);

  // No selector and no command selected: Send is inert.
  await tester.tap(find.text('Send'));
  await tester.pump();
  expect(find.text('Send Provider Command'), findsOneWidget);
  expect(fakeStatusRepo.lastCommandOperation, isNull);

  // Selector present but still no command selected: Send is inert.
  await tester.enterText(find.byType(TextField), 'node-1');
  await tester.tap(find.text('Send'));
  await tester.pump();
  expect(find.text('Send Provider Command'), findsOneWidget);
  expect(fakeStatusRepo.lastCommandOperation, isNull);

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

void _registerProviderCommandAllowlistScenario() {
  testWidgets('Client App sends only allowlisted provider commands', (
    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));

    for (final command in const ['capabilities', 'transport_status']) {
      await _sendAllowlistedCommand(tester, command);
      expect(fakeStatusRepo.lastCommandOperation, equals('provider.command'));
      expect(fakeStatusRepo.lastCommandTargetSelector, equals('node-1'));
      expect(
        fakeStatusRepo.lastCommandParameters,
        equals({'command': command}),
      );
    }

    // Confirm the dropdown only offers the three allowlisted commands.
    await tester.tap(find.text('Provider Command'));
    await tester.pump();
    await tester.tap(find.byType(DropdownButtonFormField<String>));
    await tester.pumpAndSettle();
    expect(find.text('capabilities'), findsWidgets);
    expect(find.text('transport_status'), findsWidgets);
    expect(find.text('ollama_api'), findsWidgets);
    expect(find.text('node.status'), findsNothing);
    expect(find.text('provider.command'), findsNothing);
    await tester.tap(find.text('capabilities').last);
    await tester.pumpAndSettle();
    await tester.tap(find.text('Cancel'));
    await tester.pump();
  });
}

void _registerOllamaApiPathScenario() {
  testWidgets('Client App requires Ollama API path', (
    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));

    await tester.tap(find.text('Provider Command'));
    await tester.pump();
    await tester.tap(find.byType(DropdownButtonFormField<String>).first);
    await tester.pumpAndSettle();
    await tester.tap(find.text('ollama_api').last);
    await tester.pumpAndSettle();

    // With ollama_api selected the method dropdown and path field appear.
    expect(find.byType(TextField), findsNWidgets(2));

    await tester.enterText(find.byType(TextField).at(0), 'node-1');
    // Empty path: Send stays gated and never reaches the repository.
    await tester.tap(find.text('Send'));
    await tester.pump();
    expect(find.text('Send Provider Command'), findsOneWidget);
    expect(fakeStatusRepo.lastCommandOperation, isNull);

    await tester.enterText(find.byType(TextField).at(1), '/api/tags');
    await tester.tap(find.text('Send'));
    await tester.pumpAndSettle();

    expect(fakeStatusRepo.lastCommandOperation, equals('provider.command'));
    expect(fakeStatusRepo.lastCommandTargetSelector, equals('node-1'));
    expect(
      fakeStatusRepo.lastCommandParameters,
      equals({'command': 'ollama_api', 'method': 'GET', 'path': '/api/tags'}),
    );
  });
}

Future<void> _sendAllowlistedCommand(
  WidgetTester tester,
  String command,
) async {
  await tester.tap(find.text('Provider Command'));
  await tester.pump();
  expect(find.text('Send Provider Command'), findsOneWidget);

  await tester.enterText(find.byType(TextField), 'node-1');
  await tester.tap(find.byType(DropdownButtonFormField<String>));
  await tester.pumpAndSettle();
  await tester.tap(find.text(command).last);
  await tester.pumpAndSettle();

  await tester.tap(find.text('Send'));
  await tester.pumpAndSettle();
}

Future<void> _reFetchOperations(WidgetTester tester) async {
  await tester.tap(find.byIcon(Icons.refresh));
  await tester.pump();
  await tester.pump(const Duration(milliseconds: 100));
}
