/**
 * Regression tests for the MongoDB native driver controller generator (TODO #43).
 *
 * Pins the generator's output for the headline shapes:
 *   - CURVED ops emit native-driver collection access (insertOne, findOne,
 *     updateOne, deleteOne, find().toArray())
 *   - Lifecycle transitions decode flow shorthand and emit a transition map
 *     (the buildTransitionMap path was the bug that hid `pending -> done`
 *     from evolve before #43)
 *   - Controller surfaces compile cleanly under strict-ish tsc (smoke is
 *     the realize harness; this file pins shape, not full compilation)
 */
import { describe, it, expect } from 'vitest';
import generateMongoNativeController from '../controller-generator.js';

const baseModel = {
  name: 'Todo',
  attributes: {
    id: { name: 'id', type: 'UUID', required: true, auto: 'uuid4' },
    title: { name: 'title', type: 'String', required: true },
    completed: { name: 'completed', type: 'Boolean', default: false },
  },
};

const baseController = {
  name: 'TodoController',
  description: 'Controller for Todo',
  cured: { create: true, retrieve: true, update: true, evolve: true, delete: true },
};

describe('MongoDB native — controller-generator', () => {
  it('emits all CURVED ops via collection access', () => {
    const out = generateMongoNativeController({
      controller: baseController,
      model: baseModel,
    } as any);
    expect(out).toContain(`COLLECTION_NAME = 'todos'`);
    expect(out).toContain('insertOne');
    expect(out).toContain('findOne');
    expect(out).toContain('updateOne');
    expect(out).toContain('deleteOne');
    expect(out).toContain('cursor.toArray');
    expect(out).not.toContain('PrismaClient');
    expect(out).not.toContain('prisma.');
  });

  it('parses ObjectId-shaped ids and falls through for UUID/string ids', () => {
    const out = generateMongoNativeController({
      controller: baseController,
      model: baseModel,
    } as any);
    expect(out).toContain(`/^[a-fA-F0-9]{24}$/.test(id)`);
    expect(out).toContain('new ObjectId(id)');
  });

  it('emits lifecycle transition map even when spec used `flow:` shorthand only', () => {
    // Regression: pre-fix, lifecycle.states was undefined when the spec
    // declared only `flow: "pending -> done"` (states are derived at parse-
    // time, but the inferred YAML keeps the flow string). The evolve gate
    // skipped the transition check because `states.length > 0` was false.
    const out = generateMongoNativeController({
      controller: baseController,
      model: {
        ...baseModel,
        lifecycles: { status: { name: 'status', flow: 'pending -> done' } },
      },
    } as any);
    expect(out).toContain('public async evolve(');
    expect(out).toContain('"pending":["done"]');
    expect(out).toContain('Invalid transition');
  });

  it('emits a working multi-state transition map for explicit states', () => {
    const out = generateMongoNativeController({
      controller: baseController,
      model: {
        ...baseModel,
        lifecycles: {
          status: {
            name: 'status',
            states: ['draft', 'open', 'closed'],
            transitions: { open: 'draft -> open', close: 'open -> closed' },
          },
        },
      },
    } as any);
    expect(out).toContain('"draft":["open"]');
    expect(out).toContain('"open":["closed"]');
  });

  it('uses model.storage.collection override when present', () => {
    const out = generateMongoNativeController({
      controller: baseController,
      model: { ...baseModel, storage: { collection: 'custom_todos' } },
    } as any);
    expect(out).toContain(`COLLECTION_NAME = 'custom_todos'`);
    // CURVED ops reference the constant, not the literal — keeps the
    // generator output noUnusedLocals-clean under strict tsc.
    expect(out).toContain(`getCollection(COLLECTION_NAME)`);
  });

  it('omits CURVED ops not declared on the controller', () => {
    const out = generateMongoNativeController({
      controller: { ...baseController, cured: { retrieve: true } },
      model: baseModel,
    } as any);
    expect(out).toContain('public async retrieve(');
    expect(out).not.toContain('public async create(');
    expect(out).not.toContain('public async update(');
    expect(out).not.toContain('public async delete(');
    expect(out).not.toContain('public async evolve(');
  });
});
