import { describe, expect, it } from 'vitest';
import { OptionsBuilder } from '../core/piece/engine/OptionsBuilder.js';
import type { PieceMovement } from '../core/models/types.js';
import type { PieceEngineOptions } from '../core/piece/types.js';

function createMovement(overrides: Partial<PieceMovement> = {}): PieceMovement {
  return {
    name: 'reviewers',
    personaDisplayName: 'Reviewers',
    instructionTemplate: 'review',
    passPreviousResponse: false,
    ...overrides,
  };
}

function createBuilder(step: PieceMovement, engineOverrides: Partial<PieceEngineOptions> = {}): OptionsBuilder {
  const engineOptions: PieceEngineOptions = {
    projectCwd: '/project',
    provider: 'codex',
    providerProfiles: {
      codex: {
        defaultPermissionMode: 'full',
      },
    },
    ...engineOverrides,
  };

  return new OptionsBuilder(
    engineOptions,
    () => '/project',
    () => '/project',
    () => undefined,
    () => '.takt/runs/sample/reports',
    () => 'ja',
    () => [{ name: step.name }],
    () => 'default',
    () => 'test piece',
  );
}

describe('OptionsBuilder.buildBaseOptions', () => {
  it('passes permission resolution context for provider profile resolution', () => {
    const step = createMovement();
    const builder = createBuilder(step);

    const options = builder.buildBaseOptions(step);

    expect(options.permissionMode).toBeUndefined();
    expect(options.permissionResolution).toEqual({
      movementName: 'reviewers',
      requiredPermissionMode: undefined,
      providerProfiles: {
        codex: { defaultPermissionMode: 'full' },
      },
    });
  });

  it('includes requiredPermissionMode in permission resolution context', () => {
    const step = createMovement({ requiredPermissionMode: 'full' });
    const builder = createBuilder(step);

    const options = builder.buildBaseOptions(step);

    expect(options.permissionResolution).toEqual({
      movementName: 'reviewers',
      requiredPermissionMode: 'full',
      providerProfiles: {
        codex: { defaultPermissionMode: 'full' },
      },
    });
  });

  it('still passes permission resolution context when provider is not configured', () => {
    const step = createMovement();
    const builder = createBuilder(step, {
      provider: undefined,
      providerProfiles: undefined,
    });

    const options = builder.buildBaseOptions(step);
    expect(options.permissionResolution).toEqual({
      movementName: 'reviewers',
      requiredPermissionMode: undefined,
      providerProfiles: undefined,
    });
  });

  it('merges provider options with precedence: global/project < movement', () => {
    const step = createMovement({
      providerOptions: {
        codex: { networkAccess: false },
        claude: { sandbox: { excludedCommands: ['./gradlew'] } },
      },
    });
    const builder = createBuilder(step, {
      providerOptionsSource: 'project',
      providerOptions: {
        codex: { networkAccess: true },
        claude: { sandbox: { allowUnsandboxedCommands: true } },
        opencode: { networkAccess: true },
      },
    });

    const options = builder.buildBaseOptions(step);

    expect(options.providerOptions).toEqual({
      codex: { networkAccess: false },
      opencode: { networkAccess: true },
      claude: {
        sandbox: {
          excludedCommands: ['./gradlew'],
          allowUnsandboxedCommands: true,
        },
      },
    });
  });

  it('falls back to global/project provider options when movement has none', () => {
    const step = createMovement();
    const builder = createBuilder(step, {
      providerOptions: {
        codex: { networkAccess: false },
      },
    });

    const options = builder.buildBaseOptions(step);

    expect(options.providerOptions).toEqual({
      codex: { networkAccess: false },
    });
  });
});

describe('OptionsBuilder.resolveStepProviderModel', () => {
  it('should return engine-level provider and model when step has no overrides', () => {
    const step = createMovement();
    const builder = createBuilder(step, { provider: 'claude', model: 'sonnet' });

    const result = builder.resolveStepProviderModel(step);

    expect(result.provider).toBe('claude');
    expect(result.model).toBe('sonnet');
  });

  it('should prioritize persona providers over engine-level provider', () => {
    const step = createMovement({ personaDisplayName: 'coder' });
    const builder = createBuilder(step, {
      provider: 'claude',
      model: 'sonnet',
      personaProviders: { coder: { provider: 'codex', model: 'o3-mini' } },
    });

    const result = builder.resolveStepProviderModel(step);

    expect(result.provider).toBe('codex');
    expect(result.model).toBe('o3-mini');
  });

  it('should prioritize step-level provider over engine-level provider', () => {
    const step = createMovement({ provider: 'opencode' as 'opencode' });
    const builder = createBuilder(step, { provider: 'claude' });

    const result = builder.resolveStepProviderModel(step);

    expect(result.provider).toBe('opencode');
  });

  it('should prioritize persona providers over step-level provider', () => {
    const step = createMovement({ personaDisplayName: 'coder', provider: 'claude' as 'claude' });
    const builder = createBuilder(step, {
      provider: 'mock',
      personaProviders: { coder: { provider: 'codex' } },
    });

    const result = builder.resolveStepProviderModel(step);

    expect(result.provider).toBe('codex');
  });

  it('should return undefined model when no model is configured', () => {
    const step = createMovement();
    const builder = createBuilder(step, { provider: 'claude', model: undefined });

    const result = builder.resolveStepProviderModel(step);

    expect(result.model).toBeUndefined();
  });

  it('should return undefined provider when no provider is configured', () => {
    const step = createMovement();
    const builder = createBuilder(step, { provider: undefined });

    const result = builder.resolveStepProviderModel(step);

    expect(result.provider).toBeUndefined();
  });

  it('should match buildBaseOptions stepProvider and stepModel', () => {
    const step = createMovement({ personaDisplayName: 'coder' });
    const builder = createBuilder(step, {
      provider: 'claude',
      model: 'sonnet',
      personaProviders: { coder: { provider: 'codex', model: 'o3-mini' } },
    });

    const providerInfo = builder.resolveStepProviderModel(step);
    const baseOptions = builder.buildBaseOptions(step);

    expect(providerInfo.provider).toBe(baseOptions.stepProvider);
    expect(providerInfo.model).toBe(baseOptions.stepModel);
  });
});

describe('OptionsBuilder.buildResumeOptions', () => {
  it('should enforce readonly permission and empty allowedTools for report/status phases', () => {
    // Given
    const step = createMovement({ requiredPermissionMode: 'full' });
    const builder = createBuilder(step);

    // When
    const options = builder.buildResumeOptions(step, 'session-123', { maxTurns: 3 });

    // Then
    expect(options.permissionMode).toBe('readonly');
    expect(options.allowedTools).toEqual([]);
    expect(options.maxTurns).toBe(3);
    expect(options.sessionId).toBe('session-123');
  });
});
