/**
 * command-generator (commander) — TODO #15 subtype dispatch test.
 *
 * The realize CLI command was previously taking a `<type>` positional
 * arg and ignoring it: every invocation called realizeAll with the
 * full spec. #15 wires `type` into a per-component section filter so
 * `spv realize controllers <spec>` only regenerates controller outputs.
 *
 * This test exercises the generator with a realize-shaped command spec
 * and asserts the emitted file contains the dispatch logic. End-to-end
 * verification (does spv realize controllers actually skip non-
 * controller sections?) requires a publish; the unit test pins the
 * shape of the emitted handler.
 */

import { describe, it, expect } from 'vitest';
import generateCommand from '../templates/commander/command-generator.js';

function makeContext() {
  return {
    command: {
      name: 'realize',
      description: 'Generate production code from specification',
      arguments: {
        type: { positional: true, position: 1, required: true, type: 'string' },
        file: { positional: true, position: 2, required: true, type: 'string' },
      },
      flags: {
        output: { type: 'string', shortName: 'o', description: 'Output directory' },
        manifest: { type: 'string', shortName: 'm', description: 'Implementation manifest file' },
        static: { type: 'boolean', description: 'Generate full static frontend (no @specverse/runtime dependency)' },
        estimate: { type: 'boolean', description: 'Print expected output breakdown by realize layer' },
      },
      serviceRef: 'realize',
    },
  } as any;
}

describe('cli/commander/command-generator — #15 realize subtype dispatch', () => {
  it('emits ENTITY_SECTIONS guard listing the canonical entity types', () => {
    const out = generateCommand(makeContext());
    expect(out).toContain("'models'");
    expect(out).toContain("'controllers'");
    expect(out).toContain("'services'");
    expect(out).toContain("'views'");
    expect(out).toContain("'events'");
    expect(out).toContain("'deployments'");
    expect(out).toContain("'commands'");
    expect(out).toContain("'measures'");
    expect(out).toContain("'promotions'");
    expect(out).toContain("'conventions'");
    expect(out).toContain("'distributions'");
  });

  it('rejects unknown subtypes with a clear error and process.exit', () => {
    const out = generateCommand(makeContext());
    expect(out).toMatch(/Unknown realize subtype/);
    expect(out).toContain("process.exit(1)");
  });

  it('preserves the all-default behaviour by passing inferredSpec through unchanged', () => {
    const out = generateCommand(makeContext());
    // The "all" branch must NOT JSON-clone — it should pass inferredSpec
    // straight to realizeAll. The clone only happens in the filter branch.
    expect(out).toContain("realizeType !== 'all'");
    expect(out).toContain("realizeAll(realizeSpec, outputDir)");
  });

  it('clones + filters the spec for non-all subtypes', () => {
    const out = generateCommand(makeContext());
    // Deep-clone marker.
    expect(out).toContain('JSON.parse(JSON.stringify(inferredSpec))');
    // Per-component section reset (sections other than the requested
    // type are emptied to their array/object identity element).
    expect(out).toMatch(/section !== realizeType/);
    expect(out).toMatch(/Array\.isArray\(comp\[section\]\)\s*\?\s*\[\]\s*:\s*\{\}/);
  });

  it('logs which subtype is being realized (not just the file path)', () => {
    const out = generateCommand(makeContext());
    expect(out).toMatch(/Realizing.*\+\s*type\s*\+/);
  });
});
