// Weft Sandbox - Stub Hooks Layer

import { ApiStub, ActionLog, StubHooksConfig } from './types';

export class StubHooksLayer {
  private stubs: Map<string, ApiStub> = new Map();
  private onAction: (action: ActionLog) => void;
  private actionIdCounter: number = 0;

  constructor(config: StubHooksConfig) {
    this.onAction = config.onAction;
    
    // Index stubs by method+endpoint
    for (const stub of config.stubs) {
      const key = `${stub.method}:${stub.endpoint}`;
      this.stubs.set(key, stub);
    }
  }

  // Find matching stub for a request
  findStub(method: string, url: string): ApiStub | undefined {
    // Exact match first
    const exactKey = `${method}:${url}`;
    if (this.stubs.has(exactKey)) {
      return this.stubs.get(exactKey);
    }

    // Pattern matching (simple wildcards)
    for (const [key, stub] of this.stubs) {
      const [stubMethod, stubEndpoint] = key.split(':');
      if (stubMethod !== method) continue;

      // Convert endpoint pattern to regex
      const pattern = stubEndpoint
        .replace(/\{[^}]+\}/g, '[^/]+')  // {id} -> match any segment
        .replace(/\*/g, '.*');           // * -> match anything

      const regex = new RegExp(`^${pattern}$`);
      if (regex.test(url)) {
        return stub;
      }
    }

    return undefined;
  }

  // Intercept and mock a fetch call
  async interceptFetch(
    method: string,
    url: string,
    payload?: any
  ): Promise<{ response: any; status: number; stubUsed: ApiStub | null }> {
    const stub = this.findStub(method, url);
    const startTime = Date.now();

    // Log the action
    const action: ActionLog = {
      id: `action-${++this.actionIdCounter}`,
      timestamp: new Date(),
      actionType: 'API_CALL',
      element: {
        type: 'fetch',
        selector: url
      },
      wouldTrigger: {
        endpoint: url,
        method: method,
        payload: payload
      },
      mockResult: stub ? this.resolveMockResponse(stub.mockResponse) : { error: 'No stub found' },
      duration: 0
    };

    // Simulate network delay
    const delay = stub?.mockDelay ?? Math.floor(Math.random() * 200) + 50;
    await new Promise(resolve => setTimeout(resolve, delay));

    action.duration = Date.now() - startTime;
    this.onAction(action);

    if (stub) {
      return {
        response: this.resolveMockResponse(stub.mockResponse),
        status: stub.mockStatus ?? 200,
        stubUsed: stub
      };
    }

    // No stub found - return 404
    return {
      response: { error: 'Endpoint not stubbed', endpoint: url },
      status: 404,
      stubUsed: null
    };
  }

  // Resolve dynamic mock response values
  private resolveMockResponse(response: any): any {
    if (typeof response === 'string') {
      // Handle template variables
      return response
        .replace(/\{\{timestamp\}\}/g, Date.now().toString())
        .replace(/\{\{date\}\}/g, new Date().toISOString().split('T')[0])
        .replace(/\{\{date\+([0-9]+)\}\}/g, (_, days) => {
          const d = new Date();
          d.setDate(d.getDate() + parseInt(days));
          return d.toISOString().split('T')[0];
        })
        .replace(/\{\{uuid\}\}/g, `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`);
    }

    if (Array.isArray(response)) {
      return response.map(item => this.resolveMockResponse(item));
    }

    if (typeof response === 'object' && response !== null) {
      const resolved: Record<string, any> = {};
      for (const [key, value] of Object.entries(response)) {
        resolved[key] = this.resolveMockResponse(value);
      }
      return resolved;
    }

    return response;
  }

  // Log UI interactions
  logClick(element: { type: string; id?: string; text?: string; selector: string }, wouldNavigate?: string): void {
    const action: ActionLog = {
      id: `action-${++this.actionIdCounter}`,
      timestamp: new Date(),
      actionType: 'CLICK',
      element,
      wouldTrigger: wouldNavigate ? { navigation: wouldNavigate } : {}
    };
    this.onAction(action);
  }

  logInput(element: { type: string; id?: string; selector: string }, value: any): void {
    const action: ActionLog = {
      id: `action-${++this.actionIdCounter}`,
      timestamp: new Date(),
      actionType: 'INPUT',
      element: { ...element, value },
      wouldTrigger: {}
    };
    this.onAction(action);
  }

  logSelect(element: { type: string; id?: string; selector: string }, value: any): void {
    const action: ActionLog = {
      id: `action-${++this.actionIdCounter}`,
      timestamp: new Date(),
      actionType: 'SELECT',
      element: { ...element, value },
      wouldTrigger: {}
    };
    this.onAction(action);
  }

  logSubmit(element: { type: string; id?: string; selector: string }, formData: any, endpoint: string, method: string): void {
    const action: ActionLog = {
      id: `action-${++this.actionIdCounter}`,
      timestamp: new Date(),
      actionType: 'SUBMIT',
      element,
      wouldTrigger: {
        endpoint,
        method,
        payload: formData
      }
    };
    this.onAction(action);
  }

  // Get all registered stubs (for documentation)
  getStubs(): ApiStub[] {
    return Array.from(this.stubs.values());
  }

  // Add or update a stub
  addStub(stub: ApiStub): void {
    const key = `${stub.method}:${stub.endpoint}`;
    this.stubs.set(key, stub);
  }

  // Remove a stub
  removeStub(method: string, endpoint: string): boolean {
    const key = `${method}:${endpoint}`;
    return this.stubs.delete(key);
  }
}
