import 'dart:io';
import 'package:test/test.dart';
import 'package:oto/cli/cli.dart';
import 'package:oto/cli/commands/command_agent.dart';
import 'package:oto/oto/agent/agent_config.dart';
import 'package:oto/oto/agent/agent_runner.dart';

class FakeAgentRunner implements AgentRunner {
  AgentConfig? lastConfig;
  int callCount = 0;
  Future<void> Function(AgentConfig)? onRun;

  @override
  Future<void> run(AgentConfig config) async {
    lastConfig = config;
    callCount++;
    if (onRun != null) {
      await onRun!(config);
    }
  }
}

void main() {
  const validBootstrapYaml = '''
agent:
  id: "agent-abc"
  alias: "my-agent"
  enrollment_token: "token-123"
edge:
  url: "https://edge.toki-labs.com:8443"
runtime:
  install_dir: "/usr/bin"
  workspace_root: "/var/oto"
  log_dir: "/var/log/oto"
''';

  late File tempFile;

  setUp(() {
    tempFile = File('${Directory.systemTemp.path}/agent_config_cli_test.yaml');
    tempFile.writeAsStringSync(validBootstrapYaml);
    CLI.logFunc = (msg) {};
    CLI.initialize('oto', [], []);
  });

  tearDown(() {
    if (tempFile.existsSync()) {
      tempFile.deleteSync();
    }
  });

  test('CommandAgent parses run config and calls runner', () async {
    final runner = FakeAgentRunner();
    final command = CommandAgent(runner: runner);

    await command.execute(['run', '--config', tempFile.path]);

    expect(runner.callCount, 1);
    expect(runner.lastConfig, isNotNull);
    expect(runner.lastConfig!.agent.id, 'agent-abc');
    expect(runner.lastConfig!.edge.url, 'https://edge.toki-labs.com:8443');
    expect(runner.lastConfig!.runtime.installDir, '/usr/bin');
  });

  test('CommandAgent parses run config with equals option and calls runner',
      () async {
    final runner = FakeAgentRunner();
    final command = CommandAgent(runner: runner);

    await command.execute(['run', '--config=${tempFile.path}']);

    expect(runner.callCount, 1);
    expect(runner.lastConfig!.agent.id, 'agent-abc');
  });

  test('CommandAgent rejects unknown subcommand', () async {
    final runner = FakeAgentRunner();
    final command = CommandAgent(runner: runner);

    await command.execute(['unknown_sub']);
    expect(runner.callCount, 0);
  });

  test('CommandAgent rejects missing config parameter', () async {
    final runner = FakeAgentRunner();
    final command = CommandAgent(runner: runner);

    await command.execute(['run']);
    expect(runner.callCount, 0);
  });

  test('CommandAgent sets exit code zero on accepted registration', () async {
    final runner = FakeAgentRunner();
    runner.onRun = (config) async {
      // success
    };
    final command = CommandAgent(runner: runner);

    final initialExitCode = exitCode;
    exitCode = 0;

    await command.execute(['run', '--config', tempFile.path]);

    expect(runner.callCount, 1);
    expect(exitCode, 0);

    exitCode = initialExitCode;
  });

  test('CommandAgent sets exit code ten on rejected registration', () async {
    final runner = FakeAgentRunner();
    runner.onRun = (config) async {
      throw RegistrationException('Rejected by server');
    };
    final command = CommandAgent(runner: runner);

    final initialExitCode = exitCode;
    exitCode = 0;

    await command.execute(['run', '--config', tempFile.path]);

    expect(runner.callCount, 1);
    expect(exitCode, 10);

    exitCode = initialExitCode;
  });

  // Integration tests calling actual binary
  test('actual bin agent help lists run usage', () async {
    final result = await Process.run(
      'dart',
      ['run', 'bin/main.dart', 'agent', '-h'],
    );

    expect(result.exitCode, 0, reason: result.stderr.toString());
    final stdoutStr = result.stdout.toString();
    expect(stdoutStr, contains('agent'));
    expect(stdoutStr, contains('run --config <path>'));
    expect(stdoutStr, contains('Manage the OTO agent'));
  });

  test('actual bin agent run help works', () async {
    final result = await Process.run(
      'dart',
      ['run', 'bin/main.dart', 'agent', 'run', '-h'],
    );

    expect(result.exitCode, 0, reason: result.stderr.toString());
    final stdoutStr = result.stdout.toString();
    expect(stdoutStr, contains('agent run --config <path>'));
    expect(stdoutStr, contains('Run the OTO agent daemon'));
  });

  test('actual bin agent with no parameters shows root help', () async {
    final result = await Process.run(
      'dart',
      ['run', 'bin/main.dart', 'agent'],
    );

    expect(result.exitCode, 0, reason: result.stderr.toString());
    final stdoutStr = result.stdout.toString();
    expect(stdoutStr, contains('Usage:'));
    expect(stdoutStr, contains('agent run --config <path>'));
  });
}
