/**
 * Generated RPC Client with Environment Detection
 *
 * Auto-generated WebSocket RPC client - DO NOT EDIT
 * Generated by: django_cfg_rpc codegen
 */

import type * as Types from './types';

/**
 * Environment types
 */
type Environment = "development" | "production" | "staging" | "testing";

/**
 * Environment-aware RPC client configuration
 */
class RPCClientConfig {
{% if config %}
  // 🔧 GENERATED CONFIGURATION
  private static readonly ENDPOINTS: Record<Environment, string> = {
    development: "{{ config.development.websocket_url }}",
    production: "{{ config.production.websocket_url }}",
{% if config.staging %}    staging: "{{ config.staging.websocket_url }}",
{% else %}    staging: "ws://localhost:8001/ws",
{% endif %}{% if config.testing %}    testing: "{{ config.testing.websocket_url }}",
{% else %}    testing: "ws://localhost:8001/ws",
{% endif %}  };
{% else %}
  // No configuration provided - using default localhost
  private static readonly ENDPOINTS: Record<Environment, string> = {
    development: "ws://localhost:8001/ws",
    production: "ws://localhost:8001/ws",
    staging: "ws://localhost:8001/ws",
    testing: "ws://localhost:8001/ws",
  };
{% endif %}

  /**
   * Detect current environment from environment variables.
   *
   * Priority:
   * 1. DJANGO_ENV
   * 2. NODE_ENV
   * 3. ENV
   * 4. Default: development
   */
  static detectEnvironment(): Environment {
    // Check NODE_ENV first (standard for Node.js/frontend)
    const nodeEnv = process.env.NODE_ENV?.toLowerCase();
    if (nodeEnv === "production") return "production";
    if (nodeEnv === "development") return "development";
    if (nodeEnv === "test") return "testing";

    // Check DJANGO_ENV (for Django compatibility)
    const djangoEnv = process.env.DJANGO_ENV?.toLowerCase();
    if (djangoEnv) {
      if (["dev", "devel", "develop", "local"].includes(djangoEnv)) return "development";
      if (djangoEnv === "prod") return "production";
      if (djangoEnv === "stage") return "staging";
      if (["development", "production", "staging", "testing"].includes(djangoEnv)) {
        return djangoEnv as Environment;
      }
    }

    // Check ENV fallback
    const env = process.env.ENV?.toLowerCase();
    if (env && ["development", "production", "staging", "testing"].includes(env)) {
      return env as Environment;
    }

    // Default
    return "development";
  }

  /**
   * Get WebSocket URL for environment.
   *
   * @param environment - Environment name (auto-detect if undefined)
   * @returns WebSocket URL
   * @throws Error if environment not configured
   */
  static getUrl(environment?: Environment): string {
    const env = environment || this.detectEnvironment();
    const url = this.ENDPOINTS[env];

    if (!url) {
      const available = Object.keys(this.ENDPOINTS).join(", ");
      throw new Error(
        `No URL configured for environment: ${env}. ` +
        `Available: ${available}`
      );
    }

    return url;
  }
}

/**
 * WebSocket RPC Client with Environment Detection
 *
 * Provides type-safe RPC methods over WebSocket connection.
 * Includes automatic environment detection and URL selection.
 */
export class RPCClient {
  private ws: WebSocket | null = null;
  private url: string;
  private correlationId = 0;
  private authReceived = false;
  private pendingRequests = new Map<string, {
    resolve: (value: any) => void;
    reject: (error: Error) => void;
  }>();

  /**
   * Create RPC client.
   *
   * @param url - WebSocket URL (auto-detect from ENV if undefined)
   *
   * @example
   * // Auto-detect from NODE_ENV or DJANGO_ENV
   * const client = new RPCClient();
   *
   * @example
   * // Explicit URL
   * const client = new RPCClient("ws://localhost:8001/ws");
   */
  constructor(url?: string) {
    if (!url) {
      url = RPCClientConfig.getUrl();
      console.log(`[RPCClient] Auto-detected URL: ${url}`);
    }

    this.url = url;
  }

