// engine/test-contract-generator.ts — SPEC-018 contract and mock generators
// Generates API contracts (OpenAPI, GraphQL, Pact, tRPC) and API mocks from spec endpoints.
import { getStandardVersion } from '../config/version-defaults.js';
// === Internal helpers ===
function toPascal(str) {
    return str
        .replace(/[^a-zA-Z0-9\s]/g, '')
        .split(/\s+/)
        .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
        .join('');
}
function toSnake(str) {
    return str.toLowerCase().replace(/[^a-z0-9]/g, '_');
}
// === Contract generation ===
/**
 * Generate an API contract from spec endpoint information.
 */
export function generateContractFromSpec(specTitle, endpoints, format) {
    const content = buildContractContent(specTitle, endpoints, format);
    return { format, content, endpoints };
}
function buildContractContent(title, endpoints, format) {
    if (format === 'openapi') {
        return buildOpenApiContent(title, endpoints);
    }
    if (format === 'graphql-schema') {
        return buildGraphqlContent(title, endpoints);
    }
    if (format === 'pact') {
        return buildPactContent(title, endpoints);
    }
    return buildTrpcContent(title, endpoints);
}
function buildOpenApiContent(title, endpoints) {
    const paths = endpoints
        .map((ep) => {
        const method = ep.method.toLowerCase();
        const statusLines = ep.statusCodes
            .map((s) => `        '${s}':\n          description: "${s === 200 ? 'Success' : 'Error'}"`)
            .join('\n');
        return `  ${ep.path}:\n    ${method}:\n      summary: "${title} — ${ep.method} ${ep.path}"\n      responses:\n${statusLines}`;
    })
        .join('\n');
    const openapiVersion = getStandardVersion('openapi') || '3.0.3';
    return `openapi: '${openapiVersion}'\ninfo:\n  title: "${title} API Contract"\n  version: '1.0.0'\npaths:\n${paths}\n`;
}
function toGraphqlFieldName(ep) {
    const pathName = ep.path
        .split('/')
        .filter(Boolean)
        .map((segment) => segment.replace(/[{}:]/g, ''))
        .join(' ');
    const base = toSnake(pathName || 'root')
        .replace(/_+/g, '_')
        .replace(/^_|_$/g, '');
    if (ep.method === 'GET') {
        return base || 'root';
    }
    return `${ep.method.toLowerCase()}_${base || 'root'}`;
}
function buildGraphqlContent(title, endpoints) {
    const typeName = toPascal(title);
    const queryFields = endpoints
        .filter((ep) => ep.method === 'GET')
        .map((ep) => `  ${toGraphqlFieldName(ep)}: ${typeName}`)
        .join('\n');
    const mutationFields = endpoints
        .filter((ep) => ep.method !== 'GET')
        .map((ep) => `  ${toGraphqlFieldName(ep)}: ${typeName}`)
        .join('\n');
    return [
        `# GraphQL schema for: ${title}`,
        '# Generated by Planu SDD MCP Server',
        '# Only endpoint-derived fields are emitted; add domain fields from an explicit schema.',
        '',
        `type ${typeName} {`,
        '  id: ID!',
        '}',
        '',
        'type Query {',
        queryFields || '  _empty: Boolean',
        '}',
        '',
        'type Mutation {',
        mutationFields || '  _empty: Boolean',
        '}',
        '',
    ].join('\n');
}
function buildPactContent(title, endpoints) {
    const interactions = endpoints.map((ep) => ({
        description: `${ep.method} ${ep.path}`,
        request: { method: ep.method, path: ep.path },
        response: { status: ep.statusCodes[0] ?? 200, body: {} },
    }));
    return JSON.stringify({
        consumer: { name: 'consumer' },
        provider: { name: title.replace(/\s+/g, '-').toLowerCase() },
        interactions,
        metadata: { pactSpecification: { version: '2.0.0' } },
    }, null, 2);
}
function buildTrpcProcedure(ep) {
    const procedureName = toGraphqlFieldName(ep);
    const procedureKind = ep.method === 'GET' ? 'query' : 'mutation';
    return [
        `  ${procedureName}: publicProcedure.${procedureKind}(async () => {`,
        `    throw new Error('Bind ${ep.method} ${ep.path} to its implementation before running this router.');`,
        '  }),',
    ].join('\n');
}
function buildTrpcContent(title, endpoints) {
    const routerName = toSnake(title);
    const procedures = endpoints.map((ep) => buildTrpcProcedure(ep)).join('\n');
    return [
        `// tRPC router contract for: ${title}`,
        '// Generated by Planu SDD MCP Server',
        "import { router, publicProcedure } from '../trpc';",
        '',
        `export const ${routerName}Router = router({`,
        procedures,
        '});',
        '',
    ].join('\n');
}
// === Mock generation ===
/**
 * Generate API endpoint mocks for parallel frontend/backend development.
 */
export function generateMocksFromSpec(_specTitle, endpoints) {
    return endpoints.map((ep) => buildMockDefinition(ep));
}
function buildMockDefinition(ep) {
    const errorResponses = [
        { status: 400, body: { error: 'Bad Request', message: 'Invalid input provided' } },
        { status: 404, body: { error: 'Not Found', message: 'Resource not found' } },
        { status: 500, body: { error: 'Internal Server Error', message: 'Unexpected error' } },
    ];
    return {
        endpoint: ep.path,
        method: ep.method,
        sampleData: buildSampleData(ep),
        delayMs: 100,
        errorResponses,
    };
}
function buildSampleData(ep) {
    if (ep.responseSchema && Object.keys(ep.responseSchema).length > 0) {
        return ep.responseSchema;
    }
    return {};
}
/**
 * Generate an MSW (Mock Service Worker) handler file from mock definitions.
 */
export function generateMockServerTemplate(mocks) {
    const handlers = mocks
        .map((mock) => {
        const method = mock.method.toLowerCase();
        const responseBody = JSON.stringify(mock.sampleData, null, 4).replace(/^/gm, '    ');
        return `  http.${method}('${mock.endpoint}', async () => {\n    await delay(${mock.delayMs});\n    return HttpResponse.json(\n${responseBody}\n    );\n  }),`;
    })
        .join('\n\n');
    return `// MSW mock handlers — generated by Planu SDD MCP Server\nimport { http, HttpResponse, delay } from 'msw';\n\nexport const handlers = [\n${handlers}\n];\n`;
}
// === Contract validation ===
/**
 * Validate that implemented endpoints comply with the generated contract.
 */
export function validateContractCompliance(implementedEndpoints, contract) {
    return contract.endpoints.map((ep) => {
        const key = `${ep.method}:${ep.path}`;
        const found = implementedEndpoints.includes(key);
        return {
            endpoint: key,
            compliant: found,
            issues: found ? [] : [`Endpoint ${ep.method} ${ep.path} not found in implementation`],
        };
    });
}
//# sourceMappingURL=test-contract-generator.js.map