/**
 * PostgreSQL Native (pg) — Service Generator
 *
 * Generates abstract business-logic services that use the pgClient
 * helpers directly. The service body is intentionally skeletal: each
 * declared operation gets a typed method, but the body is a TODO stub
 * containing the spec steps as comments. The behavior-step → SQL
 * conventions for service bodies are a follow-up; controllers already
 * inline conventions today.
 */
import type { TemplateContext } from '@specverse/types';

export default function generatePgNativeService(context: TemplateContext): string {
  const { service } = context;
  if (!service) throw new Error('Service is required in template context');

  const serviceName = service.name;
  const operationsCode = generateOperations(service);
  const usesDb = /\bquery\b|\bgetPool\b/.test(operationsCode);
  const hasEvents = (service.publishes && service.publishes.length > 0) ||
                    (service.subscribes && service.subscribes.length > 0);

  return `/**
 * ${serviceName}
 * Abstract business logic service (PostgreSQL native driver)
 * ${service.description || ''}
 */
${usesDb ? `import { query, getPool } from '../db/pgClient.js';` : ''}
${hasEvents ? `import { eventBus } from '../events/eventBus.js';` : ''}

export class ${serviceName} {
${operationsCode}
}

const ${lowerFirst(serviceName)} = new ${serviceName}();
export const ${lowerFirst(serviceName)}Instance = ${lowerFirst(serviceName)};
export default ${lowerFirst(serviceName)};
`;
}

function lowerFirst(s: string): string {
  return s.charAt(0).toLowerCase() + s.slice(1);
}

function generateOperations(service: any): string {
  const ops = service.operations;
  if (!ops || (Array.isArray(ops) && ops.length === 0) || (!Array.isArray(ops) && Object.keys(ops).length === 0)) {
    return `
  /**
   * Default service entrypoint — replace with real operations once declared
   * on the service's spec.
   */
  public async execute(_params: any = {}): Promise<any> {
    throw new Error('${service.name}.execute is not implemented');
  }
`;
  }
  const entries: [string, any][] = Array.isArray(ops)
    ? ops.map((op: any) => [op.name, op])
    : Object.entries(ops);
  return entries.map(([name, op]) => generateOperation(name, op, service.name)).join('\n');
}

function generateOperation(operationName: string, operation: any, serviceName: string): string {
  const stepsHeader = (operation.steps && operation.steps.length > 0)
    ? operation.steps.map((s: any) => `   *   - ${typeof s === 'string' ? s : (s.action || JSON.stringify(s))}`).join('\n')
    : '   *   (no spec steps declared)';
  return `
  /**
   * ${operationName}
   * ${operation.description || ''}
   *
   * Spec steps:
${stepsHeader}
   */
  public async ${operationName}(_args: any = {}): Promise<any> {
    // TODO: translate spec steps into pg-native SQL calls. Controllers
    // already inline conventions today; service-side parity is a
    // follow-up. For now this stub keeps realize completing and the
    // service surface callable.
    throw new Error('${serviceName}.${operationName} is not implemented');
  }
`;
}
