/**
 * Event Types Generator
 * Generates TypeScript type interfaces from YAML event definitions
 */

import * as path from 'path';
import { GeneratorContext, GenerationResult, GeneratedFile } from '../types';
import { ExtendedWorkflow } from '../../parser/extended-workflow-schema';

export async function generateEventTypes(context: GeneratorContext): Promise<GenerationResult> {
  const files: GeneratedFile[] = [];
  const workflow = context.extendedWorkflow;

  // Skip if no extended workflow or no events defined
  if (!workflow?.events || Object.keys(workflow.events).length === 0) {
    return { files };
  }

  // Determine output directory based on backend technology
  const backendDir = workflow.technology?.backend?.directory || 'backend';
  const outputPath = path.join(context.outputDirectory, backendDir, 'src', 'types', 'events.ts');

  // Generate TypeScript event type interfaces
  const eventInterfaces = generateEventInterfaces(workflow);
  const eventPayloadTypes = generateEventPayloadTypes(workflow);
  const eventTypeUnion = generateEventTypeUnion(workflow);
  const eventRegistry = generateEventRegistry(workflow);

  const content = `/**
 * Event Type Definitions
 * Auto-generated from workflow events
 * DO NOT EDIT MANUALLY
 */

// ============================================================================
// Event Payload Type Mapping
// ============================================================================

${eventPayloadTypes}

// ============================================================================
// Event Interfaces
// ============================================================================

${eventInterfaces}

// ============================================================================
// Event Type Union
// ============================================================================

${eventTypeUnion}

// ============================================================================
// Event Registry (for runtime validation)
// ============================================================================

${eventRegistry}

// ============================================================================
// Type Guards
// ============================================================================

export function isValidEventType(eventType: string): eventType is EventType {
  return EVENT_REGISTRY.hasOwnProperty(eventType);
}

export function validateEventPayload<T extends EventType>(
  eventType: T,
  payload: unknown
): payload is EventPayloadMap[T] {
  // Basic runtime validation - can be extended with more sophisticated checks
  const eventDef = EVENT_REGISTRY[eventType];
  if (!eventDef) return false;

  if (typeof payload !== 'object' || payload === null) return false;

  const payloadObj = payload as Record<string, unknown>;
  const requiredFields = Object.keys(eventDef.payload);

  // Check all required fields exist
  for (const field of requiredFields) {
    if (!(field in payloadObj)) return false;
  }

  return true;
}
`;

  files.push({ path: outputPath, content });

  return { files };
}

/**
 * Generate TypeScript type mapping for event payloads
 */
function generateEventPayloadTypes(workflow: ExtendedWorkflow): string {
  const typeMapping: Record<string, string> = {
    string: 'string',
    number: 'number',
    boolean: 'boolean',
    array: 'unknown[]',
    object: 'Record<string, unknown>',
    date: 'Date | string',
  };

  const payloadTypes: string[] = [];

  Object.entries(workflow.events!).forEach(([eventName, eventDef]) => {
    const fields = Object.entries(eventDef.payload)
      .map(([fieldName, fieldType]) => {
        const tsType = typeMapping[fieldType] || 'unknown';
        return `  ${fieldName}: ${tsType};`;
      })
      .join('\n');

    payloadTypes.push(`export interface ${toPascalCase(eventName)}Payload {
${fields}
}`);
  });

  return payloadTypes.join('\n\n');
}

/**
 * Generate event interfaces with metadata
 */
function generateEventInterfaces(workflow: ExtendedWorkflow): string {
  const interfaces: string[] = [];

  Object.entries(workflow.events!).forEach(([eventName, eventDef]) => {
    const payloadType = `${toPascalCase(eventName)}Payload`;

    interfaces.push(`/**
 * ${eventDef.description}
 */
export interface ${toPascalCase(eventName)}Event {
  type: '${eventName}';
  payload: ${payloadType};
  timestamp: Date | string;
  correlationId?: string;
  userId?: string;
}`);
  });

  return interfaces.join('\n\n');
}

/**
 * Generate union type of all event types
 */
function generateEventTypeUnion(workflow: ExtendedWorkflow): string {
  const eventNames = Object.keys(workflow.events!);
  const eventTypes = eventNames.map(name => `${toPascalCase(name)}Event`).join(' | ');

  return `export type Event = ${eventTypes};

export type EventType = ${eventNames.map(name => `'${name}'`).join(' | ')};

export type EventPayloadMap = {
${eventNames.map(name => `  '${name}': ${toPascalCase(name)}Payload;`).join('\n')}
};`;
}

/**
 * Generate event registry for runtime validation
 */
function generateEventRegistry(workflow: ExtendedWorkflow): string {
  const entries = Object.entries(workflow.events!)
    .map(([eventName, eventDef]) => {
      const payloadFields = Object.entries(eventDef.payload)
        .map(([field, type]) => `      ${field}: '${type}'`)
        .join(',\n');

      return `  '${eventName}': {
    description: '${eventDef.description}',
    payload: {
${payloadFields}
    }
  }`;
    })
    .join(',\n');

  return `export const EVENT_REGISTRY = {
${entries}
} as const;`;
}

/**
 * Convert snake_case or kebab-case to PascalCase
 */
function toPascalCase(str: string): string {
  return str
    .split(/[-_]/)
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join('');
}
