import { app, BrowserWindow, Tray, Menu, dialog, globalShortcut, ipcMain, nativeImage, clipboard } from 'electron';
import { spawn } from 'child_process';
import { readFile, writeFile } from 'fs/promises';
import { existsSync } from 'fs';
import { join, dirname, resolve } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const AGENT_ROOT = resolve(join(__dirname, '..'));
const CONFIG_PATH = join(AGENT_ROOT, 'config.json');
const DEFAULT_HOTKEY = 'CommandOrControl+Shift+A';

let agentProcess = null;
let mainWindow = null;
let tray = null;
let isQuitting = false;
let currentHotkey = '';
let hotkeyPollTimer = null;
const AGENT_NODE_BIN = process.env.AGENT_NODE_BIN || process.env.NODE_EXE || 'node';

function relayChildStream(stream, prefix = '') {
  let buffer = '';
  stream.on('data', (chunk) => {
    buffer += chunk.toString('utf8');
    const lines = buffer.split(/\r?\n/);
    buffer = lines.pop() || '';
    for (const raw of lines) {
      const line = raw.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
      if (!line.trim()) continue;
      console.log(prefix ? `${prefix}${line}` : line);
    }
  });
  stream.on('end', () => {
    const tail = buffer.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '').trim();
    if (tail) console.log(prefix ? `${prefix}${tail}` : tail);
  });
}

function safeConfigDefaults() {
  return {
    port: 4000,
    authToken: null,
    workspaceRoot: join(AGENT_ROOT, 'workspace'),
    desktopHotkey: DEFAULT_HOTKEY
  };
}

async function readConfig() {
  const defaults = safeConfigDefaults();
  if (!existsSync(CONFIG_PATH)) return defaults;
  try {
    const raw = await readFile(CONFIG_PATH, 'utf-8');
    const parsed = JSON.parse(raw);
    return { ...defaults, ...parsed };
  } catch {
    return defaults;
  }
}

async function writeConfig(nextConfig) {
  await writeFile(CONFIG_PATH, JSON.stringify(nextConfig, null, 2) + '\n', 'utf-8');
}

async function uiUrl() {
  const cfg = await readConfig();
  const base = `http://localhost:${cfg.port || 4000}`;
  return cfg.authToken ? `${base}?token=${cfg.authToken}` : base;
}

function startAgent() {
  if (agentProcess && !agentProcess.killed) return;
  agentProcess = spawn(AGENT_NODE_BIN, ['agent.mjs'], {
    cwd: AGENT_ROOT,
    env: { ...process.env },
    stdio: ['ignore', 'pipe', 'pipe'],
    shell: false
  });
  relayChildStream(agentProcess.stdout);
  relayChildStream(agentProcess.stderr, '[agent:err] ');

  agentProcess.on('exit', (code, signal) => {
    agentProcess = null;
    if (isQuitting) return;
    console.log(`[desktop] Agent exited (${code ?? 'null'}/${signal ?? 'null'}), restarting in 2s`);
    setTimeout(startAgent, 2000);
  });

  agentProcess.on('error', (err) => {
    console.error(`[desktop] Failed to launch agent with "${AGENT_NODE_BIN}": ${err.message}`);
  });
}

async function waitForServerReady(timeoutMs = 30000) {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    try {
      const url = await uiUrl();
      const res = await fetch(url, { method: 'GET' });
      if (res.ok) return;
    } catch {}
    await new Promise(r => setTimeout(r, 500));
  }
}

function showUI() {
  if (!mainWindow) return;
  if (mainWindow.isMinimized()) mainWindow.restore();
  if (!mainWindow.isVisible()) mainWindow.show();
  mainWindow.focus();
}

