import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { Machine, LogEntry } from '../types'
import { log } from '../utils/logger'
import { mockMachines } from '../mocks/mockMachines'
import { guacamoleService } from '../services/guacamole-api'
import { savedMachinesApi, SavedMachine, SavedMachineCreate, SavedMachineUpdate } from '../services/saved-machines-api'

const DEFAULT_OS = 'Unknown';
const SAVED_HYPERVISOR = 'Saved';
const SPEC_NOT_AVAILABLE = 'N/A';

/**
 * Convert SavedMachine to Machine for display in Sidebar
 */
function convertSavedMachineToMachine(saved: SavedMachine): Machine {
  return {
    id: saved.id,
    name: saved.name,
    status: 'unknown',
    os: saved.os && saved.os.trim() !== '' ? saved.os : DEFAULT_OS,
    ip: saved.hostname,
    hypervisor: SAVED_HYPERVISOR,
    port: saved.port,
    protocol: saved.protocol,
    specs: {
      cpu: SPEC_NOT_AVAILABLE,
      ram: SPEC_NOT_AVAILABLE,
      disk: SPEC_NOT_AVAILABLE
    },
    // Mark as saved machine to distinguish from mock
    testLinks: [{
      url: `${saved.protocol}://${saved.hostname}:${saved.port}`,
      name: `${saved.protocol.toUpperCase()} (Port ${saved.port})`
    }],
    logs: []
  };
}

interface MachineState {
  machines: Machine[];
  selectedMachine: Machine | null;
  loading: boolean;
  error: string | null;
  guacamoleVisible: boolean;
  searchQuery: string;
  filteredMachines: Machine[];
  
  // Saved machines
  savedMachines: SavedMachine[];
  savedMachinesLoading: boolean;
  showAddMachineModal: boolean;
  showSaveConfirmModal: boolean;
  machineToSave: SavedMachineCreate | null;
  
  // Actions
  setMachines: (machines: Machine[]) => void;
  selectMachine: (machine: Machine | null) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;
  setGuacamoleVisible: (visible: boolean) => void;
  setSearchQuery: (query: string) => void;
  setShowAddMachineModal: (show: boolean) => void;
  setShowSaveConfirmModal: (show: boolean) => void;
  setMachineToSave: (machine: SavedMachineCreate | null) => void;
  
  // Async actions
  fetchMachines: () => Promise<void>;
  connectToMachine: (machine: Machine) => Promise<void>;
  controlMachine: (machineId: string, action: 'start' | 'stop' | 'restart') => Promise<void>;
  connectGuacamole: (machine: Machine) => Promise<void>;
  
  // Saved machines actions
  fetchSavedMachines: (includeStats?: boolean) => Promise<void>;
  createSavedMachine: (machine: SavedMachineCreate) => Promise<SavedMachine>;
  updateSavedMachine: (machineId: string, updates: SavedMachineUpdate) => Promise<SavedMachine>;
  deleteSavedMachine: (machineId: string) => Promise<void>;
  toggleFavorite: (machineId: string) => Promise<void>;
  connectToSavedMachine: (machineId: string) => Promise<void>;
  
  // Utility actions
  resetState: () => void;
}