  /**
   * Create client from environment.
   *
   * @param environment - Environment name (auto-detect if undefined)
   * @returns Configured RPCClient instance
   *
   * @example
   * // Auto-detect from NODE_ENV or DJANGO_ENV
   * const client = RPCClient.fromEnv();
   *
   * @example
   * // Explicit environment
   * const client = RPCClient.fromEnv("production");
   */
  static fromEnv(environment?: Environment): RPCClient {
    const url = RPCClientConfig.getUrl(environment);
    return new RPCClient(url);
  }

  /**
   * Connect to WebSocket server.
   */
  async connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.authReceived = false;
      this.ws = new WebSocket(this.url);

      this.ws.onopen = () => {
        console.log('[RPCClient] Connected');
        resolve();
      };

      this.ws.onerror = (error) => {
        console.error('[RPCClient] Connection error:', error);
        reject(new Error('WebSocket connection failed'));
      };

      this.ws.onmessage = (event) => {
        this.handleMessage(event.data);
      };

      this.ws.onclose = () => {
        console.log('[RPCClient] Disconnected');
        this.ws = null;
      };
    });
  }

  /**
   * Disconnect from WebSocket server.
   */
  disconnect(): void {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  /**
   * Handle incoming WebSocket message.
   */
  private handleMessage(data: string): void {
    try {
      const message = JSON.parse(data);

      // Handle auth message with server version
      if (!this.authReceived && message.type === 'auth' && message.status === 'ok') {
        this.authReceived = true;
        const serverVersion = message.server_version || 'unknown';
        console.log(`[RPCClient] Server version: ${serverVersion}`);
        return;
      }

      if (message.type === 'response' && message.correlation_id) {
        const pending = this.pendingRequests.get(message.correlation_id);
        if (pending) {
          this.pendingRequests.delete(message.correlation_id);
          pending.resolve(message.result);
        }
      } else if (message.type === 'error' && message.correlation_id) {
        const pending = this.pendingRequests.get(message.correlation_id);
        if (pending) {
          this.pendingRequests.delete(message.correlation_id);
          pending.reject(new Error(message.error?.message || 'RPC error'));
        }
      }
    } catch (error) {
      console.error('[RPCClient] Failed to parse message:', error);
    }
  }

  /**
   * Call RPC method.
   *
   * @param method - Method name
   * @param params - Method parameters
   * @returns Promise resolving to method result
   */
  private async call<T>(method: string, params: any): Promise<T> {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      throw new Error('WebSocket not connected');
    }

    const correlationId = `${++this.correlationId}`;

    const message = {
      type: 'rpc',
      method,
      params,
      correlation_id: correlationId,
    };

    return new Promise((resolve, reject) => {
      this.pendingRequests.set(correlationId, { resolve, reject });
      this.ws!.send(JSON.stringify(message));

      // Timeout after 30 seconds
      setTimeout(() => {
        if (this.pendingRequests.has(correlationId)) {
          this.pendingRequests.delete(correlationId);
          reject(new Error('RPC timeout'));
        }
      }, 30000);
    });
  }

  // ========== Generated RPC Methods ==========

{% for method in methods %}
  /**
   * {{ method.docstring | replace('\n', '\n   * ') }}
   *
   * @param params - {{ method.param_type }} parameters
   * @returns Promise<{{ method.return_type }}>
   */
  async {{ method.name_camel }}(params: {% if method.param_type == 'any' %}any{% else %}Types.{{ method.param_type }}{% endif %}): Promise<{% if method.return_type == 'any' %}any{% else %}Types.{{ method.return_type }}{% endif %}> {
    return this.call<{% if method.return_type == 'any' %}any{% else %}Types.{{ method.return_type }}{% endif %}>('{{ method.name }}', params);
  }

{% endfor %}
}