async function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1360,
    height: 900,
    minWidth: 980,
    minHeight: 680,
    show: false,
    webPreferences: {
      preload: join(__dirname, 'preload.cjs'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.on('close', (event) => {
    if (isQuitting) return;
    event.preventDefault();
    mainWindow.hide();
  });

  mainWindow.on('ready-to-show', () => {
    if (mainWindow && !mainWindow.isVisible()) mainWindow.show();
  });

  await waitForServerReady();
  await mainWindow.loadURL(await uiUrl());
}

async function updateConfigViaApiOrFile(updates) {
  const cfg = await readConfig();
  const port = cfg.port || 4000;
  const token = cfg.authToken;

  try {
    const headers = { 'Content-Type': 'application/json' };
    if (token) headers.Authorization = `Bearer ${token}`;
    const res = await fetch(`http://localhost:${port}/api/config`, {
      method: 'PUT',
      headers,
      body: JSON.stringify(updates)
    });
    if (res.ok) return true;
  } catch {}

  await writeConfig({ ...cfg, ...updates });
  return false;
}

async function callAgentApi(path, method = 'GET', body = null) {
  const cfg = await readConfig();
  const port = cfg.port || 4000;
  const token = cfg.authToken;
  const headers = {};
  if (token) headers.Authorization = `Bearer ${token}`;
  if (body) headers['Content-Type'] = 'application/json';

  const res = await fetch(`http://localhost:${port}${path}`, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined
  });
  if (!res.ok) throw new Error(`API ${method} ${path} failed with ${res.status}`);
  return res.json();
}

async function buildWorkspaceSubmenu() {
  try {
    const workspaces = await callAgentApi('/api/workspaces');
    return workspaces.map(ws => ({
      label: `${ws.name || ws.id}${ws.is_active ? ' ✓' : ''}`,
      click: async () => {
        try {
          await callAgentApi(`/api/workspaces/${ws.id}/activate`, 'PUT');
          console.log(`[desktop] Switched to workspace: ${ws.id}`);
        } catch (err) {
          console.error(`[desktop] Workspace switch failed: ${err.message}`);
        }
      }
    }));
  } catch {
    return [{ label: '(loading...)', enabled: false }];
  }
}

async function pickWorkspaceFolder() {
  const result = await dialog.showOpenDialog({
    title: 'Select Workspace Folder',
    properties: ['openDirectory', 'createDirectory']
  });
  if (result.canceled || !result.filePaths.length) return null;
  const folder = resolve(result.filePaths[0]);
  await updateConfigViaApiOrFile({ workspaceRoot: folder });
  return folder;
}

