/**
 * Update State Store
 * Manages application update state
 */
import { create } from 'zustand';
import { useSettingsStore } from './settings';
import { invokeIpc } from '@/lib/api-client';

export interface UpdateInfo {
  version: string;
  releaseDate?: string;
  releaseNotes?: string | null;
}

export interface ProgressInfo {
  total: number;
  delta: number;
  transferred: number;
  percent: number;
  bytesPerSecond: number;
}

export type UpdateStatus = 
  | 'idle'
  | 'checking'
  | 'available'
  | 'not-available'
  | 'downloading'
  | 'downloaded'
  | 'error';

interface UpdateState {
  status: UpdateStatus;
  currentVersion: string;
  updateInfo: UpdateInfo | null;
  progress: ProgressInfo | null;
  error: string | null;
  isInitialized: boolean;
  /** Seconds remaining before auto-install, or null if inactive. */
  autoInstallCountdown: number | null;

  // Actions
  init: () => Promise<void>;
  checkForUpdates: () => Promise<void>;
  downloadUpdate: () => Promise<void>;
  installUpdate: () => void;
  cancelAutoInstall: () => Promise<void>;
  setChannel: (channel: 'stable' | 'beta' | 'dev') => Promise<void>;
  setAutoDownload: (enable: boolean) => Promise<void>;
  clearError: () => void;
}

let updateInitPromise: Promise<void> | null = null;

export const useUpdateStore = create<UpdateState>((set, get) => ({
  status: 'idle',
  currentVersion: '0.0.0',
  updateInfo: null,
  progress: null,
  error: null,
  isInitialized: false,
  autoInstallCountdown: null,

  init: async () => {
    if (get().isInitialized) return;
    if (updateInitPromise) return updateInitPromise;

    updateInitPromise = (async () => {
      // Get current version
      try {
        const version = await invokeIpc<string>('update:version');
        set({ currentVersion: version as string });
      } catch (error) {
        console.error('Failed to get version:', error);
      }

      // Get current status
      try {
        const status = await invokeIpc<{
          status: UpdateStatus;
          info?: UpdateInfo;
          progress?: ProgressInfo;
          error?: string;
        }>('update:status');
        set({
          status: status.status,
          updateInfo: status.info || null,
          progress: status.progress || null,
          error: status.error || null,
        });
      } catch (error) {
        console.error('Failed to get update status:', error);
      }

      // Listen for update events
      // Single source of truth: listen only to update:status-changed
      // (sent by AppUpdater.updateStatus() in the main process)
      window.electron.ipcRenderer.on('update:status-changed', (data) => {
        const status = data as {
          status: UpdateStatus;
          info?: UpdateInfo;
          progress?: ProgressInfo;
          error?: string;
        };
        set({
          status: status.status,
          updateInfo: status.info || null,
          progress: status.progress || null,
          error: status.error || null,
        });
      });

      window.electron.ipcRenderer.on('update:auto-install-countdown', (data) => {
        const { seconds, cancelled } = data as { seconds: number; cancelled?: boolean };
        set({ autoInstallCountdown: cancelled ? null : seconds });
      });

      // New default is prompt-first: never auto-download/install unless the
      // user explicitly chooses Download from the notification or Settings.
      void invokeIpc('update:setAutoDownload', false).catch(() => {});

      set({ isInitialized: true });

      // Auto-check for updates on startup (respects user toggle)
      const autoCheckUpdate = useSettingsStore.getState().autoCheckUpdate;
      if (autoCheckUpdate) {
        setTimeout(() => {
          get().checkForUpdates().catch(() => {});
        }, 10000);
      }
    })();

    try {
      await updateInitPromise;
    } finally {
      if (!get().isInitialized) {
        updateInitPromise = null;
      }
    }
  },

  checkForUpdates: async () => {
    set({ status: 'checking', error: null });
    
    try {
      const result = await Promise.race([
        invokeIpc('update:check'),
        new Promise((_, reject) => setTimeout(() => reject(new Error('Update check timed out')), 30000))
      ]) as {
        success: boolean;
        error?: string;
        status?: {
          status: UpdateStatus;
          info?: UpdateInfo;
          progress?: ProgressInfo;
          error?: string;
        };
      };
      
      if (result.status) {
        set({
          status: result.status.status,
          updateInfo: result.status.info || null,
          progress: result.status.progress || null,
          error: result.status.error || null,
        });
      } else if (!result.success) {
        set({ status: 'error', error: result.error || 'Failed to check for updates' });
      }
    } catch (error) {
      set({ status: 'error', error: String(error) });
    } finally {
      // In dev mode autoUpdater skips without emitting events, so the
      // status may still be 'checking' or even 'idle'. Catch both.
      const currentStatus = get().status;
      if (currentStatus === 'checking' || currentStatus === 'idle') {
        set({ status: 'error', error: 'Update check completed without a result. This usually means the app is running in dev mode.' });
      }
    }
  },

  downloadUpdate: async () => {
    set({ status: 'downloading', error: null });
    
    try {
      const result = await invokeIpc<{
        success: boolean;
        error?: string;
      }>('update:download');
      
      if (!result.success) {
        set({ status: 'error', error: result.error || 'Failed to download update' });
      }
    } catch (error) {
      set({ status: 'error', error: String(error) });
    }
  },

  installUpdate: () => {
    void invokeIpc('update:install');
  },

  cancelAutoInstall: async () => {
    try {
      await invokeIpc('update:cancelAutoInstall');
    } catch (error) {
      console.error('Failed to cancel auto-install:', error);
    }
  },

  setChannel: async (channel) => {
    try {
      await invokeIpc('update:setChannel', channel);
    } catch (error) {
      console.error('Failed to set update channel:', error);
    }
  },

  setAutoDownload: async (enable) => {
    try {
      // Compatibility shim for older UI paths: the updater is now prompt-first,
      // so we keep electron-updater.autoDownload disabled even if a stale
      // persisted setting says otherwise.
      await invokeIpc('update:setAutoDownload', false);
      if (enable) {
        console.info('[Update] Auto-download preference ignored; update prompts are shown instead.');
      }
    } catch (error) {
      console.error('Failed to set auto-download:', error);
    }
  },

  clearError: () => set({ error: null, status: 'idle' }),
}));