// Smoke test for the Beta UI banners. Verifies the
// non-production-ready banner appears in both supported locales.
//
// Does NOT call into the FRB Rust side; that requires the cdylib at
// runtime and is verified by alpha_e2e_test.dart + beta_e2e_test.dart.
//
// Requirement trace (SWE.4 unit verification — banner widget):
//   SRS-165..SRS-169 (localization), SRS-170..SRS-176 (Unicode / server content),
//   SAD-014 (design-system + localization integration),
//   SDD-031, SDD-032, SDD-033, SDD-034 (localization fallback + product-vs-server text).
//   Verification-plan rows: SWE4-UV-014, SWE4-UV-019, SWE4-UV-020 (swe4-unit-verification-plan.md).

import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

// ignore_for_file: deprecated_member_use

import 'package:chanora_flutter/l10n/generated/app_localizations.dart';
import 'package:chanora_flutter/main.dart';
import 'package:chanora_flutter/services/android_permissions_service.dart';
import 'package:chanora_flutter/services/ios_permissions_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:chanora_flutter/widgets/permission_state_banner.dart';
import 'package:chanora_flutter/widgets/voice_compact.dart';

void main() {
  testWidgets('theme menu emits selected mode from user action', (tester) async {
    SharedPreferences.setMockInitialValues({});
    var selected = ThemeMode.system;

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            actions: [
              ChanoraThemeModeMenu(
                themeMode: ThemeMode.system,
                onThemeModeChanged: (mode) async {
                  selected = mode;
                },
              ),
            ],
          ),
        ),
      ),
    );
    await tester.pumpAndSettle();

    await tester.tap(find.byIcon(Icons.palette_outlined));
    await tester.pumpAndSettle();
    await tester.tap(find.text('Dark').last);
    await tester.pumpAndSettle();

    expect(selected, ThemeMode.dark);
  });

  testWidgets('renders English banner', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Builder(
          builder: (ctx) {
            final l10n = AppL10n.of(ctx);
            return Scaffold(body: Text(l10n.homeNotProductionReadyBanner));
          },
        ),
      ),
    );
    await tester.pumpAndSettle();
    expect(find.textContaining('Beta build'), findsOneWidget);
  });

  testWidgets('renders Simplified Chinese banner', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        locale: const Locale('zh'),
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Builder(
          builder: (ctx) {
            final l10n = AppL10n.of(ctx);
            return Scaffold(body: Text(l10n.homeNotProductionReadyBanner));
          },
        ),
      ),
    );
    await tester.pumpAndSettle();
    expect(find.textContaining('Beta 版本'), findsOneWidget);
  });

  testWidgets('localizes diagnostic export save action', (tester) async {
    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Builder(
          builder: (ctx) {
            final l10n = AppL10n.of(ctx);
            return Scaffold(
              body: Column(
                children: [
                  Text(l10n.diagnosticsSaveAction),
                  Text(l10n.diagnosticsSaved('/tmp/chanora-diagnostics.txt')),
                ],
              ),
            );
          },
        ),
      ),
    );
    await tester.pumpAndSettle();
    expect(find.text('Save export'), findsOneWidget);
    expect(find.textContaining('chanora-diagnostics.txt'), findsOneWidget);
  });

  testWidgets('on-screen PTT exposes hold-to-talk semantics', (tester) async {
    final semantics = tester.ensureSemantics();

    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Scaffold(
          body: VoicePttButton(active: false, onHeldChanged: (_) {}),
        ),
      ),
    );
    await tester.pumpAndSettle();

    final node = tester.getSemantics(find.byType(VoicePttButton));
    expect(node.label, 'Hold to talk');
    expect(node.hint, 'Press and hold to transmit voice; release to stop.');
    expect(node.hasFlag(SemanticsFlag.isButton), isTrue);
    expect(node.hasFlag(SemanticsFlag.isLiveRegion), isTrue);
    semantics.dispose();
  });

  testWidgets('permission banner grants denied microphone access', (
    tester,
  ) async {
    final state = ValueNotifier(AndroidRecordAudioPermissionState.denied);
    var grantCount = 0;

    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Scaffold(
          body: PermissionStateBanner.fromCallbacks(
            recordAudioState: state,
            ensureRecordAudio: () async {
              grantCount += 1;
              return AndroidRecordAudioPermissionState.granted;
            },
            openAppSettings: () async {},
          ),
        ),
      ),
    );

    expect(find.text('Grant'), findsOneWidget);
    await tester.tap(find.text('Grant'));
    expect(grantCount, 1);
    state.dispose();
  });

  testWidgets('permission banner opens settings after permanent denial', (
    tester,
  ) async {
    final state = ValueNotifier(
      AndroidRecordAudioPermissionState.permanentlyDenied,
    );
    var settingsCount = 0;

    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Scaffold(
          body: PermissionStateBanner.fromCallbacks(
            recordAudioState: state,
            ensureRecordAudio: () async =>
                AndroidRecordAudioPermissionState.permanentlyDenied,
            openAppSettings: () async {
              settingsCount += 1;
            },
          ),
        ),
      ),
    );

    expect(find.text('Open System Settings'), findsOneWidget);
    await tester.tap(find.text('Open System Settings'));
    expect(settingsCount, 1);
    state.dispose();
  });

  testWidgets('on-screen PTT reports press and release gestures', (
    tester,
  ) async {
    final states = <bool>[];

    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Scaffold(
          body: VoicePttButton(active: false, onHeldChanged: states.add),
        ),
      ),
    );

    final center = tester.getCenter(find.byType(VoicePttButton));
    final gesture = await tester.startGesture(center);
    await tester.pump();
    await gesture.up();
    await tester.pump();

    expect(states, [true, false]);
  });

  testWidgets('on-screen PTT can release from pan gestures', (tester) async {
    final states = <bool>[];

    await tester.pumpWidget(
      MaterialApp(
        localizationsDelegates: AppL10n.localizationsDelegates,
        supportedLocales: AppL10n.supportedLocales,
        home: Scaffold(
          body: VoicePttButton(
            active: false,
            listenForPan: true,
            onHeldChanged: states.add,
          ),
        ),
      ),
    );

    final center = tester.getCenter(find.byType(VoicePttButton));
    final gesture = await tester.startGesture(center);
    await tester.pump();
    await gesture.moveBy(const Offset(0, 24));
    await tester.pump();
    await gesture.up();
    await tester.pump();

    expect(states, [true, false]);
  });

  testWidgets('iOS permission service maps channel states and settings', (
    tester,
  ) async {
    const channel = MethodChannel(iosPlatformChannelName);
    final methods = <String>[];
    TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
        .setMockMethodCallHandler(channel, (call) async {
          methods.add(call.method);
          switch (call.method) {
            case methodGetMicrophonePermissionState:
              return 'NotDetermined';
            case methodRequestMicrophonePermission:
              return 'Granted';
            case methodIosOpenAppSettings:
              return true;
          }
          return null;
        });

    final service = IosPermissionsService(channel: channel);
    await service.start();
    expect(
      service.recordAudioState.value,
      AndroidRecordAudioPermissionState.denied,
    );
    expect(
      await service.ensureRecordAudio(),
      AndroidRecordAudioPermissionState.granted,
    );
    await service.openAppSettings();

    expect(methods, [
      methodGetMicrophonePermissionState,
      methodRequestMicrophonePermission,
      methodIosOpenAppSettings,
    ]);

    service.dispose();
    TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
        .setMockMethodCallHandler(channel, null);
  });
}
