// Weft Sandbox - Main Runtime

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

export class WeftSandbox {
  private config: WeftConfig;
  private mockProvider: MockDataProvider;
  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();
  }

  start(): void {
    this.isRunning = true;
  }

  stop(): void {
    this.isRunning = false;
  }

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

  async handleFetch(method: string, url: string, payload?: unknown): Promise<{ response: unknown; status: number }> {
    if (!this.isRunning) {
      throw new Error('Sandbox is not running');
    }

    const stub = this.findStub(method, url);
    if (!stub) {
      this.recordError('network', `No stub found for ${method} ${url}`);
      return { response: { error: 'Endpoint not stubbed' }, status: 404 };
    }

    // Simulate network delay
    if (stub.mockDelay) {
      await new Promise(resolve => setTimeout(resolve, stub.mockDelay));
    }

    const action = this.actionLogger.createAction({
      actionType: 'API_CALL',
      element: { type: 'fetch', selector: url },
      wouldTrigger: { endpoint: url, method, payload },
      mockResult: stub.mockResponse,
      duration: stub.mockDelay || 50
    });
    this.actionLogger.log(action);

    return {
      response: stub.mockResponse,
      status: stub.mockStatus || 200
    };
  }

  private findStub(method: string, url: string): ApiStub | undefined {
    return this.config.apiStubs.find(
      stub => stub.method === method && this.matchEndpoint(stub.endpoint, url)
    );
  }

  private matchEndpoint(pattern: string, url: string): boolean {
    // Simple pattern matching - supports :param placeholders
    const patternParts = pattern.split('/');
    const urlParts = url.split('/');
    
    if (patternParts.length !== urlParts.length) return false;
    
    return patternParts.every((part, i) => 
      part.startsWith(':') || part === urlParts[i]
    );
  }

  getMockData(path?: string): unknown {
    if (path) {
      return this.mockProvider.get(path);
    }
    return this.mockProvider.export();
  }

  setMockData(path: string, value: unknown): void {
    this.mockProvider.set(path, value);
  }

  resetMockData(): void {
    this.mockProvider.reset();
  }

  logClick(element: { type: string; id?: string; text?: string; selector: string }, wouldNavigate?: string): void {
    const action = this.actionLogger.createAction({
      actionType: 'CLICK',
      element,
      wouldTrigger: { navigation: wouldNavigate }
    });
    this.actionLogger.log(action);
  }

  logInput(element: { type: string; id?: string; selector: string }, value: unknown): void {
    const action = this.actionLogger.createAction({
      actionType: 'INPUT',
      element: { ...element, value },
      wouldTrigger: {}
    });
    this.actionLogger.log(action);
  }

  logSubmit(element: { type: string; id?: string; selector: string }, formData: unknown, endpoint: string, method: string): void {
    const action = this.actionLogger.createAction({
      actionType: 'SUBMIT',
      element,
      wouldTrigger: { endpoint, method, payload: formData }
    });
    this.actionLogger.log(action);
  }

  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();
  }

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

  clearActions(): void {
    this.actionLogger.clear();
  }

  recordError(type: SandboxError['type'], message: string, context?: Record<string, unknown>): 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 = [];
  }

  getArtifactInfo() {
    return this.config.artifact;
  }

  getApiDocs(): { endpoints: Array<{ method: string; path: string; description: string; requestSchema?: Record<string, string>; responseExample: unknown }> } {
    return {
      endpoints: this.config.apiStubs.map(stub => ({
        method: stub.method,
        path: stub.endpoint,
        description: stub.description,
        requestSchema: stub.requestSchema,
        responseExample: stub.mockResponse
      }))
    };
  }

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

  getConfig(): WeftConfig {
    return this.config;
  }
}

export function createSandbox(config: WeftConfig): WeftSandbox {
  return new WeftSandbox(config);
}
