/**
 * SET 43b(ii) — constants superbuffer page: spec literals as intent oracle.
 */
import { describe, expect, it } from 'vitest';
import type { FeatureNode, FeatureSpec } from '../src/core/types';
import {
  assembleSuperbuffer,
  encodeFeatureSpecText,
  extractSpecConstants,
  STAGE_PAGE_POLICY,
} from '../src/tier1/superbuffer';

function feature(partial: Partial<FeatureNode> & { id: string }): FeatureNode {
  return {
    name: partial.name ?? partial.id,
    description: partial.description ?? 'plain prose without literals',
    parentId: null,
    deps: partial.deps ?? [],
    files: partial.files ?? [`src/${partial.id.toLowerCase()}.ts`],
    testFile: partial.testFile ?? `tests/${partial.id.toLowerCase()}.test.ts`,
    edgeCases: partial.edgeCases ?? [],
    envVars: [],
    exports: partial.exports ?? [{ file: `src/${partial.id.toLowerCase()}.ts`, symbol: 'fn' }],
    stubs: partial.stubs ?? [{
      path: `src/${partial.id.toLowerCase()}.ts`,
      content: 'export function fn(x: string): string { throw new Error("stub: not implemented"); }\n',
    }],
    ...partial,
  };
}

describe('SET 43b(ii) extractSpecConstants', () => {
  it('finds statuses, limits, dimensions, and format strings from feature text', () => {
    const text = [
      'Feature Pay: Wallet',
      'Description: return status "ok" or "error"; reject when over limit 100;',
      '  canvas is 640x480; log with format "user=%s code=%d"; enum PENDING / MAX_RETRIES',
      'Edge cases: status code 404 | empty',
      'Prompt span: timeout 30 and height 720',
    ].join('\n');

    const items = extractSpecConstants(text);
    expect(items).toEqual(expect.arrayContaining([
      'ok',
      'error',
      '100',
      '640x480',
      'user=%s code=%d',
      'PENDING',
      'MAX_RETRIES',
      '404',
      '30',
      '720',
    ]));
    // Deterministic sort + soft cap.
    expect(items).toEqual([...items].sort((a, b) => a.localeCompare(b)));
    expect(items.length).toBeLessThanOrEqual(24);
  });

  it('returns empty when no literals are present', () => {
    expect(extractSpecConstants('Feature F1: plain\nDescription: no quoted values here')).toEqual([]);
  });
});

describe('SET 43b(ii) constants page packing', () => {
  it('stage=build includes pages.constants when literals present', () => {
    const f = feature({
      id: 'Pay',
      name: 'Wallet',
      description: 'statuses "ok"/"error"; limit 50; format "amt=%d"',
      edgeCases: ['status PENDING', 'MAX_ITEMS'],
    });
    const spec: FeatureSpec = { title: 'Pay', prompt: 'wallet statuses', features: [f] };
    const encoded = encodeFeatureSpecText(f, spec);
    expect(extractSpecConstants(encoded).length).toBeGreaterThan(0);

    const build = assembleSuperbuffer({
      spec, feature: f, parts: null, stubs: f.stubs, lessons: [],
      siblingParts: new Map(), target: 'fn', stage: 'build',
      signature: { name: 'fn', params: ['string'], returnType: 'string' },
    });
    expect(STAGE_PAGE_POLICY.build).toContain('constants');
    expect(build.pages.constants).toBeDefined();
    expect(build.pages.constants!.text).toContain('Page constants — spec literals (intent oracle):');
    expect(build.pages.constants!.text).toMatch(/^- /m);
    expect(build.promptBlock).toContain('spec literals (intent oracle)');
    expect(build.logLine).toMatch(/pages \[.*constants:\d+/);
  });

  it('stage=repair includes constants', () => {
    const f = feature({
      id: 'Pay',
      description: 'use status "ok" and code 200',
    });
    const spec: FeatureSpec = { title: 'Pay', prompt: 'p', features: [f] };
    const repair = assembleSuperbuffer({
      spec, feature: f, parts: null, stubs: f.stubs, lessons: [],
      siblingParts: new Map(), target: 'assertion|eq', stage: 'repair',
      findingEvidence: 'Expected "ok" but got "error"',
    });
    expect(STAGE_PAGE_POLICY.repair).toContain('constants');
    expect(STAGE_PAGE_POLICY.fill).not.toContain('constants');
    expect(repair.pages.constants).toBeDefined();
    expect(repair.promptBlock).toContain('spec literals (intent oracle)');
    expect(repair.logLine).toContain('stage=repair');
  });

  it('omits constants page when no literals (empty skipped)', () => {
    const f = feature({
      id: 'Plain',
      description: 'a quiet feature with nothing to extract',
      edgeCases: ['roundtrip'],
    });
    const spec: FeatureSpec = { title: 'T', prompt: 'plain prose only', features: [f] };
    const build = assembleSuperbuffer({
      spec, feature: f, parts: null, stubs: f.stubs, lessons: [],
      siblingParts: new Map(), target: 'fn', stage: 'build',
    });
    expect(extractSpecConstants(build.specText)).toEqual([]);
    expect(build.pages.constants).toBeUndefined();
    expect(build.promptBlock).not.toContain('spec literals (intent oracle)');
  });
});
