import { contextBridge, ipcRenderer } from 'electron';
import { Command, Query } from './cqrs/types';

const LINUX_SCREEN_SHARE_MONITOR_AUDIO_CHUNK_CHANNEL = 'linux-screen-share-monitor-audio-chunk';
const LINUX_SCREEN_SHARE_MONITOR_AUDIO_ENDED_CHANNEL = 'linux-screen-share-monitor-audio-ended';
const AUTO_UPDATE_STATE_CHANGED_CHANNEL = 'auto-update-state-changed';
const DEEP_LINK_RECEIVED_CHANNEL = 'deep-link-received';
const WINDOW_STATE_CHANGED_CHANNEL = 'window-state-changed';
const IDLE_STATE_CHANGED_CHANNEL = 'idle-state-changed';

export interface LinuxScreenShareAudioRoutingInfo {
  available: boolean;
  active: boolean;
  monitorCaptureSupported: boolean;
  screenShareSinkName: string;
  screenShareMonitorSourceName: string;
  voiceSinkName: string;
  reason?: string;
}

export interface LinuxScreenShareMonitorCaptureInfo {
  bitsPerSample: number;
  captureId: string;
  channelCount: number;
  sampleRate: number;
  sourceName: string;
}

export interface LinuxScreenShareMonitorAudioChunkPayload {
  captureId: string;
  chunk: Uint8Array;
}

export interface LinuxScreenShareMonitorAudioEndedPayload {
  captureId: string;
  reason?: string;
}

export interface ClipboardFilePayload {
  data: string;
  lastModified: number;
  mime: string;
  name: string;
  path?: string;
}

export type DesktopUpdateServerVersionStatus = 'unknown' | 'reported' | 'missing' | 'unavailable';

export interface DesktopUpdateServerContext {
  manifestUrls: string[];
  serverVersion: string | null;
  serverVersionStatus: DesktopUpdateServerVersionStatus;
}

export interface DesktopUpdateServerHealthSnapshot {
  manifestUrl: string | null;
  serverVersion: string | null;
  serverVersionStatus: DesktopUpdateServerVersionStatus;
}

export interface DesktopUpdateState {
  autoUpdateMode: 'auto' | 'off' | 'version';
  availableVersions: string[];
  configuredManifestUrls: string[];
  currentVersion: string;
  defaultManifestUrls: string[];
  isSupported: boolean;
  lastCheckedAt: number | null;
  latestVersion: string | null;
  manifestUrl: string | null;
  manifestUrls: string[];
  minimumServerVersion: string | null;
  preferredVersion: string | null;
  restartRequired: boolean;
  serverBlocked: boolean;
  serverBlockMessage: string | null;
  serverVersion: string | null;
  serverVersionStatus: DesktopUpdateServerVersionStatus;
  status:
    | 'idle'
    | 'disabled'
    | 'checking'
    | 'downloading'
    | 'up-to-date'
    | 'restart-required'
    | 'unsupported'
    | 'no-manifest'
    | 'target-unavailable'
    | 'target-older-than-installed'
    | 'error';
  statusMessage: string | null;
  targetVersion: string | null;
}

export interface DesktopNotificationPayload {
  body: string;
  requestAttention: boolean;
  title: string;
}

export interface WindowStateSnapshot {
  isFocused: boolean;
  isMinimized: boolean;
}

export interface SavedThemeFileDescriptor {
  fileName: string;
  modifiedAt: number;
  path: string;
}

export interface LocalPluginManifestDescriptor {
  discoveredAt: number;
  entrypointPath?: string;
  pluginRootUrl: string;
  manifest: unknown;
  manifestPath: string;
  pluginRoot: string;
  readmePath?: string;
}

export interface LocalApiSettings {
  enabled: boolean;
  port: number;
  exposeOnLan: boolean;
  scalarEnabled: boolean;
  docusaurusEnabled: boolean;
  allowedSignalingServers: string[];
}

export type LocalApiStatus = 'stopped' | 'starting' | 'running' | 'error';

export interface LocalApiSnapshot {
  status: LocalApiStatus;
  host: string | null;
  port: number | null;
  baseUrl: string | null;
  error: string | null;
  exposeOnLan: boolean;
  scalarEnabled: boolean;
  docusaurusEnabled: boolean;
}

export interface LocalPluginDiscoveryError {
  manifestPath?: string;
  message: string;
  pluginRoot?: string;
}

export interface LocalPluginDiscoveryResult {
  errors: LocalPluginDiscoveryError[];
  plugins: LocalPluginManifestDescriptor[];
  pluginsPath: string;
}

export interface ExportUserDataResult {
  cancelled: boolean;
  exported: boolean;
  filePath?: string;
}

