// engine/test-generators/contract-test-generator.ts
// SPEC-018: Contract test generation (OpenAPI, Pact, GraphQL, tRPC)
import { getStandardVersion } from '../../config/version-defaults.js';
// === Contract format detection ===
/**
 * Detect the contract format from project stack.
 * REST/unknown → openapi, GraphQL → graphql-schema, tRPC → trpc-router, Pact detected → pact.
 */
export function detectContractFormat(knowledge) {
    const stack = knowledge.stack.map((s) => s.toLowerCase());
    if (stack.some((s) => s.includes('pact'))) {
        return 'pact';
    }
    if (stack.some((s) => s.includes('graphql') || s.includes('@apollo') || s.includes('yoga') || s === 'gql')) {
        return 'graphql-schema';
    }
    if (stack.some((s) => s.includes('trpc'))) {
        return 'trpc-router';
    }
    return 'openapi';
}
// === Contract generation functions ===
/**
 * Legacy helper retained for API compatibility. Contract generation must not
 * infer endpoints from a title; callers must pass observed endpoints instead.
 */
export function buildSampleEndpoints(_specTitle) {
    return [];
}
// === OpenAPI contract generation ===
function buildOpenApiYaml(specTitle, endpoints) {
    const resource = specTitle
        .toLowerCase()
        .replace(/[^a-z0-9]/g, '-')
        .replace(/-+/g, '-');
    const openapiVersion = getStandardVersion('openapi') || '3.0.3';
    const lines = [
        `openapi: "${openapiVersion}"`,
        'info:',
        `  title: "${specTitle} API Contract"`,
        '  version: "1.0.0"',
        '  description: "Auto-generated contract from Planu SPEC-018"',
        'paths:',
    ];
    const grouped = new Map();
    for (const ep of endpoints) {
        const existing = grouped.get(ep.path) ?? [];
        existing.push(ep);
        grouped.set(ep.path, existing);
    }
    for (const [path, eps] of grouped) {
        lines.push(`  "${path}":`);
        for (const ep of eps) {
            const method = ep.method.toLowerCase();
            lines.push(`    ${method}:`);
            lines.push(`      operationId: "${method}${resource.replace(/-/g, '')}${ep.path.includes('{id}') ? 'ById' : ''}"`);
            lines.push('      tags:');
            lines.push(`        - "${resource}"`);
            lines.push('      responses:');
            for (const code of ep.statusCodes) {
                lines.push(`        "${code}":`);
                lines.push(`          description: "HTTP ${code}"`);
                if (code < 400) {
                    lines.push('          content:');
                    lines.push('            application/json:');
                    lines.push('              schema:');
                    lines.push('                type: object');
                }
            }
        }
    }
    return lines.join('\n');
}
// === Pact contract generation ===
function buildPactContract(specTitle, endpoints) {
    const consumer = specTitle.toLowerCase().replace(/[^a-z0-9]/g, '-');
    const provider = `${consumer}-provider`;
    const interactions = endpoints
        .map((ep, i) => {
        const description = `${ep.method} ${ep.path} — scenario ${i + 1}`;
        return `    {
      "description": "${description}",
      "providerState": "${specTitle} is initialized",
      "request": {
        "method": "${ep.method}",
        "path": "${ep.path.replace('{id}', '550e8400-e29b-41d4-a716-446655440000')}"
      },
      "response": {
        "status": ${ep.statusCodes[0] ?? 200},
        "headers": { "Content-Type": "application/json" },
        "body": {}
      }
    }`;
    })
        .join(',\n');
    return `{
  "consumer": { "name": "${consumer}-consumer" },
  "provider": { "name": "${provider}" },
  "interactions": [
${interactions}
  ],
  "metadata": {
    "pactSpecification": { "version": "2.0.0" },
    "planu": { "spec": "${specTitle}", "generated": true }
  }
}`;
}
// === GraphQL schema contract generation ===
function buildGraphQlSchema(specTitle) {
    const typeName = specTitle.replace(/[^a-zA-Z0-9]/g, '').replace(/^\d+/, '');
    return `# GraphQL schema contract — ${specTitle}
# Auto-generated by Planu SPEC-018

type ${typeName} {
  id: ID!
  name: String!
  createdAt: String!
  updatedAt: String!
}

input Create${typeName}Input {
  name: String!
}

input Update${typeName}Input {
  name: String
}

type Query {
  list${typeName}s: [${typeName}!]!
  get${typeName}(id: ID!): ${typeName}
}

type Mutation {
  create${typeName}(input: Create${typeName}Input!): ${typeName}!
  update${typeName}(id: ID!, input: Update${typeName}Input!): ${typeName}!
  delete${typeName}(id: ID!): Boolean!
}
`;
}
// === tRPC router contract generation ===
function buildTrpcRouter(specTitle) {
    const name = specTitle
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, '')
        .split(/\s+/)
        .map((w, i) => (i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)))
        .join('');
    return `// tRPC router contract — ${specTitle}
// Auto-generated by Planu SPEC-018
// Install: @trpc/server, zod

import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';

const ${name}Schema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(255),
  createdAt: z.iso.datetime(),
});

const create${name.charAt(0).toUpperCase() + name.slice(1)}Schema = z.object({
  name: z.string().min(1).max(255),
});

export const ${name}Router = router({
  list: publicProcedure
    .output(z.array(${name}Schema))
    .query(async () => {
      // TODO: Implement list
      return [];
    }),

  getById: publicProcedure
    .input(z.object({ id: z.string().uuid() }))
    .output(${name}Schema.nullable())
    .query(async ({ input }) => {
      // TODO: Implement getById
      void input;
      return null;
    }),

  create: protectedProcedure
    .input(create${name.charAt(0).toUpperCase() + name.slice(1)}Schema)
    .output(${name}Schema)
    .mutation(async ({ input }) => {
      // TODO: Implement create
      throw new Error(\`Not implemented: \${input.name}\`);
    }),

  delete: protectedProcedure
    .input(z.object({ id: z.string().uuid() }))
    .output(z.boolean())
    .mutation(async ({ input }) => {
      // TODO: Implement delete
      void input;
      return false;
    }),
});
`;
}
// === Main export ===
/**
 * Generate a contract spec for the given spec title and knowledge.
 * Format is auto-detected from project stack.
 */
export function generateContractSpec(specTitle, knowledge) {
    const format = detectContractFormat(knowledge);
    const endpoints = [];
    if (endpoints.length === 0) {
        return { format, content: '', endpoints };
    }
    let content;
    switch (format) {
        case 'pact':
            content = buildPactContract(specTitle, endpoints);
            break;
        case 'graphql-schema':
            content = buildGraphQlSchema(specTitle);
            break;
        case 'trpc-router':
            content = buildTrpcRouter(specTitle);
            break;
        case 'openapi':
            content = buildOpenApiYaml(specTitle, endpoints);
            break;
        /* v8 ignore next 3 */
        default:
            content = buildOpenApiYaml(specTitle, endpoints);
            break;
    }
    return { format, content, endpoints };
}
/**
 * Get the file extension for a given contract format.
 */
export function getContractFileExtension(format) {
    const extMap = {
        openapi: 'yaml',
        pact: 'json',
        'graphql-schema': 'graphql',
        'trpc-router': 'ts',
    };
    return extMap[format];
}
/**
 * Get the contract file path for a spec.
 */
export function getContractFilePath(specSlug, format) {
    const ext = getContractFileExtension(format);
    const dir = format === 'pact' ? 'contracts/pacts' : 'contracts';
    return `${dir}/${specSlug}.contract.${ext}`;
}
//# sourceMappingURL=contract-test-generator.js.map