import 'package:test/test.dart';

class SmokeFixtureBoundary {
  final String name;
  final String target;
  final bool startsExternalRepo;
  final List<String> requiredInputs;
  final List<String> expectedEvidence;
  final List<String> forbiddenDependencies;

  const SmokeFixtureBoundary({
    required this.name,
    required this.target,
    required this.startsExternalRepo,
    required this.requiredInputs,
    required this.expectedEvidence,
    required this.forbiddenDependencies,
  });
}

class SmokeMigrationStep {
  final String id;
  final String description;

  const SmokeMigrationStep(this.id, this.description);
}

class OtoServerSmokeMigrationPlan {
  final SmokeFixtureBoundary legacySource;
  final SmokeFixtureBoundary otoServerTarget;
  final List<SmokeMigrationStep> steps;

  const OtoServerSmokeMigrationPlan({
    required this.legacySource,
    required this.otoServerTarget,
    required this.steps,
  });
}

const migrationPlan = OtoServerSmokeMigrationPlan(
  legacySource: SmokeFixtureBoundary(
    name: 'legacy-iop-edge-online-smoke',
    target: 'apps/runner/test/oto_iop_connection_smoke_test.dart',
    startsExternalRepo: true,
    requiredInputs: [
      'IOP_REPO_ROOT or workspace sibling iop checkout',
      'iop Edge config fixture',
      'iop runtime RegisterRequest/RegisterResponse protobuf',
    ],
    expectedEvidence: [
      'registration accepted by iop Edge',
      'first heartbeat transitions the iop Edge node record to online',
    ],
    forbiddenDependencies: [],
  ),
  otoServerTarget: SmokeFixtureBoundary(
    name: 'oto-server-runner-online-smoke',
    target: 'apps/runner/test/oto_server_connection_smoke_test.dart',
    startsExternalRepo: false,
    requiredInputs: [
      'loopback OTO Core listener with dynamic port',
      'temporary runner install/workspace/log directories',
      'runner enrollment token fixture owned by services/core',
      'OTO Server registration and heartbeat contract fixture',
    ],
    expectedEvidence: [
      'runner registration accepted by OTO Server',
      'runner id and alias recorded in OTO Server registry',
      'first heartbeat or equivalent online signal marks runner online',
    ],
    forbiddenDependencies: [
      'IOP_REPO_ROOT',
      '../iop checkout',
      'iop Edge process',
      'iop runtime protobuf as the acceptance contract',
    ],
  ),
  steps: [
    SmokeMigrationStep(
      'freeze-legacy',
      'Keep the iop smoke as migration source evidence until the OTO Server '
          'target fixture reaches parity.',
    ),
    SmokeMigrationStep(
      'define-core-fixture',
      'Add a services/core test fixture that can issue enrollment tokens, '
          'accept runner registration, and expose runner online state.',
    ),
    SmokeMigrationStep(
      'define-runner-fixture',
      'Start OTO Core on loopback from the runner test, create temporary '
          'runtime directories, and point AgentConfig at the OTO Server target.',
    ),
    SmokeMigrationStep(
      'replace-contract',
      'Replace iop RegisterRequest/RegisterResponse acceptance with an OTO '
          'Server owned registration and heartbeat contract.',
    ),
    SmokeMigrationStep(
      'retire-iop-smoke',
      'Remove the iop smoke from required acceptance once the OTO Server smoke '
          'proves registration accepted and online evidence.',
    ),
  ],
);

void main() {
  group('OTO Server migration smoke plan', () {
    test('keeps the existing iop smoke as legacy source evidence only', () {
      expect(migrationPlan.legacySource.name, 'legacy-iop-edge-online-smoke');
      expect(migrationPlan.legacySource.startsExternalRepo, isTrue);
      expect(
        migrationPlan.legacySource.requiredInputs,
        contains('IOP_REPO_ROOT or workspace sibling iop checkout'),
      );
      expect(
        migrationPlan.legacySource.expectedEvidence,
        contains('registration accepted by iop Edge'),
      );
    });

    test('defines an OTO Server target fixture without iop dependencies', () {
      final target = migrationPlan.otoServerTarget;

      expect(target.name, 'oto-server-runner-online-smoke');
      expect(target.startsExternalRepo, isFalse);
      expect(
        target.requiredInputs,
        contains('loopback OTO Core listener with dynamic port'),
      );
      expect(
        target.requiredInputs,
        contains('runner enrollment token fixture owned by services/core'),
      );
      expect(
        target.expectedEvidence,
        contains('runner registration accepted by OTO Server'),
      );
      expect(target.forbiddenDependencies, contains('IOP_REPO_ROOT'));
      expect(target.forbiddenDependencies, contains('../iop checkout'));
      expect(target.forbiddenDependencies, contains('iop Edge process'));
      expect(
        target.forbiddenDependencies,
        contains('iop runtime protobuf as the acceptance contract'),
      );
    });

    test('orders the handoff before retiring the iop smoke', () {
      final stepIds = migrationPlan.steps.map((step) => step.id).toList();

      expect(stepIds, [
        'freeze-legacy',
        'define-core-fixture',
        'define-runner-fixture',
        'replace-contract',
        'retire-iop-smoke',
      ]);
      expect(
        migrationPlan.steps.last.description,
        contains('Remove the iop smoke from required acceptance'),
      );
    });
  });
}
