// Copyright 2014 The Flutter 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_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/devtools_launcher.dart';
import 'package:flutter_tools/src/resident_runner.dart';

import '../src/common.dart';
import '../src/fake_process_manager.dart';
import '../src/fakes.dart';

void main() {
  late BufferLogger logger;

  Cache.flutterRoot = '';

  setUp(() {
    logger = BufferLogger.test();
  });

  testWithoutContext('DevtoolsLauncher launches DevTools from the SDK and saves the URI', () async {
    final Completer<void> completer = Completer<void>();
    final DevtoolsLauncher launcher = DevtoolsServerLauncher(
      dartExecutable: 'dart',
      logger: logger,
      botDetector: const FakeBotDetector(false),
      processManager: FakeProcessManager.list(<FakeCommand>[
        FakeCommand(
          command: const <String>[
            'dart',
            'devtools',
            '--no-launch-browser',
          ],
          stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
          completer: completer,
        ),
      ]),
    );

    final DevToolsServerAddress? address = await launcher.serve();
    expect(address?.host, '127.0.0.1');
    expect(address?.port, 9100);
  });

  testWithoutContext('DevtoolsLauncher does not launch a new DevTools instance if one is already active', () async {
    final Completer<void> completer = Completer<void>();
    final DevtoolsLauncher launcher = DevtoolsServerLauncher(
      dartExecutable: 'dart',
      logger: logger,
      botDetector: const FakeBotDetector(false),
      processManager: FakeProcessManager.list(<FakeCommand>[
        FakeCommand(
          command: const <String>[
            'dart',
            'devtools',
            '--no-launch-browser',
          ],
          stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
          completer: completer,
        ),
      ]),
    );

    DevToolsServerAddress? address = await launcher.serve();
    expect(address?.host, '127.0.0.1');
    expect(address?.port, 9100);

    // Call `serve` again and verify that the already running server is returned.
    address = await launcher.serve();
    expect(address?.host, '127.0.0.1');
    expect(address?.port, 9100);
  });

  testWithoutContext('DevtoolsLauncher can launch devtools with a memory profile', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(
        command: <String>[
          'dart',
          'devtools',
          '--no-launch-browser',
          '--vm-uri=localhost:8181/abcdefg',
          '--profile-memory=foo',
        ],
        stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
      ),
    ]);
    final DevtoolsLauncher launcher = DevtoolsServerLauncher(
      dartExecutable: 'dart',
      logger: logger,
      botDetector: const FakeBotDetector(false),
      processManager: processManager,
    );

    await launcher.launch(Uri.parse('localhost:8181/abcdefg'), additionalArguments: <String>['--profile-memory=foo']);

    expect(launcher.processStart, completes);
    expect(processManager, hasNoRemainingExpectations);
  });

  testWithoutContext('DevtoolsLauncher prints error if exception is thrown during launch', () async {
    final DevtoolsLauncher launcher = DevtoolsServerLauncher(
      dartExecutable: 'dart',
      logger: logger,
      botDetector: const FakeBotDetector(false),
      processManager: FakeProcessManager.list(<FakeCommand>[
        const FakeCommand(
          command: <String>[
            'dart',
            'devtools',
            '--no-launch-browser',
            '--vm-uri=http://127.0.0.1:1234/abcdefg',
          ],
          exception: ProcessException('pub', <String>[]),
        ),
      ]),
    );

    await launcher.launch(Uri.parse('http://127.0.0.1:1234/abcdefg'));

    expect(logger.errorText, contains('Failed to launch DevTools: ProcessException'));
  });

  testWithoutContext('DevtoolsLauncher handles failure of DevTools process on a bot', () async {
    final Completer<void> completer = Completer<void>();
    final DevtoolsServerLauncher launcher = DevtoolsServerLauncher(
      dartExecutable: 'dart',
      logger: logger,
      botDetector: const FakeBotDetector(true),
      processManager: FakeProcessManager.list(<FakeCommand>[
        FakeCommand(
          command: const <String>[
            'dart',
            'devtools',
            '--no-launch-browser',
          ],
          stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
          completer: completer,
          exitCode: 255,
        ),
      ]),
    );

    await launcher.launch(null);
    completer.complete();
    expect(launcher.devToolsProcessExit, throwsToolExit());
  });
}