import { generateBehaviorWithHelpers } from "./behavior-generator.js";
function generatePrismaService(context) {
  const { service, spec } = context;
  if (!service) {
    throw new Error("Service is required in template context");
  }
  const serviceName = service.name;
  const hasEvents = service.publishes && service.publishes.length > 0 || service.subscribes && service.subscribes.length > 0;
  const operationsCode = generateOperationsWithHelpers(service);
  const hasAiBehaviors = /\baiBehaviors\./.test(operationsCode);
  const usesPrisma = /\bprisma\b/.test(operationsCode);
  return `/**
 * ${serviceName}
 * Abstract business logic service
 * ${service.description || ""}
 */
${usesPrisma ? `
import { PrismaClient } from '@prisma/client';` : ""}
${hasEvents ? `import { eventBus } from '../events/eventBus.js';` : ""}
${hasAiBehaviors ? `import * as aiBehaviors from '../behaviors/${serviceName}.ai.js';` : ""}
${usesPrisma ? `
const prisma = new PrismaClient();` : ""}

/**
 * ${serviceName} class
 */
export class ${serviceName} {
  ${generateConstructor(service)}
  ${operationsCode}
  ${generateEventSubscriptions(service)}
}

// Export singleton instance
export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = new ${serviceName}();
export default ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)};
`;
}
function generateConstructor(service) {
  const hasSubscriptions = service.subscribes && service.subscribes.length > 0;
  if (!hasSubscriptions) {
    return "";
  }
  return `
  private unsubscribers: Array<() => void> = [];

  constructor() {
    // Subscribe to events
    this.setupEventSubscriptions();
  }

  /**
   * Setup event subscriptions
   */
  private setupEventSubscriptions(): void {
    ${service.subscribes.map((eventName) => `
    // Subscribe to ${eventName}
    const unsubscribe${eventName} = eventBus.subscribe(
      EventName.${eventName},
      this.on${eventName}.bind(this)
    );
    this.unsubscribers.push(unsubscribe${eventName});
    `).join("\n")}
  }

  /**
   * Cleanup subscriptions
   */
  public destroy(): void {
    this.unsubscribers.forEach(unsubscribe => unsubscribe());
    this.unsubscribers = [];
  }
`;
}
function generateOperationsWithHelpers(service) {
  const ops = generateOperations(service);
  if (_collectedHelpers.length > 0) {
    const helpers = [...new Set(_collectedHelpers)].join("\n");
    _collectedHelpers.length = 0;
    return ops + "\n" + helpers;
  }
  return ops;
}
const _collectedHelpers = [];
function generateOperations(service) {
  if (!service.operations || Array.isArray(service.operations) && service.operations.length === 0 || !Array.isArray(service.operations) && Object.keys(service.operations).length === 0) {
    return `
  /**
   * Execute service logic
   */
  public async execute(params: any): Promise<any> {
    // TODO: Implement service logic
    throw new Error('Service logic not implemented');
  }
`;
  }
  const entries = Array.isArray(service.operations) ? service.operations.map((op) => [op.name, op]) : Object.entries(service.operations);
  const operations = [];
  entries.forEach(([operationName, operation]) => {
    operations.push(generateOperation(operationName, operation, service));
  });
  return operations.join("\n");
}
function generateOperation(operationName, operation, service) {
  const rawParams = generateOperationParams(operation);
  const hasPublish = service.publishes && service.publishes.length > 0;
  const body = generateOperationLogic(operation, service);
  const params = renameUnusedParams(rawParams, body);
  return `
  /**
   * ${operationName}
   * ${operation.description || ""}
   */
  public async ${operationName}(${params}): Promise<any> {
    try {
      ${body}

      ${hasPublish ? `
      // Publish event (example)
      // eventBus.publish(EventName.SomeEvent, {
      //   operation: '${operationName}',
      //   timestamp: new Date().toISOString()
      // });
      ` : ""}

      // Return result
      return { success: true };
    } catch (error) {
      console.error(\`[${service.name}] ${operationName} failed:\`, error);
      throw error;
    }
  }
`;
}
function renameUnusedParams(paramsString, body) {
  if (!paramsString.trim()) return paramsString;
  return paramsString.split(",").map((segment) => {
    const trimmed = segment.trim();
    const nameMatch = trimmed.match(/^(\w+)/);
    if (!nameMatch) return segment;
    const name = nameMatch[1];
    if (name.startsWith("_")) return segment;
    const re = new RegExp(`\\b${name}\\b`);
    if (re.test(body)) return segment;
    return segment.replace(new RegExp(`\\b${name}\\b`), `_${name}`);
  }).join(", ");
}
function generateOperationParams(operation) {
  if (!operation.parameters || Object.keys(operation.parameters).length === 0) {
    return "params: any = {}";
  }
  const params = Object.entries(operation.parameters).map(([name, param]) => {
    const optional = !param.required;
    return `${name}${optional ? "?" : ""}: ${mapTypeToTypeScript(param.type)}`;
  });
  return params.join(", ");
}
function generateOperationLogic(operation, service) {
  const impl = operation.implementation || {};
  const meta = operation.metadata;
  if (!impl.preconditions && operation.requires) {
    impl.preconditions = Array.isArray(operation.requires) ? operation.requires : [operation.requires];
  }
  if (!impl.postconditions && operation.ensures) {
    impl.postconditions = Array.isArray(operation.ensures) ? operation.ensures : [operation.ensures];
  }
  if (!impl.sideEffects && operation.publishes) {
    impl.sideEffects = (Array.isArray(operation.publishes) ? operation.publishes : [operation.publishes]).map((e) => `Publish event: ${e}`);
  }
  if (impl.preconditions?.length || impl.postconditions?.length || impl.steps?.length || impl.transactional) {
    const modelName = inferModelFromServiceName(service.name);
    const parameterNames = Object.keys(operation.parameters || {});
    const context = {
      modelName,
      serviceName: service.name,
      operationName: operation.name || "execute",
      prismaModel: modelName,
      parameterNames
    };
    const behavior = {
      preconditions: impl.preconditions || [],
      postconditions: impl.postconditions || [],
      sideEffects: impl.sideEffects || [],
      steps: impl.steps || operation.steps || [],
      transactional: impl.transactional || false
    };
    const opMeta = {
      async: meta?.async ?? true,
      cacheable: meta?.cacheable ?? false,
      idempotent: meta?.idempotent ?? false
    };
    const result = generateBehaviorWithHelpers(behavior, opMeta, context);
    _collectedHelpers.push(...result.helperMethods);
    return result.body;
  }
  if (operation.description) {
    return `// ${operation.description}`;
  }
  return `// TODO: Implement operation logic`;
}
function inferModelFromServiceName(serviceName) {
  return serviceName.replace(/RelationshipService$/, "").replace(/Service$/, "") || "Entity";
}
function generateEventSubscriptions(service) {
  if (!service.subscribes || service.subscribes.length === 0) {
    return "";
  }
  const handlers = [];
  service.subscribes.forEach((eventName) => {
    handlers.push(`
  /**
   * Handle ${eventName} event
   */
  private async on${eventName}(payload: any): Promise<void> {
    try {
      console.log(\`[${service.name}] Received ${eventName} event\`, payload);

      // TODO: Implement event handling logic
      ${generateEventHandlerLogic(eventName, service)}

      console.log(\`[${service.name}] ${eventName} event processed successfully\`);
    } catch (error) {
      console.error(\`[${service.name}] Failed to process ${eventName} event\`, error);
      // TODO: Add error handling (retry, dead letter queue, etc.)
    }
  }`);
  });
  return handlers.join("\n");
}
function generateEventHandlerLogic(eventName, service) {
  return `
      // Example: Update related records
      // await prisma.someModel.update({
      //   where: { id: payload.id },
      //   data: { ... }
      // });

      // Example: Trigger workflows
      // await this.triggerWorkflow(payload);

      // Example: Publish follow-up events
      // eventBus.publish(EventName.AnotherEvent, {
      //   original: payload,
      //   processed: true
      // });
  `;
}
function mapTypeToTypeScript(type) {
  const typeMap = {
    String: "string",
    Integer: "number",
    Float: "number",
    Boolean: "boolean",
    Date: "Date",
    DateTime: "Date",
    UUID: "string",
    JSON: "any",
    Array: "any[]",
    Object: "Record<string, any>"
  };
  return typeMap[type] || "any";
}
export {
  generatePrismaService as default
};