export interface ImportUserDataResult {
  backupPath?: string;
  cancelled: boolean;
  imported: boolean;
  restartRequired: boolean;
}

export interface EraseUserDataResult {
  erased: boolean;
  restartRequired: boolean;
}

function readLinuxDisplayServer(): string {
  if (process.platform !== 'linux') {
    return 'N/A';
  }

  try {
    const displayServer = ipcRenderer.sendSync('get-linux-display-server');

    return typeof displayServer === 'string' && displayServer.trim().length > 0
      ? displayServer
      : 'Unknown (Linux)';
  } catch {
    return 'Unknown (Linux)';
  }
}

export interface ContextMenuParams {
  posX: number;
  posY: number;
  isEditable: boolean;
  selectionText: string;
  linkURL: string;
  mediaType: string;
  srcURL: string;
  editFlags: {
    canCut: boolean;
    canCopy: boolean;
    canPaste: boolean;
    canSelectAll: boolean;
  };
}

export interface ElectronAPI {
  linuxDisplayServer: string;
  minimizeWindow: () => void;
  maximizeWindow: () => void;
  closeWindow: () => void;

  openExternal: (url: string) => Promise<boolean>;
  getSources: () => Promise<{ id: string; name: string; thumbnail: string }[]>;
  getRunningProcessNames: () => Promise<string[]>;
  prepareLinuxScreenShareAudioRouting: () => Promise<LinuxScreenShareAudioRoutingInfo>;
  activateLinuxScreenShareAudioRouting: () => Promise<LinuxScreenShareAudioRoutingInfo>;
  deactivateLinuxScreenShareAudioRouting: () => Promise<boolean>;
  startLinuxScreenShareMonitorCapture: () => Promise<LinuxScreenShareMonitorCaptureInfo>;
  stopLinuxScreenShareMonitorCapture: (captureId?: string) => Promise<boolean>;
  onLinuxScreenShareMonitorAudioChunk: (listener: (payload: LinuxScreenShareMonitorAudioChunkPayload) => void) => () => void;
  onLinuxScreenShareMonitorAudioEnded: (listener: (payload: LinuxScreenShareMonitorAudioEndedPayload) => void) => () => void;
  getAppDataPath: () => Promise<string>;
  openCurrentDataFolder: () => Promise<boolean>;
  exportUserData: () => Promise<ExportUserDataResult>;
  importUserData: () => Promise<ImportUserDataResult>;
  eraseUserData: () => Promise<EraseUserDataResult>;
  getSavedThemesPath: () => Promise<string>;
  getLocalPluginsPath: () => Promise<string>;
  listLocalPluginManifests: () => Promise<LocalPluginDiscoveryResult>;
  listSavedThemes: () => Promise<SavedThemeFileDescriptor[]>;
  readSavedTheme: (fileName: string) => Promise<string>;
  writeSavedTheme: (fileName: string, text: string) => Promise<boolean>;
  deleteSavedTheme: (fileName: string) => Promise<boolean>;
  consumePendingDeepLink: () => Promise<string | null>;
  getDesktopSettings: () => Promise<{
    autoUpdateMode: 'auto' | 'off' | 'version';
    autoStart: boolean;
    closeToTray: boolean;
    hardwareAcceleration: boolean;
    localApi: LocalApiSettings;
    manifestUrls: string[];
    preferredVersion: string | null;
    runtimeHardwareAcceleration: boolean;
    restartRequired: boolean;
    vaapiVideoEncode: boolean;
  }>;
  showDesktopNotification: (payload: DesktopNotificationPayload) => Promise<boolean>;
  requestWindowAttention: () => Promise<boolean>;
  clearWindowAttention: () => Promise<boolean>;
  onWindowStateChanged: (listener: (state: WindowStateSnapshot) => void) => () => void;
  getAutoUpdateState: () => Promise<DesktopUpdateState>;
  getAutoUpdateServerHealth: (serverUrl: string) => Promise<DesktopUpdateServerHealthSnapshot>;
  configureAutoUpdateContext: (context: Partial<DesktopUpdateServerContext>) => Promise<DesktopUpdateState>;
  checkForAppUpdates: () => Promise<DesktopUpdateState>;
  restartToApplyUpdate: () => Promise<boolean>;
  onAutoUpdateStateChanged: (listener: (state: DesktopUpdateState) => void) => () => void;
  setDesktopSettings: (patch: {
    autoUpdateMode?: 'auto' | 'off' | 'version';
    autoStart?: boolean;
    closeToTray?: boolean;
    hardwareAcceleration?: boolean;
    localApi?: Partial<LocalApiSettings>;
    manifestUrls?: string[];
    preferredVersion?: string | null;
    vaapiVideoEncode?: boolean;
  }) => Promise<{
    autoUpdateMode: 'auto' | 'off' | 'version';
    autoStart: boolean;
    closeToTray: boolean;
    hardwareAcceleration: boolean;
    localApi: LocalApiSettings;
    manifestUrls: string[];
    preferredVersion: string | null;
    runtimeHardwareAcceleration: boolean;
    restartRequired: boolean;
    vaapiVideoEncode: boolean;
  }>;
  getLocalApiStatus: () => Promise<LocalApiSnapshot>;
  openLocalApiDocs: () => Promise<{ opened: boolean; reason?: string }>;
  openDocusaurusDocs: () => Promise<{ opened: boolean; reason?: string }>;
  relaunchApp: () => Promise<boolean>;
  onDeepLinkReceived: (listener: (url: string) => void) => () => void;
  readClipboardFiles: () => Promise<ClipboardFilePayload[]>;
  readFile: (filePath: string) => Promise<string>;
  writeFile: (filePath: string, data: string) => Promise<boolean>;
  saveFileAs: (defaultFileName: string, data: string) => Promise<{ saved: boolean; cancelled: boolean }>;
  fileExists: (filePath: string) => Promise<boolean>;
  deleteFile: (filePath: string) => Promise<boolean>;
  ensureDir: (dirPath: string) => Promise<boolean>;