async function createTray() {
  if (tray) return;
  let trayIcon;
  try {
    trayIcon = process.platform === 'win32'
      ? nativeImage.createFromPath(process.execPath)
      : nativeImage.createEmpty();
  } catch {
    trayIcon = nativeImage.createEmpty();
  }

  tray = new Tray(trayIcon);
  tray.setToolTip('AI-Do');
  tray.on('double-click', showUI);

  const template = [
    { label: 'Show UI', click: () => showUI() },
    {
      label: 'Hide UI',
      click: () => {
        if (mainWindow) mainWindow.hide();
      }
    },
    { type: 'separator' },
    {
      label: 'Select Workspace Folder...',
      click: async () => {
        const folder = await pickWorkspaceFolder();
        if (folder && mainWindow && mainWindow.webContents) {
          mainWindow.webContents.send('desktop:workspace-updated', folder);
        }
      }
    },
    { type: 'separator' },
    {
      label: 'Pause Background Loop',
      click: async () => {
        try {
          const result = await callAgentApi('/api/loop', 'POST', { enabled: false });
          console.log(`[desktop] Background loop paused: ${result.enabled === false ? 'ok' : 'unexpected state'}`);
        } catch (err) {
          console.error(`[desktop] Could not pause loop: ${err.message}`);
        }
      }
    },
    {
      label: 'Resume Background Loop',
      click: async () => {
        try {
          const result = await callAgentApi('/api/loop', 'POST', { enabled: true });
          console.log(`[desktop] Background loop resumed: ${result.enabled === true ? 'ok' : 'unexpected state'}`);
        } catch (err) {
          console.error(`[desktop] Could not resume loop: ${err.message}`);
        }
      }
    },
    { type: 'separator' },
    {
      label: 'Switch Workspace',
      type: 'submenu',
      submenu: await buildWorkspaceSubmenu()
    },
    {
      label: 'Ingest Clipboard',
      click: async () => {
        try {
          const text = clipboard.readText();
          if (!text || !text.trim()) {
            console.log('[desktop] Clipboard is empty');
            return;
          }
          await callAgentApi('/api/ingest', 'POST', {
            sourceType: 'text',
            sourcePath: text.slice(0, 10000)
          });
          console.log('[desktop] Clipboard content sent to ingestion queue');
        } catch (err) {
          console.error(`[desktop] Clipboard ingest failed: ${err.message}`);
        }
      }
    },
    { type: 'separator' },
    {
      label: 'Voice Activation',
      type: 'submenu',
      submenu: [
        {
          label: 'Enable',
          click: async () => {
            try {
              await callAgentApi('/api/voice/toggle', 'POST', { enabled: true });
              console.log('[desktop] Voice activation enabled');
            } catch (err) {
              console.error(`[desktop] Voice toggle failed: ${err.message}`);
            }
          }
        },
        {
          label: 'Disable',
          click: async () => {
            try {
              await callAgentApi('/api/voice/toggle', 'POST', { enabled: false });
              console.log('[desktop] Voice activation disabled');
            } catch (err) {
              console.error(`[desktop] Voice toggle failed: ${err.message}`);
            }
          }
        },
        {
          label: 'Listen Now',
          click: async () => {
            try {
              await callAgentApi('/api/voice/listen', 'POST');
              console.log('[desktop] Voice listen triggered');
            } catch (err) {
              console.error(`[desktop] Voice listen failed: ${err.message}`);
            }
          }
        }
      ]
    },
    { type: 'separator' },
    {
      label: 'Persona',
      type: 'submenu',
      submenu: [
        {
          label: 'Edit Persona',
          click: () => {
            showUI();
            if (mainWindow && mainWindow.webContents) {
              mainWindow.webContents.send('desktop:open-persona-editor');
            }
          }
        }
      ]
    },
    {
      label: 'Theme',
      type: 'submenu',
      submenu: [
        {
          label: 'Dark',
          click: async () => {
            try {
              await updateConfigViaApiOrFile({ theme: 'dark' });
              if (mainWindow && mainWindow.webContents) {
                mainWindow.webContents.send('desktop:theme-change', 'dark');
              }
              console.log('[desktop] Theme set to dark');
            } catch (err) {
              console.error(`[desktop] Theme change failed: ${err.message}`);
            }
          }
        },
        {
          label: 'Light',
          click: async () => {
            try {
              await updateConfigViaApiOrFile({ theme: 'light' });
              if (mainWindow && mainWindow.webContents) {
                mainWindow.webContents.send('desktop:theme-change', 'light');
              }
              console.log('[desktop] Theme set to light');
            } catch (err) {
              console.error(`[desktop] Theme change failed: ${err.message}`);
            }
          }
        }
      ]
    },
    {
      label: 'Focus Mode',
      click: () => {
        if (mainWindow && mainWindow.webContents) {
          mainWindow.webContents.send('desktop:focus-mode-toggled');
        }
        console.log('[desktop] Focus mode toggled');
      }
    },
    { type: 'separator' },
    {
      label: 'Quit',
      click: () => {
        isQuitting = true;
        app.quit();
      }
    }
  ];
  tray.setContextMenu(Menu.buildFromTemplate(template));
}

function normalizeHotkey(candidate) {
  const value = String(candidate || '').trim();
  return value || DEFAULT_HOTKEY;
}

function registerShowHotkey(nextHotkey) {
  const normalized = normalizeHotkey(nextHotkey);
  if (normalized === currentHotkey) return true;

  if (currentHotkey) {
    try { globalShortcut.unregister(currentHotkey); } catch {}
    currentHotkey = '';
  }

  const ok = globalShortcut.register(normalized, () => showUI());
  if (ok) {
    currentHotkey = normalized;
    return true;
  }

  if (normalized !== DEFAULT_HOTKEY) {
    const fallbackOk = globalShortcut.register(DEFAULT_HOTKEY, () => showUI());
    if (fallbackOk) {
      currentHotkey = DEFAULT_HOTKEY;
      return true;
    }
  }
  return false;
}

async function refreshHotkeyFromConfig() {
  const cfg = await readConfig();
  registerShowHotkey(cfg.desktopHotkey);
}

ipcMain.handle('desktop:show-ui', async () => {
  showUI();
  return true;
});

ipcMain.handle('desktop:pick-workspace-folder', async () => {
  const folder = await pickWorkspaceFolder();
  if (folder && mainWindow && mainWindow.webContents) {
    mainWindow.webContents.send('desktop:workspace-updated', folder);
  }
  return folder;
});