export const useMachineStore = create<MachineState>()(
  persist(
    (set, get) => {
      // Add debug log when creating store
      log.info('store', 'Initializing machine store with persistence');
      
      return {
        machines: [],
        selectedMachine: null,
        loading: false,
        error: null,
        guacamoleVisible: false,
        searchQuery: '',
        filteredMachines: [],
        
        // Saved machines state
        savedMachines: [],
        savedMachinesLoading: false,
        showAddMachineModal: false,
        showSaveConfirmModal: false,
        machineToSave: null,

    // Synchronous actions
    setMachines: (machines) => {
      log.info('store', 'Setting machines', { count: machines.length });
      set({ 
        machines,
        filteredMachines: machines.filter(machine => 
          machine.name.toLowerCase().includes(get().searchQuery.toLowerCase()) ||
          machine.ip.toLowerCase().includes(get().searchQuery.toLowerCase())
        )
      });
    },

    selectMachine: (machine) => set({ selectedMachine: machine }),
    setLoading: (loading) => set({ loading }),
    setError: (error) => set({ error }),
    setGuacamoleVisible: (visible) => set({ guacamoleVisible: visible }),
    setShowAddMachineModal: (show) => set({ showAddMachineModal: show }),
    setShowSaveConfirmModal: (show) => set({ showSaveConfirmModal: show }),
    setMachineToSave: (machine) => set({ machineToSave: machine }),
    
    setSearchQuery: (query) => {
      const { machines } = get();
      set({ 
        searchQuery: query,
        filteredMachines: machines.filter(machine => 
          machine.name.toLowerCase().includes(query.toLowerCase()) ||
          machine.ip.toLowerCase().includes(query.toLowerCase())
        )
      });
    },

    // Asynchronous actions
    fetchMachines: async () => {
      const { setLoading, setError, savedMachines } = get();
      setLoading(true);
      try {
        if (import.meta.env.DEV) {
          // In development mode, merge saved machines with mock data
          const savedAsMachines = savedMachines.map(convertSavedMachineToMachine);
          const allMachines = [...savedAsMachines, ...mockMachines];
          
          log.info('store', 'Merging saved and mock machines', { 
            savedCount: savedAsMachines.length,
            mockCount: mockMachines.length,
            totalCount: allMachines.length
          });
          
          set({ 
            machines: allMachines,
            filteredMachines: allMachines.filter(machine => 
              machine.name.toLowerCase().includes(get().searchQuery.toLowerCase()) ||
              machine.ip.toLowerCase().includes(get().searchQuery.toLowerCase())
            )
          });
          setError(null);
        } else {
          const result = await window.electronAPI.getMachineList();
          if (result.success && result.machines) {
            log.info('store', 'Machines fetched successfully', {
              count: result.machines.length
            });
            
            // In production, also merge with saved machines
            const savedAsMachines = savedMachines.map(convertSavedMachineToMachine);
            const allMachines = [...savedAsMachines, ...result.machines];
            
            set({ 
              machines: allMachines,
              filteredMachines: allMachines.filter(machine => 
                machine.name.toLowerCase().includes(get().searchQuery.toLowerCase()) ||
                machine.ip.toLowerCase().includes(get().searchQuery.toLowerCase())
              )
            });
            setError(null);
          } else {
            throw new Error(result.message || 'Failed to fetch machines');
          }
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to fetch machines';
        log.error('store', 'Failed to fetch machines', { error: errorMessage });
        setError(errorMessage);
      } finally {
        setLoading(false);
      }
    },

    connectToMachine: async (machine) => {
      const { setError } = get();
      try {
        log.info('store', 'Connecting to machine', {
          machineId: machine.id,
          machineName: machine.name
        });

        if (import.meta.env.DEV) {
          // In development mode, simulate successful connection
          log.info('store', 'Mock connection successful');
          setError(null);
        } else {
          const result = await window.electronAPI.connectToMachine(machine);
          if (!result.success) {
            throw new Error(result.message);
          }
          log.info('store', 'Connected to machine successfully', {
            machineId: machine.id
          });
          setError(null);
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to connect to machine';
        log.error('store', 'Connection failed', {
          machineId: machine.id,
          error: errorMessage
        });
        setError(errorMessage);
        throw error;
      }
    },

    controlMachine: async (machineId, action) => {
      const { setError, fetchMachines, machines } = get();
      try {
        log.info('store', `Controlling machine: ${action}`, { machineId });

        if (import.meta.env.DEV) {
          // In development mode, simulate state change
          const updatedMachines = machines.map(machine => {
            if (machine.id === machineId) {
              const newLog: LogEntry = {
                timestamp: new Date().toISOString(),
                message: `Machine ${action} successful`,
                level: 'info'
              };
              return {
                ...machine,
                status: action === 'start' ? 'running' : action === 'stop' ? 'stopped' : machine.status,
                logs: [newLog, ...machine.logs]
              };
            }
            return machine;
          });
          set({ machines: updatedMachines });
          log.info('store', `Mock machine ${action} successful`, { machineId });
          setError(null);
        } else {
          const result = await window.electronAPI.controlMachine(machineId, action);
          if (!result.success) {
            throw new Error(result.message);
          }
          log.info('store', `Machine ${action} successful`, { machineId });
          setError(null);
          await fetchMachines();
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : `Failed to ${action} machine`;
        log.error('store', `Machine ${action} failed`, {
          machineId,
          error: errorMessage
        });
        setError(errorMessage);
        throw error;
      }
    },

    connectGuacamole: async (machine) => {
      const { setError, setGuacamoleVisible } = get();
      try {
        log.info('store', 'Connecting to Guacamole', {
          machineId: machine.id,
          machineName: machine.name
        });

        // In development mode, just show component
        if (import.meta.env.DEV) {
          setGuacamoleVisible(true);
          return;
        }

        // In production, create connection
        const result = await guacamoleService.createConnection(machine);
        if (!result.connection_url) {
          throw new Error('Failed to create Guacamole connection');
        }

        setGuacamoleVisible(true);
        setError(null);
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to connect to Guacamole';
        log.error('store', 'Guacamole connection failed', {
          machineId: machine.id,
          error: errorMessage
        });
        setError(errorMessage);
        throw error;
      }
    },

    // ========================================================================================
    // Saved Machines Actions
    // ========================================================================================

    fetchSavedMachines: async (includeStats = false) => {
      set({ savedMachinesLoading: true });
      try {
        log.info('store', 'Fetching saved machines', { includeStats });
        
        const result = await savedMachinesApi.getSavedMachines(includeStats);
        
        set({ 
          savedMachines: result.machines,
          savedMachinesLoading: false
        });
        
        log.info('store', 'Saved machines fetched successfully', {
          total: result.total
        });
        
        // After loading saved machines, update general list
        // (fetchMachines will merge savedMachines with mock/electron data)
        await get().fetchMachines();
        
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to fetch saved machines';
        log.error('store', 'Failed to fetch saved machines', { error: errorMessage });
        set({ 
          error: errorMessage,
          savedMachinesLoading: false 
        });
        throw error;
      }
    },

    createSavedMachine: async (machine) => {
      try {
        log.info('store', 'Creating saved machine', { name: machine.name });
        
        const created = await savedMachinesApi.createSavedMachine(machine);
        
        // Add to savedMachines
        set(state => ({ 
          savedMachines: [...state.savedMachines, created]
        }));
        
        // Update general list (fetchMachines will merge savedMachines with mock)
        await get().fetchMachines();
        
        log.info('store', 'Saved machine created', { 
          id: created.id,
          name: created.name
        });
        
        return created;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to create saved machine';
        log.error('store', 'Failed to create saved machine', { error: errorMessage });
        set({ error: errorMessage });
        throw error;
      }
    },

    updateSavedMachine: async (machineId, updates) => {
      try {
        log.info('store', 'Updating saved machine', { machineId, updates });
        
        const updated = await savedMachinesApi.updateSavedMachine(machineId, updates);
        
        // Update in savedMachines
        set(state => ({
          savedMachines: state.savedMachines.map(m => 
            m.id === machineId ? updated : m
          )
        }));
        
        // Update general list
        await get().fetchMachines();
        
        log.info('store', 'Saved machine updated', { id: updated.id });
        
        return updated;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to update saved machine';
        log.error('store', 'Failed to update saved machine', { error: errorMessage });
        set({ error: errorMessage });
        throw error;
      }
    },

    deleteSavedMachine: async (machineId) => {
      try {
        log.info('store', 'Deleting saved machine', { machineId });
        
        await savedMachinesApi.deleteSavedMachine(machineId);
        
        // Remove from savedMachines
        set(state => ({
          savedMachines: state.savedMachines.filter(m => m.id !== machineId)
        }));
        
        // Update general list
        await get().fetchMachines();
        
        log.info('store', 'Saved machine deleted', { machineId });
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to delete saved machine';
        log.error('store', 'Failed to delete saved machine', { error: errorMessage });
        set({ error: errorMessage });
        throw error;
      }
    },

    toggleFavorite: async (machineId) => {
      try {
        const machine = get().savedMachines.find(m => m.id === machineId);
        if (!machine) {
          throw new Error('Machine not found');
        }
        
        log.info('store', 'Toggling favorite', { machineId, current: machine.is_favorite });
        
        await get().updateSavedMachine(machineId, {
          is_favorite: !machine.is_favorite
        });
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to toggle favorite';
        log.error('store', 'Failed to toggle favorite', { error: errorMessage });
        set({ error: errorMessage });
        throw error;
      }
    },

    connectToSavedMachine: async (machineId) => {
      try {
        log.info('store', 'Connecting to saved machine', { machineId });
        
        // Record connection in history
        await savedMachinesApi.connectToSavedMachine(machineId);
        
        // Update local list
        await get().fetchSavedMachines();
        
        log.info('store', 'Connected to saved machine', { machineId });
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to connect to saved machine';
        log.error('store', 'Failed to connect to saved machine', { error: errorMessage });
        set({ error: errorMessage });
        throw error;
      }
    },

    // ========================================================================================
    // Utility Actions
    // ========================================================================================

    resetState: () => {
      log.info('store', 'Resetting all state (logout/user switch)');
      
      set({
        machines: [],
        selectedMachine: null,
        loading: false,
        error: null,
        guacamoleVisible: false,
        searchQuery: '',
        filteredMachines: [],
        savedMachines: [],
        savedMachinesLoading: false,
        showAddMachineModal: false,
        showSaveConfirmModal: false,
        machineToSave: null
      });
      
      log.info('store', 'State reset complete');
    }
  };
},
{
  name: 'machine-store', // Unique name for localStorage
  storage: createJSONStorage(() => localStorage),
  // Choose what to persist (don't persist loading states and modals)
  partialize: (state) => ({
    machines: state.machines,
    selectedMachine: state.selectedMachine,
    savedMachines: state.savedMachines,
    searchQuery: state.searchQuery,
  }),
}
)
); 