// Weft Sandbox - Main Runtime

import { WeftConfig, SandboxState, SandboxError, ActionLog } from './types';
import { createMockProvider, MockDataProvider } from './mock-provider';
import { StubHooksLayer } from './stub-hooks';
import { ActionLogger } from './action-logger';

export class WeftSandbox {
  private config: WeftConfig;
  private mockProvider: MockDataProvider;
  private stubHooks: StubHooksLayer;
  private actionLogger: ActionLogger;
  private errors: SandboxError[] = [];
  private isRunning: boolean = false;
  private errorIdCounter: number = 0;

  constructor(config: WeftConfig) {
    this.config = config;
    this.mockProvider = createMockProvider(config.mockData);
    this.actionLogger = new ActionLogger();
    this.stubHooks = new StubHooksLayer({
      stubs: config.apiStubs,
      onAction: (action) => this.actionLogger.log(action)
    });
  }

  // Start the sandbox
  start(): void {
    this.isRunning = true;
    this.logInfo('Sandbox started', { artifactId: this.config.artifact.id });
  }

  // Stop the sandbox
  stop(): void {
    this.isRunning = false;
    this.logInfo('Sandbox stopped');
  }

  // Get current state
  getState(): SandboxState {
    return {
      isRunning: this.isRunning,
      actionLogs: this.actionLogger.getLogs(),
      currentMockData: this.mockProvider.export(),
      errors: [...this.errors]
    };
  }

  // Handle API calls (intercepts fetch)
  async handleFetch(method: string, url: string, payload?: any): Promise<Response> {
    if (!this.isRunning) {
      throw new Error('Sandbox is not running');
    }

    try {
      const result = await this.stubHooks.interceptFetch(method, url, payload);
      
      return new Response(JSON.stringify(result.response), {
        status: result.status,
        headers: { 'Content-Type': 'application/json' }
      });
    } catch (error) {
      this.recordError('network', `Fetch failed: ${error}`, { method, url, payload });
      throw error;
    }
  }

  // Get mock data
  getMockData(path?: string): any {
    if (path) {
      return this.mockProvider.get(path);
    }
    return this.mockProvider.export();
  }

  // Set mock data
  setMockData(path: string, value: any): void {
    this.mockProvider.set(path, value);
  }

  // Reset mock data to original
  resetMockData(): void {
    this.mockProvider.reset();
  }

  // Log UI interactions
  logClick(element: { type: string; id?: string; text?: string; selector: string }, wouldNavigate?: string): void {
    this.stubHooks.logClick(element, wouldNavigate);
  }

  logInput(element: { type: string; id?: string; selector: string }, value: any): void {
    this.stubHooks.logInput(element, value);
  }

  logSelect(element: { type: string; id?: string; selector: string }, value: any): void {
    this.stubHooks.logSelect(element, value);
  }

  logSubmit(element: { type: string; id?: string; selector: string }, formData: any, endpoint: string, method: string): void {
    this.stubHooks.logSubmit(element, formData, endpoint, method);
  }

  // Get action logs
  getActionLogs(): ActionLog[] {
    return this.actionLogger.getLogs();
  }

  getRecentActions(n: number = 10): ActionLog[] {
    return this.actionLogger.getRecent(n);
  }

  getActionSummary() {
    return this.actionLogger.getSummary();
  }

  exportActionsAsText(): string {
    return this.actionLogger.exportAsText();
  }

  // Subscribe to actions
  onAction(listener: (action: ActionLog) => void): () => void {
    return this.actionLogger.subscribe(listener);
  }

  // Error handling
  recordError(type: SandboxError['type'], message: string, context?: Record<string, any>): void {
    const error: SandboxError = {
      id: `err-${++this.errorIdCounter}`,
      timestamp: new Date(),
      type,
      message,
      context
    };
    this.errors.push(error);
  }

  getErrors(): SandboxError[] {
    return [...this.errors];
  }

  clearErrors(): void {
    this.errors = [];
  }

  // Get artifact info
  getArtifactInfo() {
    return this.config.artifact;
  }

  // Get API documentation (stubs as OpenAPI-like spec)
  getApiDocs(): { endpoints: Array<{ method: string; path: string; description: string; requestSchema?: any; responseExample: any }> } {
    const stubs = this.stubHooks.getStubs();
    return {
      endpoints: stubs.map(stub => ({
        method: stub.method,
        path: stub.endpoint,
        description: stub.description,
        requestSchema: stub.requestSchema,
        responseExample: stub.mockResponse
      }))
    };
  }

  // Get navigation config
  getNavigationConfig() {
    return this.config.navigation;
  }

  // Internal logging
  private logInfo(message: string, data?: any): void {
    console.log(`[WeftSandbox] ${message}`, data || '');
  }
}

// Factory function to create sandbox from config
export function createSandbox(config: WeftConfig): WeftSandbox {
  return new WeftSandbox(config);
}