// v3.1 — Voice activation IPC handlers
ipcMain.handle('desktop:voice-toggle', async (_, enabled) => {
  try {
    return await callAgentApi('/api/voice/toggle', 'POST', { enabled });
  } catch (err) {
    console.error(`[desktop] Voice toggle IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

ipcMain.handle('desktop:voice-listen', async () => {
  try {
    showUI();   // Bring window to front so user sees the voice interaction
    return await callAgentApi('/api/voice/listen', 'POST');
  } catch (err) {
    console.error(`[desktop] Voice listen IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

// v4.0 — Persona IPC handlers
ipcMain.handle('desktop:get-persona', async () => {
  try {
    return await callAgentApi('/api/persona');
  } catch (err) {
    console.error(`[desktop] Get persona IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

ipcMain.handle('desktop:update-persona', async (_, data) => {
  try {
    return await callAgentApi('/api/persona', 'PUT', data);
  } catch (err) {
    console.error(`[desktop] Update persona IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

// v4.0 — Theme toggle IPC handler
ipcMain.handle('desktop:toggle-theme', async () => {
  try {
    const cfg = await readConfig();
    const newTheme = cfg.theme === 'dark' ? 'light' : 'dark';
    await writeConfig({ ...cfg, theme: newTheme });
    if (mainWindow && mainWindow.webContents) {
      mainWindow.webContents.send('desktop:theme-change', newTheme);
    }
    return newTheme;
  } catch (err) {
    console.error(`[desktop] Toggle theme IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

// v4.0 — Focus mode IPC handler
ipcMain.handle('desktop:toggle-focus-mode', async () => {
  try {
    if (mainWindow && mainWindow.webContents) {
      mainWindow.webContents.send('desktop:focus-mode-toggled');
    }
    return { ok: true };
  } catch (err) {
    console.error(`[desktop] Toggle focus mode IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

// v4.0 — Run skill IPC handler
ipcMain.handle('desktop:run-skill', async (_, skillName) => {
  try {
    return await callAgentApi('/api/chat', 'POST', { message: `Run the ${skillName} skill` });
  } catch (err) {
    console.error(`[desktop] Run skill IPC failed: ${err.message}`);
    return { error: err.message };
  }
});

const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
  app.quit();
} else {
  app.on('second-instance', () => {
    showUI();
  });
}

app.whenReady().then(async () => {
  startAgent();
  await createWindow();
  await createTray();
  await refreshHotkeyFromConfig();
  hotkeyPollTimer = setInterval(refreshHotkeyFromConfig, 4000);

  try {
    globalShortcut.register('CommandOrControl+Shift+V', async () => {
      try {
        const text = clipboard.readText();
        if (text?.trim()) {
          await callAgentApi('/api/ingest', 'POST', { sourceType: 'text', sourcePath: text.slice(0, 10000) });
          console.log('[desktop] Clipboard ingested via hotkey');
        }
      } catch (err) {
        console.error(`[desktop] Hotkey clipboard ingest failed: ${err.message}`);
      }
    });
  } catch (err) {
    console.error(`[desktop] Failed to register clipboard hotkey: ${err.message}`);
  }

  // v3.1 — Voice listen hotkey
  try {
    const cfg = await readConfig();
    const voiceHotkey = cfg.voice?.listenHotkey || 'CommandOrControl+Shift+Space';
    globalShortcut.register(voiceHotkey, async () => {
      try {
        showUI();   // Bring window to front so user sees the voice interaction
        await callAgentApi('/api/voice/listen', 'POST');
        console.log('[desktop] Voice listen triggered via hotkey');
      } catch (err) {
        console.error(`[desktop] Voice hotkey failed: ${err.message}`);
      }
    });
  } catch (err) {
    console.error(`[desktop] Failed to register voice hotkey: ${err.message}`);
  }

  // v4.0 — Focus mode hotkey
  try {
    globalShortcut.register('CommandOrControl+Shift+F', () => {
      if (mainWindow && mainWindow.webContents) {
        mainWindow.webContents.send('desktop:focus-mode-toggled');
      }
      console.log('[desktop] Focus mode toggled via hotkey');
    });
  } catch (err) {
    console.error(`[desktop] Failed to register focus mode hotkey: ${err.message}`);
  }

  app.on('activate', () => {
    showUI();
  });
});

app.on('window-all-closed', (event) => {
  // Keep running in tray on all platforms.
  event.preventDefault();
});

app.on('before-quit', () => {
  isQuitting = true;
  if (hotkeyPollTimer) clearInterval(hotkeyPollTimer);
  hotkeyPollTimer = null;
  try { globalShortcut.unregisterAll(); } catch {}
  if (agentProcess && !agentProcess.killed) {
    try { agentProcess.kill('SIGINT'); } catch {}
  }
});
