import { useState, useEffect, useCallback, useRef } from 'react';
import { LadderEngine } from '../../engine/engine';
import type { Program, EngineState } from '../../engine/types';
import { storageService } from '../services/storage';

// Singleton engine instance for the application
const engine = new LadderEngine();
(window as any).engine = engine; // Expose for debugging

export function useEngine() {
  const [state, setState] = useState<EngineState>(() => engine.getState());
  const [program, setProgram] = useState<Program | null>(() => engine.getProgram() || null);
  const [currentProgramId, setCurrentProgramId] = useState<string | null>(null);
  const stopFnRef = useRef<(() => void) | null>(null);

  useEffect(() => {
    const unsubscribe = engine.subscribe(() => {
      setState({ ...engine.getState() });
      const currentProgram = engine.getProgram();
      if (currentProgram) {
        setProgram(currentProgram);
      }
    });

    return () => unsubscribe();
  }, []);

  const loadProgram = useCallback((newProgram: Program) => {
    engine.loadProgram(newProgram);
    setProgram(newProgram);
  }, []);

  const start = useCallback(() => {
    const stopFn = engine.start();
    stopFnRef.current = stopFn;
  }, []);

  const stop = useCallback(() => {
    if (stopFnRef.current) {
      stopFnRef.current();
      stopFnRef.current = null;
    }
  }, []);

  const newProgram = useCallback((name?: string) => {
    const freshProgram: Program = {
      name: name || 'Untitled Program',
      rungs: [],
      cycleTime: 100,
    };
    loadProgram(freshProgram);
    setCurrentProgramId(null);
    return freshProgram;
  }, [loadProgram]);

  const saveCurrentProgram = useCallback(async () => {
    if (!program) return;
    try {
      const id = await storageService.saveProgram(program, currentProgramId || undefined);
      setCurrentProgramId(id);
    } catch (error) {
      console.error('Failed to save program:', error);
      alert('Failed to save program. Please try again.');
    }
  }, [program, currentProgramId]);

  const loadProgramById = useCallback(async (id: string) => {
    try {
      const loaded = await storageService.loadProgram(id);
      engine.loadProgram(loaded);
      setProgram(loaded);
      setCurrentProgramId(id);
    } catch (error) {
      console.error('Failed to load program:', error);
      alert('Failed to load program.');
    }
  }, []);

  const listPrograms = useCallback(async () => {
    try {
      return await storageService.listPrograms();
    } catch (error) {
      console.error('Failed to list programs:', error);
      return [];
    }
  }, []);

  const deleteProgramById = useCallback(async (id: string) => {
    try {
      await storageService.deleteProgram(id);
      if (currentProgramId === id) {
        const freshProgram = newProgram();
        setCurrentProgramId(null);
        loadProgram(freshProgram);
      }
    } catch (error) {
      console.error('Failed to delete program:', error);
      alert('Failed to delete program.');
    }
  }, [currentProgramId, newProgram, loadProgram]);

  const deleteCurrentProgram = useCallback(async () => {
    if (currentProgramId) {
      await deleteProgramById(currentProgramId);
    }
  }, [currentProgramId, deleteProgramById]);

  const renameCurrentProgram = useCallback(async (newName: string) => {
    if (currentProgramId && program) {
      try {
        await storageService.renameProgram(currentProgramId, newName);
        const updatedProgram = { ...program, name: newName };
        engine.loadProgram(updatedProgram);
        setProgram(updatedProgram);
      } catch (error) {
        console.error('Failed to rename program:', error);
        alert('Failed to rename program.');
      }
    }
  }, [currentProgramId, program, engine]);

  return {
    state,
    engine,
    program,
    currentProgramId,
    loadProgram,
    start,
    stop,
    newProgram,
    saveCurrentProgram,
    loadProgramById,
    listPrograms,
    deleteProgramById,
    deleteCurrentProgram,
    renameCurrentProgram,
  };
}
