'use client';

import React, { createContext, useContext, useState, useCallback, useEffect, useMemo } from 'react';
import { WeftSandbox, createSandbox, WeftConfig, ActionLog, SandboxError, ActionSummary } from '@/lib/weft';

interface SandboxContextValue {
  sandbox: WeftSandbox | null;
  isRunning: boolean;
  actionLogs: ActionLog[];
  errors: SandboxError[];
  summary: ActionSummary | null;
  mockData: Record<string, unknown>;
  
  // Actions
  initSandbox: (config: WeftConfig) => void;
  startSandbox: () => void;
  stopSandbox: () => void;
  clearActions: () => void;
  clearErrors: () => void;
  resetMockData: () => void;
  setMockData: (path: string, value: unknown) => void;
  simulateFetch: (method: string, url: string, payload?: unknown) => Promise<{ response: unknown; status: number }>;
  logClick: (element: { type: string; id?: string; text?: string; selector: string }, wouldNavigate?: string) => void;
  logInput: (element: { type: string; id?: string; selector: string }, value: unknown) => void;
  logSubmit: (element: { type: string; id?: string; selector: string }, formData: unknown, endpoint: string, method: string) => void;
}

const SandboxContext = createContext<SandboxContextValue | null>(null);

export function useSandbox() {
  const context = useContext(SandboxContext);
  if (!context) {
    throw new Error('useSandbox must be used within a WeftSandboxProvider');
  }
  return context;
}

export function WeftSandboxProvider({ children, initialConfig }: { children: React.ReactNode; initialConfig?: WeftConfig }) {
  const [sandbox, setSandbox] = useState<WeftSandbox | null>(null);
  const [isRunning, setIsRunning] = useState(false);
  const [actionLogs, setActionLogs] = useState<ActionLog[]>([]);
  const [errors, setErrors] = useState<SandboxError[]>([]);
  const [mockData, setMockDataState] = useState<Record<string, unknown>>({});
  const [summary, setSummary] = useState<ActionSummary | null>(null);

  // Initialize sandbox
  const initSandbox = useCallback((config: WeftConfig) => {
    const newSandbox = createSandbox(config);
    setSandbox(newSandbox);
    setMockDataState(config.mockData);
    setIsRunning(false);
    setActionLogs([]);
    setErrors([]);
    setSummary(null);
  }, []);

  // Initialize with initial config if provided
  useEffect(() => {
    if (initialConfig && !sandbox) {
      initSandbox(initialConfig);
    }
  }, [initialConfig, sandbox, initSandbox]);

  // Subscribe to action changes
  useEffect(() => {
    if (!sandbox) return;

    const unsubscribe = sandbox.onAction(() => {
      setActionLogs(sandbox.getActionLogs());
      setSummary(sandbox.getActionSummary());
    });

    return unsubscribe;
  }, [sandbox]);

  const startSandbox = useCallback(() => {
    if (sandbox) {
      sandbox.start();
      setIsRunning(true);
    }
  }, [sandbox]);

  const stopSandbox = useCallback(() => {
    if (sandbox) {
      sandbox.stop();
      setIsRunning(false);
    }
  }, [sandbox]);

  const clearActions = useCallback(() => {
    if (sandbox) {
      sandbox.clearActions();
      setActionLogs([]);
      setSummary(null);
    }
  }, [sandbox]);

  const clearErrors = useCallback(() => {
    if (sandbox) {
      sandbox.clearErrors();
      setErrors([]);
    }
  }, [sandbox]);

  const resetMockData = useCallback(() => {
    if (sandbox) {
      sandbox.resetMockData();
      setMockDataState(sandbox.getMockData() as Record<string, unknown>);
    }
  }, [sandbox]);

  const setMockData = useCallback((path: string, value: unknown) => {
    if (sandbox) {
      sandbox.setMockData(path, value);
      setMockDataState(sandbox.getMockData() as Record<string, unknown>);
    }
  }, [sandbox]);

  const simulateFetch = useCallback(async (method: string, url: string, payload?: unknown) => {
    if (!sandbox) {
      return { response: { error: 'Sandbox not initialized' }, status: 500 };
    }
    const result = await sandbox.handleFetch(method, url, payload);
    setActionLogs(sandbox.getActionLogs());
    setSummary(sandbox.getActionSummary());
    return result;
  }, [sandbox]);

  const logClick = useCallback((element: { type: string; id?: string; text?: string; selector: string }, wouldNavigate?: string) => {
    if (sandbox && isRunning) {
      sandbox.logClick(element, wouldNavigate);
      setActionLogs(sandbox.getActionLogs());
      setSummary(sandbox.getActionSummary());
    }
  }, [sandbox, isRunning]);

  const logInput = useCallback((element: { type: string; id?: string; selector: string }, value: unknown) => {
    if (sandbox && isRunning) {
      sandbox.logInput(element, value);
      setActionLogs(sandbox.getActionLogs());
      setSummary(sandbox.getActionSummary());
    }
  }, [sandbox, isRunning]);

  const logSubmit = useCallback((element: { type: string; id?: string; selector: string }, formData: unknown, endpoint: string, method: string) => {
    if (sandbox && isRunning) {
      sandbox.logSubmit(element, formData, endpoint, method);
      setActionLogs(sandbox.getActionLogs());
      setSummary(sandbox.getActionSummary());
    }
  }, [sandbox, isRunning]);

  const value = useMemo(() => ({
    sandbox,
    isRunning,
    actionLogs,
    errors,
    summary,
    mockData,
    initSandbox,
    startSandbox,
    stopSandbox,
    clearActions,
    clearErrors,
    resetMockData,
    setMockData,
    simulateFetch,
    logClick,
    logInput,
    logSubmit,
  }), [sandbox, isRunning, actionLogs, errors, summary, mockData, initSandbox, startSandbox, stopSandbox, clearActions, clearErrors, resetMockData, setMockData, simulateFetch, logClick, logInput, logSubmit]);

  return (
    <SandboxContext.Provider value={value}>
      {children}
    </SandboxContext.Provider>
  );
}
