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

void main() {
  const validYaml = '''
commands:
  - command: Print
    id: hello
    param:
      message: hi
pipeline:
  id: main
  workflow:
    - exe: hello
''';

  const invalidYaml = '''
commands:
  - command: Print
    id: hello
    param:
      message: hi
pipeline:
  id: main
  workflow:
    - exe: non_existent_id
''';

  test('CommandValidateCli shouldPrintExecuteLog behavior', () {
    final command = CommandValidateCli();
    expect(command.shouldPrintExecuteLog(['--json']), isFalse);
    expect(
        command.shouldPrintExecuteLog(['--json', '-f', 'file.yaml']), isFalse);
    expect(command.shouldPrintExecuteLog(['-f', 'file.yaml']), isTrue);
  });

  test('CommandValidateCli json output for valid yaml', () async {
    final file =
        File('${Directory.systemTemp.path}/oto_validate_valid_test.yaml');
    file.writeAsStringSync(validYaml);

    final lines = <String>[];
    final command = CommandValidateCli(printString: (value) async {
      lines.add(value);
    });

    try {
      final oldExitCode = exitCode;
      await command.execute(['-f', file.path, '--json']);

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

      expect(decoded['schemaVersion'], 1);
      expect(decoded['type'], 'yamlValidation');
      expect(decoded['valid'], isTrue);
      expect(decoded['exitCode'], 0);
      expect(exitCode, 0);

      // restore exitCode
      exitCode = oldExitCode;
    } finally {
      if (file.existsSync()) {
        file.deleteSync();
      }
    }
  });

  test('CommandValidateCli json output for invalid yaml', () async {
    final file =
        File('${Directory.systemTemp.path}/oto_validate_invalid_test.yaml');
    file.writeAsStringSync(invalidYaml);

    final lines = <String>[];
    final command = CommandValidateCli(printString: (value) async {
      lines.add(value);
    });

    try {
      final oldExitCode = exitCode;
      await command.execute(['-f', file.path, '--json']);

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

      expect(decoded['schemaVersion'], 1);
      expect(decoded['type'], 'yamlValidation');
      expect(decoded['valid'], isFalse);
      expect(decoded['phase'], 'Validate Pipeline');
      expect(decoded['exitCode'], 10);
      expect(exitCode, 10);

      // restore exitCode
      exitCode = oldExitCode;
    } finally {
      if (file.existsSync()) {
        file.deleteSync();
      }
    }
  });

  test('CommandValidateCli human format output for valid/invalid yaml',
      () async {
    final validFile =
        File('${Directory.systemTemp.path}/oto_validate_human_valid.yaml');
    validFile.writeAsStringSync(validYaml);

    final invalidFile =
        File('${Directory.systemTemp.path}/oto_validate_human_invalid.yaml');
    invalidFile.writeAsStringSync(invalidYaml);

    try {
      final linesValid = <String>[];
      final commandValid = CommandValidateCli(printString: (value) async {
        linesValid.add(value);
      });

      final oldExitCode1 = exitCode;
      await commandValid.execute(['-f', validFile.path]);
      expect(linesValid.join('\n'), contains('Valid YAML.'));
      expect(exitCode, 0);
      exitCode = oldExitCode1;

      final linesInvalid = <String>[];
      final commandInvalid = CommandValidateCli(printString: (value) async {
        linesInvalid.add(value);
      });

      final oldExitCode2 = exitCode;
      await commandInvalid.execute(['-f', invalidFile.path]);
      expect(linesInvalid.join('\n'),
          contains('Invalid YAML: [Validate Pipeline]'));
      expect(exitCode, 10);
      exitCode = oldExitCode2;
    } finally {
      if (validFile.existsSync()) validFile.deleteSync();
      if (invalidFile.existsSync()) invalidFile.deleteSync();
    }
  });

  test('CommandValidateCli missing file returns validation json and exit code',
      () async {
    final lines = <String>[];
    final command = CommandValidateCli(printString: (value) async {
      lines.add(value);
    });

    final oldExitCode = exitCode;
    await command.execute(['-f', 'non_existent_file_path_12345.yaml', '--json']);

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

    expect(decoded['schemaVersion'], 1);
    expect(decoded['type'], 'yamlValidation');
    expect(decoded['valid'], isFalse);
    expect(decoded['phase'], 'Read YAML file');
    expect(decoded['message'], contains('There are no files in path'));
    expect(decoded['exitCode'], 10);
    expect(exitCode, 10);

    // restore exitCode
    exitCode = oldExitCode;
  });

  test(
      'actual bin validate --json output has parseable json and no execute logs',
      () async {
    final file =
        File('${Directory.systemTemp.path}/oto_validate_bin_test.yaml');
    file.writeAsStringSync(validYaml);

    try {
      final result = await Process.run(
        'dart',
        ['run', 'bin/main.dart', 'validate', '-f', file.path, '--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'], 'yamlValidation');
      expect(decoded['valid'], isTrue);
      expect(decoded['exitCode'], 0);

      expect(stdoutStr, isNot(contains('Execute command:')));
      expect(stdoutStr, isNot(contains('Usage:')));
    } finally {
      if (file.existsSync()) {
        file.deleteSync();
      }
    }
  });
}
