import 'dart:convert';

import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:qlyra/frontend/screens/webapp/web_app_bridge.dart';

void main() {
  late List<(String, Map<String, dynamic>, bool)> sent;
  late int closeCalls;

  WebAppBridge buildBridge({
    bool privateChannel = false,
    String entryPoint = WebAppEntryPoint.webApp,
  }) {
    return WebAppBridge(
      botId: 777,
      entryPoint: entryPoint,
      privateChannel: privateChannel,
      contextResolver: () => null,
      viewportResolver: () => const Size(420, 800),
      onClose: () => closeCalls++,
      emitter: (method, payload, private) => sent.add((
        method,
        jsonDecode(payload) as Map<String, dynamic>,
        private,
      )),
    );
  }

  setUp(() {
    sent = [];
    closeCalls = 0;
  });

  test('reports the launch context it was created with', () async {
    final bridge = buildBridge(entryPoint: WebAppEntryPoint.inlineButton);

    await bridge.handleEvent(
      'WebAppGetLaunchContext',
      '{"requestId":"r1"}',
      false,
    );

    expect(sent, hasLength(1));
    expect(sent.first.$1, 'WebAppGetLaunchContext');
    expect(sent.first.$2, {'requestId': 'r1', 'entryPoint': 'inline_button'});
  });

  test('answers viewport requests with the current webview size', () async {
    final bridge = buildBridge();

    await bridge.handleEvent(
      'WebAppGetViewportSize',
      '{"requestId":"r2"}',
      false,
    );

    expect(sent.first.$2['width'], 420);
    expect(sent.first.$2['height'], 800);
    expect(sent.first.$2['isStateStable'], isTrue);
  });

  test('rejects an unknown method with the client error code', () async {
    final bridge = buildBridge();

    await bridge.handleEvent(
      'WebAppSomethingElse',
      '{"requestId":"r3"}',
      false,
    );

    expect(sent.first.$2['error'], {
      'code': 'client.unsupported_method.unsupported_method',
    });
  });

  test('stays silent for methods that never get an answer', () async {
    final bridge = buildBridge();

    await bridge.handleEvent('WebAppReady', '{}', false);
    await bridge.handleEvent('WebAppStat', '{}', false);

    expect(sent, isEmpty);
  });

  test('drops gesture-gated methods until the user touches the page', () async {
    final bridge = buildBridge();

    await bridge.handleEvent(
      'WebAppShare',
      '{"requestId":"r4","text":"hi"}',
      false,
    );
    expect(sent, isEmpty);

    bridge.registerGesture();
    await bridge.handleEvent('WebAppShare', '{"requestId":"r5"}', false);

    expect(sent.single.$2['error'], {
      'code': 'client.web_app_share.invalid_request',
    });
  });

  test('ignores private-channel events when the channel is off', () async {
    final bridge = buildBridge();

    await bridge.handleEvent(
      'WebAppVerifyMobileId',
      '{"requestId":"r6","url":"https://example.test/verify"}',
      true,
    );

    expect(sent, isEmpty);
  });

  test('reports malformed payloads as a decode error', () async {
    final bridge = buildBridge();

    await bridge.handleEvent('WebAppGetViewportSize', 'not-json', false);

    expect(sent, isEmpty);
  });

  test(
    'tracks the back button and closing behaviour the app asked for',
    () async {
      final bridge = buildBridge();

      expect(bridge.handlesBackButton, isFalse);
      expect(bridge.needsCloseConfirmation, isFalse);

      await bridge.handleEvent(
        'WebAppSetupBackButton',
        '{"isVisible":true}',
        false,
      );
      await bridge.handleEvent(
        'WebAppSetupClosingBehavior',
        '{"needConfirmation":true}',
        false,
      );

      expect(bridge.handlesBackButton, isTrue);
      expect(bridge.needsCloseConfirmation, isTrue);

      bridge.notifyBackPressed();
      expect(sent.single.$1, 'WebAppBackButtonPressed');
    },
  );

  test('closes the screen when the app asks to', () async {
    final bridge = buildBridge();

    await bridge.handleEvent('WebAppClose', '{}', false);

    expect(closeCalls, 1);
  });

  test('echoes the screen capture behaviour back', () async {
    final bridge = buildBridge();

    await bridge.handleEvent(
      'WebAppSetupScreenCaptureBehavior',
      '{"requestId":"r7","isScreenCaptureEnabled":true}',
      false,
    );

    expect(sent.first.$2, {'requestId': 'r7', 'isScreenCaptureEnabled': true});
  });

  test('answers NFC availability without pretending to support it', () async {
    final bridge = buildBridge();

    await bridge.handleEvent('WebAppNfcGetInfo', '{"requestId":"r8"}', false);
    await bridge.handleEvent(
      'WebAppNfcEmulateNfcTag',
      '{"requestId":"r9"}',
      false,
    );

    expect(sent[0].$2, {
      'requestId': 'r8',
      'available': false,
      'enabled': false,
    });
    expect(sent[1].$2['error'], {
      'code': 'client.nfc_emulate_nfc_tag.not_supported',
    });
  });
}