  onContextMenu: (listener: (params: ContextMenuParams) => void) => () => void;
  contextMenuCommand: (command: string) => Promise<void>;
  copyImageToClipboard: (srcURL: string) => Promise<boolean>;

  getIdleState: () => Promise<'active' | 'idle'>;
  onIdleStateChanged: (listener: (state: 'active' | 'idle') => void) => () => void;

  command: <T = unknown>(command: Command) => Promise<T>;
  query: <T = unknown>(query: Query) => Promise<T>;
}

const electronAPI: ElectronAPI = {
  linuxDisplayServer: readLinuxDisplayServer(),
  minimizeWindow: () => ipcRenderer.send('window-minimize'),
  maximizeWindow: () => ipcRenderer.send('window-maximize'),
  closeWindow: () => ipcRenderer.send('window-close'),

  openExternal: (url) => ipcRenderer.invoke('open-external', url),
  getSources: () => ipcRenderer.invoke('get-sources'),
  getRunningProcessNames: () => ipcRenderer.invoke('get-running-process-names'),
  prepareLinuxScreenShareAudioRouting: () => ipcRenderer.invoke('prepare-linux-screen-share-audio-routing'),
  activateLinuxScreenShareAudioRouting: () => ipcRenderer.invoke('activate-linux-screen-share-audio-routing'),
  deactivateLinuxScreenShareAudioRouting: () => ipcRenderer.invoke('deactivate-linux-screen-share-audio-routing'),
  startLinuxScreenShareMonitorCapture: () => ipcRenderer.invoke('start-linux-screen-share-monitor-capture'),
  stopLinuxScreenShareMonitorCapture: (captureId) => ipcRenderer.invoke('stop-linux-screen-share-monitor-capture', captureId),
  onLinuxScreenShareMonitorAudioChunk: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, payload: LinuxScreenShareMonitorAudioChunkPayload) => {
      const chunk = payload.chunk instanceof Uint8Array
        ? payload.chunk
        : Uint8Array.from((payload as { chunk?: Iterable<number> }).chunk || []);

      listener({
        ...payload,
        chunk
      });
    };

    ipcRenderer.on(LINUX_SCREEN_SHARE_MONITOR_AUDIO_CHUNK_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(LINUX_SCREEN_SHARE_MONITOR_AUDIO_CHUNK_CHANNEL, wrappedListener);
    };
  },
  onLinuxScreenShareMonitorAudioEnded: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, payload: LinuxScreenShareMonitorAudioEndedPayload) => {
      listener(payload);
    };

    ipcRenderer.on(LINUX_SCREEN_SHARE_MONITOR_AUDIO_ENDED_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(LINUX_SCREEN_SHARE_MONITOR_AUDIO_ENDED_CHANNEL, wrappedListener);
    };
  },
  getAppDataPath: () => ipcRenderer.invoke('get-app-data-path'),
  openCurrentDataFolder: () => ipcRenderer.invoke('open-current-data-folder'),
  exportUserData: () => ipcRenderer.invoke('export-user-data'),
  importUserData: () => ipcRenderer.invoke('import-user-data'),
  eraseUserData: () => ipcRenderer.invoke('erase-user-data'),
  getSavedThemesPath: () => ipcRenderer.invoke('get-saved-themes-path'),
  getLocalPluginsPath: () => ipcRenderer.invoke('get-local-plugins-path'),
  listLocalPluginManifests: () => ipcRenderer.invoke('list-local-plugin-manifests'),
  listSavedThemes: () => ipcRenderer.invoke('list-saved-themes'),
  readSavedTheme: (fileName) => ipcRenderer.invoke('read-saved-theme', fileName),
  writeSavedTheme: (fileName, text) => ipcRenderer.invoke('write-saved-theme', fileName, text),
  deleteSavedTheme: (fileName) => ipcRenderer.invoke('delete-saved-theme', fileName),
  consumePendingDeepLink: () => ipcRenderer.invoke('consume-pending-deep-link'),
  getDesktopSettings: () => ipcRenderer.invoke('get-desktop-settings'),
  showDesktopNotification: (payload) => ipcRenderer.invoke('show-desktop-notification', payload),
  requestWindowAttention: () => ipcRenderer.invoke('request-window-attention'),
  clearWindowAttention: () => ipcRenderer.invoke('clear-window-attention'),
  onWindowStateChanged: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, state: WindowStateSnapshot) => {
      listener(state);
    };

    ipcRenderer.on(WINDOW_STATE_CHANGED_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(WINDOW_STATE_CHANGED_CHANNEL, wrappedListener);
    };
  },
  getAutoUpdateState: () => ipcRenderer.invoke('get-auto-update-state'),
  getAutoUpdateServerHealth: (serverUrl) => ipcRenderer.invoke('get-auto-update-server-health', serverUrl),
  configureAutoUpdateContext: (context) => ipcRenderer.invoke('configure-auto-update-context', context),
  checkForAppUpdates: () => ipcRenderer.invoke('check-for-app-updates'),
  restartToApplyUpdate: () => ipcRenderer.invoke('restart-to-apply-update'),
  onAutoUpdateStateChanged: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, state: DesktopUpdateState) => {
      listener(state);
    };

    ipcRenderer.on(AUTO_UPDATE_STATE_CHANGED_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(AUTO_UPDATE_STATE_CHANGED_CHANNEL, wrappedListener);
    };
  },
  setDesktopSettings: (patch) => ipcRenderer.invoke('set-desktop-settings', patch),
  getLocalApiStatus: () => ipcRenderer.invoke('get-local-api-status'),
  openLocalApiDocs: () => ipcRenderer.invoke('open-local-api-docs'),
  openDocusaurusDocs: () => ipcRenderer.invoke('open-docusaurus-docs'),
  relaunchApp: () => ipcRenderer.invoke('relaunch-app'),
  onDeepLinkReceived: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, url: string) => {
      listener(url);
    };

    ipcRenderer.on(DEEP_LINK_RECEIVED_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(DEEP_LINK_RECEIVED_CHANNEL, wrappedListener);
    };
  },
  readClipboardFiles: () => ipcRenderer.invoke('read-clipboard-files'),
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
  writeFile: (filePath, data) => ipcRenderer.invoke('write-file', filePath, data),
  saveFileAs: (defaultFileName, data) => ipcRenderer.invoke('save-file-as', defaultFileName, data),
  fileExists: (filePath) => ipcRenderer.invoke('file-exists', filePath),
  deleteFile: (filePath) => ipcRenderer.invoke('delete-file', filePath),
  ensureDir: (dirPath) => ipcRenderer.invoke('ensure-dir', dirPath),

  onContextMenu: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, params: ContextMenuParams) => {
      listener(params);
    };

    ipcRenderer.on('show-context-menu', wrappedListener);

    return () => {
      ipcRenderer.removeListener('show-context-menu', wrappedListener);
    };
  },
  contextMenuCommand: (command) => ipcRenderer.invoke('context-menu-command', command),
  copyImageToClipboard: (srcURL) => ipcRenderer.invoke('copy-image-to-clipboard', srcURL),

  getIdleState: () => ipcRenderer.invoke('get-idle-state'),
  onIdleStateChanged: (listener) => {
    const wrappedListener = (_event: Electron.IpcRendererEvent, state: 'active' | 'idle') => {
      listener(state);
    };

    ipcRenderer.on(IDLE_STATE_CHANGED_CHANNEL, wrappedListener);

    return () => {
      ipcRenderer.removeListener(IDLE_STATE_CHANGED_CHANNEL, wrappedListener);
    };
  },

  command: (command) => ipcRenderer.invoke('cqrs:command', command),
  query: (query) => ipcRenderer.invoke('cqrs:query', query)
};

contextBridge.exposeInMainWorld('electronAPI', electronAPI);

declare global {
  interface Window {
    electronAPI: ElectronAPI;
  }
}
