"use strict";
/**
 * WebSocket Client Generator
 * Generates React hooks and utilities for WebSocket connections
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateWebSocketClient = generateWebSocketClient;
const path = __importStar(require("path"));
async function generateWebSocketClient(context) {
    const files = [];
    const workflow = context.extendedWorkflow;
    // Skip if no extended workflow or no events defined
    if (!workflow?.events || Object.keys(workflow.events).length === 0) {
        return { files };
    }
    // Skip if no Redis cache configured
    if (!workflow.technology?.cache || !workflow.technology.cache.provider.includes('redis')) {
        return { files };
    }
    const frontendDir = 'frontend'; // Frontend directory is always 'frontend' in extended workflow
    // Generate WebSocket client core
    files.push(generateWebSocketClientCore(frontendDir, context.outputDirectory));
    // Generate WebSocket React hook
    files.push(generateWebSocketReactHook(frontendDir, context.outputDirectory, workflow));
    // Generate WebSocket context provider
    files.push(generateWebSocketContext(frontendDir, context.outputDirectory));
    return { files };
}
/**
 * Generate core WebSocket client implementation
 */
function generateWebSocketClientCore(frontendDir, outputDirectory) {
    const content = `/**
 * WebSocket Client Core
 * Manages WebSocket connection and subscriptions
 * DO NOT EDIT MANUALLY
 */

export type EventCallback = (payload: any) => void;

export interface WebSocketMessage {
  type: string;
  [key: string]: any;
}

export class WebSocketClient {
  private ws: WebSocket | null = null;
  private url: string;
  private token: string | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;
  private reconnectDelay = 1000;
  private subscriptions: Map<string, Set<EventCallback>> = new Map();
  private messageQueue: any[] = [];
  private isConnecting = false;
  private heartbeatInterval: number | null = null;

  constructor(url: string) {
    this.url = url;
  }

  /**
   * Connect to WebSocket server
   */
  connect(token?: string): Promise<void> {
    return new Promise((resolve, reject) => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        resolve();
        return;
      }

      if (this.isConnecting) {
        return;
      }

      this.isConnecting = true;
      this.token = token || null;

      try {
        const wsUrl = token ? \`\${this.url}?token=\${token}\` : this.url;
        this.ws = new WebSocket(wsUrl);

        this.ws.onopen = () => {
          console.log('[WebSocket] Connected');
          this.isConnecting = false;
          this.reconnectAttempts = 0;

          // Send queued messages
          this.flushMessageQueue();

          // Start heartbeat
          this.startHeartbeat();

          resolve();
        };

        this.ws.onmessage = (event) => {
          try {
            const message = JSON.parse(event.data) as WebSocketMessage;
            this.handleMessage(message);
          } catch (error) {
            console.error('[WebSocket] Error parsing message:', error);
          }
        };

        this.ws.onerror = (error) => {
          console.error('[WebSocket] Error:', error);
          this.isConnecting = false;
          reject(error);
        };

        this.ws.onclose = () => {
          console.log('[WebSocket] Disconnected');
          this.isConnecting = false;
          this.stopHeartbeat();
          this.attemptReconnect();
        };
      } catch (error) {
        this.isConnecting = false;
        reject(error);
      }
    });
  }

  /**
   * Handle incoming message
   */
  private handleMessage(message: WebSocketMessage): void {
    switch (message.type) {
      case 'connected':
        console.log('[WebSocket] Connection confirmed:', message.clientId);
        break;

      case 'event':
        this.handleEvent(message.eventType, message.payload);
        break;

      case 'error':
        console.error('[WebSocket] Server error:', message.error);
        break;

      case 'pong':
        // Heartbeat response
        break;

      default:
        console.warn('[WebSocket] Unknown message type:', message.type);
    }
  }

  /**
   * Handle incoming event
   */
  private handleEvent(eventType: string, payload: any): void {
    const callbacks = this.subscriptions.get(eventType);
    if (callbacks) {
      callbacks.forEach(callback => {
        try {
          callback(payload);
        } catch (error) {
          console.error(\`[WebSocket] Error in event callback for \${eventType}:\`, error);
        }
      });
    }

    // Also trigger wildcard subscriptions
    const wildcardCallbacks = this.subscriptions.get('*');
    if (wildcardCallbacks) {
      wildcardCallbacks.forEach(callback => {
        try {
          callback({ eventType, payload });
        } catch (error) {
          console.error('[WebSocket] Error in wildcard callback:', error);
        }
      });
    }
  }

  /**
   * Subscribe to event type
   */
  subscribe(eventType: string, callback: EventCallback): () => void {
    if (!this.subscriptions.has(eventType)) {
      this.subscriptions.set(eventType, new Set());

      // Send subscribe message to server
      this.send({
        type: 'subscribe',
        eventTypes: [eventType],
      });
    }

    this.subscriptions.get(eventType)!.add(callback);

    // Return unsubscribe function
    return () => {
      const callbacks = this.subscriptions.get(eventType);
      if (callbacks) {
        callbacks.delete(callback);

        if (callbacks.size === 0) {
          this.subscriptions.delete(eventType);

          // Send unsubscribe message to server
          this.send({
            type: 'unsubscribe',
            eventTypes: [eventType],
          });
        }
      }
    };
  }

  /**
   * Send message to server
   */
  private send(message: any): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    } else {
      // Queue message for when connection is ready
      this.messageQueue.push(message);
    }
  }

  /**
   * Flush queued messages
   */
  private flushMessageQueue(): void {
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      this.send(message);
    }
  }

  /**
   * Attempt to reconnect
   */
  private attemptReconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('[WebSocket] Max reconnect attempts reached');
      return;
    }

    this.reconnectAttempts++;
    const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);

    console.log(\`[WebSocket] Reconnecting in \${delay}ms (attempt \${this.reconnectAttempts})\`);

    setTimeout(() => {
      this.connect(this.token || undefined);
    }, delay);
  }

  /**
   * Start heartbeat ping
   */
  private startHeartbeat(): void {
    this.heartbeatInterval = window.setInterval(() => {
      this.send({ type: 'ping' });
    }, 30000);
  }

  /**
   * Stop heartbeat ping
   */
  private stopHeartbeat(): void {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      this.heartbeatInterval = null;
    }
  }

  /**
   * Disconnect from server
   */
  disconnect(): void {
    this.stopHeartbeat();

    if (this.ws) {
      this.ws.close(1000, 'Client disconnect');
      this.ws = null;
    }

    this.subscriptions.clear();
    this.messageQueue = [];
  }

  /**
   * Check if connected
   */
  isConnected(): boolean {
    return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
  }
}
`;
    return {
        path: path.join(outputDirectory, frontendDir, 'src', 'lib', 'websocket-client.ts'),
        content,
    };
}
/**
 * Generate WebSocket React hook
 */
