/**
 * Error Replay System
 * Allows developers to replay errors with the exact same context
 */

import { getGlobalErrorHandler } from './GlobalErrorHandler';
import { getGlobalApiLogger } from './ApiRequestLogger';
import { ApiError } from '../api/base/ApiError';

export interface ReplayContext {
  timestamp: string;
  error: {
    message: string;
    type: string;
    stack?: string;
    context?: unknown;
  };
  environment: {
    userAgent: string;
    url: string;
    viewport: { width: number; height: number };
    language: string;
    platform: string;
  };
  apiRequests: Array<{
    url: string;
    method: string;
    duration?: number;
    status?: number;
    error?: string;
  }>;
  reactErrors: Array<{
    message: string;
    componentStack?: string;
  }>;
  metadata: {
    mode: string;
    timestamp: string;
    replayId: string;
  };
}

export class ErrorReplaySystem {
  private replayHistory: ReplayContext[] = [];
  private readonly MAX_REPLAYS = 20;

  /**
   * Capture current state for replay
   */
  captureReplay(error: Error | ApiError, additionalContext: Record<string, unknown> = {}): ReplayContext {
    const apiLogger = getGlobalApiLogger();

    const replayId = `replay_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    const replay: ReplayContext = {
      timestamp: new Date().toISOString(),
      error: {
        message: error.message,
        type: error instanceof ApiError ? error.type : error.name || 'Error',
        stack: error.stack,
        context: error instanceof ApiError ? error.context : additionalContext,
      },
      environment: {
        userAgent: navigator.userAgent,
        url: window.location.href,
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight,
        },
        language: navigator.language,
        platform: navigator.platform,
      },
      apiRequests: apiLogger.getHistory().slice(-10).map(log => ({
        url: log.url,
        method: log.method,
        duration: log.response?.duration,
        status: log.response?.status,
        error: log.error?.message,
      })),
      reactErrors: (window as { __GRIDPILOT_REACT_ERRORS__?: Array<{ error?: { message?: string }; componentStack?: string }> }).__GRIDPILOT_REACT_ERRORS__?.slice(-5).map((e) => ({
        message: e.error?.message || 'Unknown React error',
        componentStack: e.componentStack,
      })) || [],
      metadata: {
        mode: process.env.NODE_ENV || 'unknown',
        timestamp: new Date().toISOString(),
        replayId,
      },
    };

    this.replayHistory.push(replay);

    // Keep only last N replays
    if (this.replayHistory.length > this.MAX_REPLAYS) {
      this.replayHistory = this.replayHistory.slice(-this.MAX_REPLAYS);
    }

    // Store in localStorage for persistence across sessions
    this.persistReplay(replay);

    return replay;
  }

  /**
   * Persist replay to localStorage
   */
  private persistReplay(replay: ReplayContext): void {
    try {
      const key = `gridpilot_replay_${replay.metadata.replayId}`;
      localStorage.setItem(key, JSON.stringify(replay));
      
      // Also add to index
      const indexKey = 'gridpilot_replay_index';
      const existing = JSON.parse(localStorage.getItem(indexKey) || '[]');
      existing.push({
        id: replay.metadata.replayId,
        timestamp: replay.timestamp,
        error: replay.error.message,
        type: replay.error.type,
      });
      localStorage.setItem(indexKey, JSON.stringify(existing.slice(-this.MAX_REPLAYS)));
    } catch (e) {
      // Storage might be full or disabled
      console.warn('Failed to persist replay:', e);
    }
  }

  /**
   * Get all replays
   */
  getReplays(): ReplayContext[] {
    return [...this.replayHistory];
  }

  /**
   * Get replay by ID
   */
  getReplay(replayId: string): ReplayContext | null {
    // Check memory first
    const inMemory = this.replayHistory.find(r => r.metadata.replayId === replayId);
    if (inMemory) return inMemory;

    // Check localStorage
    try {
      const key = `gridpilot_replay_${replayId}`;
      const stored = localStorage.getItem(key);
      if (stored) {
        return JSON.parse(stored);
      }
    } catch (e) {
      console.warn('Failed to load replay from storage:', e);
    }

    return null;
  }

  /**
   * Replay an error by re-executing it with the same context
   */
  async replay(replayId: string): Promise<void> {
    const replay = this.getReplay(replayId);
    if (!replay) {
      console.error(`Replay ${replayId} not found`);
      return;
    }

    console.groupCollapsed(`%c[REPLAY] Replaying error: ${replay.error.message}`, 
      'color: #00ff88; font-weight: bold; font-size: 14px;'
    );

    console.log('Original Error:', replay.error);
    console.log('Environment:', replay.environment);
    console.log('API Requests:', replay.apiRequests);
    console.log('React Errors:', replay.reactErrors);
    console.log('Metadata:', replay.metadata);

    // Recreate the error
    const error = replay.error.type === 'ApiError'
      ? new ApiError(
          replay.error.message,
          ((replay.error.context as { type?: string } | undefined)?.type as import('../api/base/ApiError').ApiErrorType) || 'UNKNOWN_ERROR',
          {
            timestamp: replay.timestamp,
            ...(replay.error.context as Record<string, unknown> | undefined),
          }
        )
      : new Error(replay.error.message);

    if (replay.error.stack) {
      error.stack = replay.error.stack;
    }

    // Report through global handler
    const globalHandler = getGlobalErrorHandler();
    globalHandler.report(error, {
      source: 'replay',
      replayId: replay.metadata.replayId,
      originalTimestamp: replay.timestamp,
      environment: replay.environment,
      apiRequests: replay.apiRequests,
      reactErrors: replay.reactErrors,
    });

    console.groupEnd();

    // Show a notification
    if (typeof window !== 'undefined') {
      const notificationEvent = new CustomEvent('gridpilot-notification', {
        detail: {
          type: 'replay_success',
          title: 'Error Replay Complete',
          message: `Replayed error from ${new Date(replay.timestamp).toLocaleString()}`,
          variant: 'toast',
        }
      });
      window.dispatchEvent(notificationEvent);
    }
  }

  /**
   * Export replay data
   */
  exportReplay(replayId: string, format: 'json' | 'text' = 'json'): string {
    const replay = this.getReplay(replayId);
    if (!replay) {
      return 'Replay not found';
    }

    if (format === 'json') {
      return JSON.stringify(replay, null, 2);
    }

    // Text format
    return `
Error Replay Report
===================

Replay ID: ${replay.metadata.replayId}
Timestamp: ${replay.timestamp}

ERROR
-----
Type: ${replay.error.type}
Message: ${replay.error.message}
Stack: ${replay.error.stack || 'N/A'}

ENVIRONMENT
-----------
User Agent: ${replay.environment.userAgent}
URL: ${replay.environment.url}
Viewport: ${replay.environment.viewport.width}x${replay.environment.viewport.height}
Language: ${replay.environment.language}
Platform: ${replay.environment.platform}

API REQUESTS (${replay.apiRequests.length})
----------------${replay.apiRequests.map(req => `
  ${req.method} ${req.url}
  ${req.status ? `Status: ${req.status}` : ''} ${req.duration ? `Duration: ${req.duration}ms` : ''}
  ${req.error ? `Error: ${req.error}` : ''}
`).join('')}

REACT ERRORS (${replay.reactErrors.length})
----------------${replay.reactErrors.map(react => `
  ${react.message}
  ${react.componentStack ? `Component Stack: ${react.componentStack}` : ''}
`).join('')}

METADATA
--------
Mode: ${replay.metadata.mode}
Original Timestamp: ${replay.metadata.timestamp}
`;
  }

  /**
   * Delete replay
   */
  deleteReplay(replayId: string): void {
    // Remove from memory
    this.replayHistory = this.replayHistory.filter(r => r.metadata.replayId !== replayId);

    // Remove from localStorage
    try {
      localStorage.removeItem(`gridpilot_replay_${replayId}`);
      
      // Update index
      const indexKey = 'gridpilot_replay_index';
      const existing = JSON.parse(localStorage.getItem(indexKey) || '[]');
      const updated = existing.filter((r: any) => r.id !== replayId);
      localStorage.setItem(indexKey, JSON.stringify(updated));
    } catch (e) {
      console.warn('Failed to delete replay:', e);
    }
  }

  /**
   * Clear all replays
   */
  clearAll(): void {
    this.replayHistory = [];

    // Clear from localStorage
    try {
      const indexKey = 'gridpilot_replay_index';
      const existing = JSON.parse(localStorage.getItem(indexKey) || '[]');
      
      existing.forEach((r: any) => {
        localStorage.removeItem(`gridpilot_replay_${r.id}`);
      });
      
      localStorage.removeItem(indexKey);
    } catch (e) {
      console.warn('Failed to clear replays:', e);
    }
  }

  /**
   * Get replay index (summary of all available replays)
   */
  getReplayIndex(): Array<{ id: string; timestamp: string; error: string; type: string }> {
    try {
      const indexKey = 'gridpilot_replay_index';
      const stored = localStorage.getItem(indexKey);
      if (stored) {
        return JSON.parse(stored);
      }
    } catch (e) {
      console.warn('Failed to load replay index:', e);
    }

    // Return in-memory index
    return this.replayHistory.map(r => ({
      id: r.metadata.replayId,
      timestamp: r.timestamp,
      error: r.error.message,
      type: r.error.type,
    }));
  }

  /**
   * Auto-capture errors for replay
   */
  autoCapture(error: Error | ApiError, context: Record<string, unknown> = {}): void {
    // Only capture in development or if explicitly enabled
    if (process.env.NODE_ENV !== 'development') {
      return;
    }

    const replay = this.captureReplay(error, context);
    
    if (console) {
      console.log('%c[REPLAY] Captured error for replay', 'color: #00ff88; font-weight: bold;', {
        replayId: replay.metadata.replayId,
        message: replay.error.message,
      });
    }
  }
}

/**
 * Global instance accessor
 */
let globalReplayInstance: ErrorReplaySystem | null = null;

export function getGlobalReplaySystem(): ErrorReplaySystem {
  if (!globalReplayInstance) {
    globalReplayInstance = new ErrorReplaySystem();
  }
  return globalReplayInstance;
}

/**
 * Initialize replay system
 */
export function initializeReplaySystem(): ErrorReplaySystem {
  const system = new ErrorReplaySystem();
  globalReplayInstance = system;
  return system;
}

/**
 * React hook for error replay
 */
export function useErrorReplay() {
  const system = getGlobalReplaySystem();

  return {
    capture: (error: Error | ApiError, context?: Record<string, unknown>) => 
      system.captureReplay(error, context),
    replay: (replayId: string) => system.replay(replayId),
    getReplays: () => system.getReplays(),
    getReplay: (replayId: string) => system.getReplay(replayId),
    exportReplay: (replayId: string, format?: 'json' | 'text') => 
      system.exportReplay(replayId, format),
    deleteReplay: (replayId: string) => system.deleteReplay(replayId),
    clearAll: () => system.clearAll(),
    getReplayIndex: () => system.getReplayIndex(),
  };
}