import 'dart:convert';
import 'dart:io';
import 'package:oto/cli/commands/command_catalog.dart';
import 'package:test/test.dart';

void main() {
  test('CommandCatalogCli shouldPrintExecuteLog behavior', () {
    final command = CommandCatalogCli();
    expect(command.shouldPrintExecuteLog(['--json']), isFalse);
    expect(command.shouldPrintExecuteLog(['--json', '--command', 'Print']), isFalse);
    expect(command.shouldPrintExecuteLog(['--command', 'Print']), isTrue);
    expect(command.shouldPrintExecuteLog([]), isTrue);
  });

  test('CommandCatalogCli shouldExecuteWithoutParameters behavior', () {
    final command = CommandCatalogCli();
    expect(command.shouldExecuteWithoutParameters(), isTrue);
  });

  test('CommandCatalogCli json output format and filters', () async {
    final lines = <String>[];
    final command = CommandCatalogCli(printString: (value) async {
      lines.add(value);
    });

    await command.execute(['--json', '--command', 'Print']);

    final jsonStr = lines.join('\n');
    final decoded = jsonDecode(jsonStr) as Map<String, dynamic>;

    expect(decoded['schemaVersion'], 1);
    expect(decoded['type'], 'commandCatalog');
    expect(decoded['filters'], isA<Map>());
    expect(decoded['filters']['command'], 'Print');
    expect(decoded['filters']['category'], isNull);

    final commands = decoded['commands'] as List;
    expect(commands, hasLength(1));
    expect(commands[0]['command'], 'Print');
    expect(commands[0]['category'], 'util');
  });

  test('CommandCatalogCli category filter with json', () async {
    final lines = <String>[];
    final command = CommandCatalogCli(printString: (value) async {
      lines.add(value);
    });

    await command.execute(['--json', '--category', 'build']);

    final decoded = jsonDecode(lines.join('\n')) as Map<String, dynamic>;
    expect(decoded['filters']['category'], 'build');

    final commands = decoded['commands'] as List;
    expect(commands.every((c) => c['category'] == 'build'), isTrue);
  });

  test('CommandCatalogCli human table output', () async {
    final lines = <String>[];
    final command = CommandCatalogCli(printString: (value) async {
      lines.add(value);
    });

    await command.execute(['--command', 'Print']);

    expect(lines, isNotEmpty);
    expect(lines[0], contains('Command'));
    expect(lines[0], contains('Category'));
    expect(lines[0], contains('DataModel'));
    expect(lines[0], contains('SamplePath'));
    expect(lines[0], contains('SampleStatus'));

    // Divider line should be present
    expect(lines[1], contains('-+-'));

    // Data line
    expect(lines[2], contains('Print'));
    expect(lines[2], contains('util'));
  });

  test('CommandCatalogCli invalid options should throw exception', () async {
    final command = CommandCatalogCli();
    expect(
      () => command.execute(['--invalid-flag']),
      throwsException,
    );
    expect(
      () => command.execute(['--category']),
      throwsException,
    );
    expect(
      () => command.execute(['--command']),
      throwsException,
    );
  });

  test('actual bin execution with --json output has parseable json and no execute logs', () async {
    final result = await Process.run('dart', ['run', 'bin/main.dart', 'catalog', '--json']);
    expect(result.exitCode, 0, reason: result.stderr.toString());

    final stdoutStr = result.stdout.toString().trim();
    final decoded = jsonDecode(stdoutStr) as Map<String, dynamic>;
    expect(decoded['schemaVersion'], 1);
    expect(decoded['type'], 'commandCatalog');
    expect(decoded['commands'], isNotEmpty);

    expect(stdoutStr, isNot(contains('Execute command:')));
    expect(stdoutStr, isNot(contains('Usage:')));
  });

  test('actual bin execution without args outputs a table, not help', () async {
    final result = await Process.run('dart', ['run', 'bin/main.dart', 'catalog']);
    expect(result.exitCode, 0, reason: result.stderr.toString());

    final stdoutStr = result.stdout.toString().trim();
    
    expect(stdoutStr, contains('Command'));
    expect(stdoutStr, contains('Category'));
    expect(stdoutStr, contains('DataModel'));
    expect(stdoutStr, contains('SamplePath'));
    expect(stdoutStr, contains('SampleStatus'));

    expect(stdoutStr, isNot(contains('Usage:')));
  });
}
