// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'package:flutter/services.dart';
import 'basic_messaging.dart';
import 'test_step.dart';

Future<TestStepResult> methodCallJsonSuccessHandshake(dynamic payload) async {
  const MethodChannel channel =
      MethodChannel('json-method', JSONMethodCodec());
  return _methodCallSuccessHandshake(
      'JSON success($payload)', channel, payload);
}

Future<TestStepResult> methodCallJsonErrorHandshake(dynamic payload) async {
  const MethodChannel channel =
      MethodChannel('json-method', JSONMethodCodec());
  return _methodCallErrorHandshake('JSON error($payload)', channel, payload);
}

Future<TestStepResult> methodCallJsonNotImplementedHandshake() async {
  const MethodChannel channel =
      MethodChannel('json-method', JSONMethodCodec());
  return _methodCallNotImplementedHandshake('JSON notImplemented()', channel);
}

Future<TestStepResult> methodCallStandardSuccessHandshake(
    dynamic payload) async {
  const MethodChannel channel = MethodChannel(
    'std-method',
    StandardMethodCodec(ExtendedStandardMessageCodec()),
  );
  return _methodCallSuccessHandshake(
      'Standard success($payload)', channel, payload);
}

Future<TestStepResult> methodCallStandardErrorHandshake(dynamic payload) async {
  const MethodChannel channel = MethodChannel(
    'std-method',
    StandardMethodCodec(ExtendedStandardMessageCodec()),
  );
  return _methodCallErrorHandshake(
      'Standard error($payload)', channel, payload);
}

Future<TestStepResult> methodCallStandardNotImplementedHandshake() async {
  const MethodChannel channel = MethodChannel(
    'std-method',
    StandardMethodCodec(ExtendedStandardMessageCodec()),
  );
  return _methodCallNotImplementedHandshake(
      'Standard notImplemented()', channel);
}

Future<TestStepResult> _methodCallSuccessHandshake(
  String description,
  MethodChannel channel,
  dynamic arguments,
) async {
  final List<dynamic> received = <dynamic>[];
  channel.setMethodCallHandler((MethodCall call) async {
    received.add(call.arguments);
    return call.arguments;
  });
  dynamic result = nothing;
  dynamic error = nothing;
  try {
    result = await channel.invokeMethod<dynamic>('success', arguments);
  } catch (e) {
    error = e;
  }
  return resultOfHandshake(
    'Method call success handshake',
    description,
    arguments,
    received,
    result,
    error,
  );
}

Future<TestStepResult> _methodCallErrorHandshake(
  String description,
  MethodChannel channel,
  dynamic arguments,
) async {
  final List<dynamic> received = <dynamic>[];
  channel.setMethodCallHandler((MethodCall call) async {
    received.add(call.arguments);
    throw PlatformException(
        code: 'error', message: null, details: arguments);
  });
  dynamic errorDetails = nothing;
  dynamic error = nothing;
  try {
    error = await channel.invokeMethod<dynamic>('error', arguments);
  } on PlatformException catch (e) {
    errorDetails = e.details;
  } catch (e) {
    error = e;
  }
  return resultOfHandshake(
    'Method call error handshake',
    description,
    arguments,
    received,
    errorDetails,
    error,
  );
}

Future<TestStepResult> _methodCallNotImplementedHandshake(
  String description,
  MethodChannel channel,
) async {
  final List<dynamic> received = <dynamic>[];
  channel.setMethodCallHandler((MethodCall call) async {
    received.add(call.arguments);
    throw MissingPluginException();
  });
  dynamic result = nothing;
  dynamic error = nothing;
  try {
    error = await channel.invokeMethod<dynamic>('notImplemented');
  } on MissingPluginException {
    result = null;
  } catch (e) {
    error = e;
  }
  return resultOfHandshake(
    'Method call not implemented handshake',
    description,
    null,
    received,
    result,
    error,
  );
}