function generateWebSocketReactHook(frontendDir, outputDirectory, workflow) {
    const eventNames = Object.keys(workflow.events);
    const content = `/**
 * WebSocket React Hook
 * Provides easy-to-use hooks for subscribing to real-time events
 * DO NOT EDIT MANUALLY
 */

import { useEffect, useCallback } from 'react';
import { useWebSocket } from './websocket-context';
import { EventPayloadMap } from '../types/events';

${eventNames.map(eventName => `import type { ${toPascalCase(eventName)}Payload } from '../types/events';`).join('\n')}

/**
 * Subscribe to a specific event type
 */
export function useEvent<T extends keyof EventPayloadMap>(
  eventType: T,
  callback: (payload: EventPayloadMap[T]) => void,
  enabled = true
): void {
  const { client, isConnected } = useWebSocket();

  const memoizedCallback = useCallback(callback, [callback]);

  useEffect(() => {
    if (!client || !isConnected || !enabled) {
      return;
    }

    const unsubscribe = client.subscribe(eventType as string, memoizedCallback);

    return () => {
      unsubscribe();
    };
  }, [client, isConnected, eventType, memoizedCallback, enabled]);
}

/**
 * Hook for subscribing to multiple events
 */
export function useEvents() {
  return {
${eventNames.map(eventName => `    ${eventName}: (callback: (payload: ${toPascalCase(eventName)}Payload) => void) => {
      useEvent('${eventName}', callback);
    }`).join(',\n')}
  };
}

/**
 * Subscribe to all events
 */
export function useAllEvents(callback: (event: { eventType: string; payload: any }) => void): void {
  const { client, isConnected } = useWebSocket();

  const memoizedCallback = useCallback(callback, [callback]);

  useEffect(() => {
    if (!client || !isConnected) {
      return;
    }

    const unsubscribe = client.subscribe('*', memoizedCallback);

    return () => {
      unsubscribe();
    };
  }, [client, isConnected, memoizedCallback]);
}
`;
    return {
        path: path.join(outputDirectory, frontendDir, 'src', 'hooks', 'useEvents.ts'),
        content,
    };
}
/**
 * Generate WebSocket context provider
 */
function generateWebSocketContext(frontendDir, outputDirectory) {
    const content = `/**
 * WebSocket Context Provider
 * Manages WebSocket connection state for the application
 * DO NOT EDIT MANUALLY
 */

import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { WebSocketClient } from '../lib/websocket-client';

interface WebSocketContextValue {
  client: WebSocketClient | null;
  isConnected: boolean;
  connect: (token?: string) => Promise<void>;
  disconnect: () => void;
}

const WebSocketContext = createContext<WebSocketContextValue>({
  client: null,
  isConnected: false,
  connect: async () => {},
  disconnect: () => {},
});

export function useWebSocket(): WebSocketContextValue {
  return useContext(WebSocketContext);
}

interface WebSocketProviderProps {
  children: ReactNode;
  url?: string;
  autoConnect?: boolean;
  token?: string;
}

export function WebSocketProvider({
  children,
  url = \`ws://\${window.location.host}/ws\`,
  autoConnect = false,
  token,
}: WebSocketProviderProps) {
  const [client] = useState(() => new WebSocketClient(url));
  const [isConnected, setIsConnected] = useState(false);

  const connect = async (authToken?: string) => {
    try {
      await client.connect(authToken || token);
      setIsConnected(true);
    } catch (error) {
      console.error('[WebSocketProvider] Connection failed:', error);
      setIsConnected(false);
    }
  };

  const disconnect = () => {
    client.disconnect();
    setIsConnected(false);
  };

  useEffect(() => {
    if (autoConnect) {
      connect(token);
    }

    // Monitor connection status
    const checkInterval = setInterval(() => {
      const connected = client.isConnected();
      if (connected !== isConnected) {
        setIsConnected(connected);
      }
    }, 1000);

    return () => {
      clearInterval(checkInterval);
      disconnect();
    };
  }, []);

  return (
    <WebSocketContext.Provider value={{ client, isConnected, connect, disconnect }}>
      {children}
    </WebSocketContext.Provider>
  );
}
`;
    return {
        path: path.join(outputDirectory, frontendDir, 'src', 'lib', 'websocket-context.tsx'),
        content,
    };
}
/**
 * Convert snake_case or kebab-case to PascalCase
 */
function toPascalCase(str) {
    return str
        .split(/[-_]/)
        .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
        .join('');
}
//# sourceMappingURL=websocket-client-generator.js.map