import { describe, expect, it } from 'vitest';
import type { HarnessActorContext, HarnessSelection } from '@mosaicstack/types';
import { HarnessOperationError } from '../harness.registry.js';
import { FakeHarnessAdapter } from './fake-harness.adapter.js';
import { runHarnessAdapterContract } from './harness-adapter.contract.js';

const CONTEXT: HarnessActorContext = {
  actorId: 'actor-1',
  tenantId: 'tenant-1',
  seatId: 'seat-1',
  correlationId: 'correlation-1',
};

// The reusable conformance suite. Task 13 re-runs it against the native Pi adapter.
runHarnessAdapterContract('FakeHarnessAdapter', () => new FakeHarnessAdapter({ id: 'fake' }));

describe('FakeHarnessAdapter no-substitution', () => {
  it('never substitutes the first catalog row when a bogus selection is requested', async () => {
    const adapter = new FakeHarnessAdapter({ id: 'fake' });
    const catalog = await adapter.catalog(CONTEXT);
    const firstRow = catalog.models[0];
    if (!firstRow) {
      throw new Error('fixture requires a catalog model');
    }
    const available = catalog.models.find(
      (entry) => entry.availability === 'available' && entry.modelId !== firstRow.modelId,
    );
    expect(available).toBeDefined();
    const selected: HarnessSelection = {
      harnessId: available!.harnessId,
      providerId: available!.providerId,
      modelId: available!.modelId,
    };

    const handle = await adapter.create({
      context: CONTEXT,
      conversationId: 'conversation-1',
      selection: selected,
    });

    const bogus: HarnessSelection = {
      harnessId: 'fake',
      providerId: 'ghost-provider',
      modelId: 'ghost-model',
    };

    let error: unknown;
    try {
      await handle.setModel(bogus);
    } catch (caught) {
      error = caught;
    }

    expect(error).toBeInstanceOf(HarnessOperationError);
    const dto = (error as HarnessOperationError).dto;
    expect(dto.code).toBe('selection_invalid');
    // The DTO echoes the exact requested tuple, unchanged.
    expect(dto.selection).toEqual(bogus);
    // No substitution to the first catalog row.
    expect(dto.selection).not.toEqual({
      harnessId: firstRow.harnessId,
      providerId: firstRow.providerId,
      modelId: firstRow.modelId,
    });
    // The active selection is untouched by the rejected request.
    expect((await handle.snapshot()).selection).toEqual(selected);
  });

  it('reports model_unavailable with the unchanged tuple for a known but unavailable model', async () => {
    const adapter = new FakeHarnessAdapter({ id: 'fake' });
    const catalog = await adapter.catalog(CONTEXT);
    const unavailable = catalog.models.find((entry) => entry.availability === 'unavailable');
    const available = catalog.models.find((entry) => entry.availability === 'available');
    expect(unavailable).toBeDefined();
    expect(available).toBeDefined();

    const startingSelection: HarnessSelection = {
      harnessId: available!.harnessId,
      providerId: available!.providerId,
      modelId: available!.modelId,
    };
    const handle = await adapter.create({
      context: CONTEXT,
      conversationId: 'conversation-2',
      selection: startingSelection,
    });

    const requested: HarnessSelection = {
      harnessId: unavailable!.harnessId,
      providerId: unavailable!.providerId,
      modelId: unavailable!.modelId,
    };

    let error: unknown;
    try {
      await handle.setModel(requested);
    } catch (caught) {
      error = caught;
    }

    expect(error).toBeInstanceOf(HarnessOperationError);
    const dto = (error as HarnessOperationError).dto;
    expect(dto.code).toBe('model_unavailable');
    expect(dto.selection).toEqual(requested);
    expect((await handle.snapshot()).selection).toEqual(startingSelection);
  });
});
