/**
 * ContextMenu — 右键菜单组件
 *
 * 支持 5 种对象的右键菜单：消息、工具调用卡片、会话项、代码块、Trace 步骤。
 *
 * @see docs/MetonaAI-Desktop UI UX 设计集成方案.html — 右键菜单设计
 */

import {
  Menu, MenuItem, ListItemIcon, ListItemText,
  Dialog, DialogTitle, DialogContent, DialogActions,
  Button, TextField, Typography,
} from '@mui/material';
import { Copy, Quote, Edit, Trash2, RotateCcw, Eye, Code, ExternalLink, Pin, Archive, FileDown } from 'lucide-react';
import { create } from 'zustand';
import { useAgentStore } from '@renderer/stores/agent-store';
import { useSessionStore } from '@renderer/stores/session-store';

export type ContextMenuType = 'message' | 'tool-call' | 'session' | 'code-block' | 'trace-step';

interface ContextMenuItem {
  id: string;
  icon: typeof Copy;
  label: string;
  action: () => void;
  disabled?: boolean;
}

interface ContextMenuProps {
  x: number;
  y: number;
  onClose: () => void;
  items: ContextMenuItem[];
}

export function ContextMenu({ x, y, onClose, items }: ContextMenuProps): React.JSX.Element {
  return (
    <Menu open onClose={onClose} anchorReference="anchorPosition" anchorPosition={{ top: y, left: x }} slotProps={{ paper: { sx: { minWidth: 160 } } }}>
      {items.map((item) => {
        const Icon = item.icon;
        return (
          <MenuItem key={item.id} onClick={() => { if (!item.disabled) { item.action(); onClose(); } }} disabled={item.disabled} dense>
            <ListItemIcon sx={{ minWidth: 28 }}><Icon size={12} /></ListItemIcon>
            <ListItemText slotProps={{ primary: { sx: { fontSize: 12 } } }}>{item.label}</ListItemText>
          </MenuItem>
        );
      })}
    </Menu>
  );
}

/**
 * 通用 Dialog Store — 模块级状态，供 createContextMenuItems 等工厂函数调用
 *
 * 替代原生 confirm()/prompt()（Electron 下不可靠，且与 MUI 风格不一致）。
 * 由 ContextMenuDialogHost 组件渲染实际的 MUI Dialog。
 */
interface DialogState {
  // confirm dialog
  confirmOpen: boolean;
  confirmTitle: string;
  confirmMessage: string;
  confirmResolve: ((ok: boolean) => void) | null;
  // prompt dialog
  promptOpen: boolean;
  promptTitle: string;
  promptLabel: string;
  promptValue: string;
  promptResolve: ((value: string | null) => void) | null;
  // actions
  confirm: (message: string, title?: string) => Promise<boolean>;
  prompt: (label: string, defaultValue?: string, title?: string) => Promise<string | null>;
  setPromptValue: (v: string) => void;
  closeConfirm: (ok: boolean) => void;
  closePrompt: (value: string | null) => void;
}

const useDialogStore = create<DialogState>((set, get) => ({
  confirmOpen: false,
  confirmTitle: '',
  confirmMessage: '',
  confirmResolve: null,
  promptOpen: false,
  promptTitle: '',
  promptLabel: '',
  promptValue: '',
  promptResolve: null,
  confirm: (message, title = '确认') => new Promise<boolean>((resolve) => {
    set({ confirmOpen: true, confirmTitle: title, confirmMessage: message, confirmResolve: resolve });
  }),
  prompt: (label, defaultValue = '', title = '输入') => new Promise<string | null>((resolve) => {
    set({ promptOpen: true, promptTitle: title, promptLabel: label, promptValue: defaultValue, promptResolve: resolve });
  }),
  setPromptValue: (v) => set({ promptValue: v }),
  closeConfirm: (ok) => {
    const { confirmResolve } = get();
    confirmResolve?.(ok);
    set({ confirmOpen: false, confirmResolve: null, confirmTitle: '', confirmMessage: '' });
  },
  closePrompt: (value) => {
    const { promptResolve } = get();
    promptResolve?.(value);
    set({ promptOpen: false, promptResolve: null, promptTitle: '', promptLabel: '', promptValue: '' });
  },
}));

/**
 * ContextMenuDialogHost — 渲染通用 confirm/prompt Dialog
 *
 * 在 App 根组件挂载一次即可。由 useDialogStore 驱动显示。
 */
export function ContextMenuDialogHost(): React.JSX.Element {
  const confirmOpen = useDialogStore((s) => s.confirmOpen);
  const confirmTitle = useDialogStore((s) => s.confirmTitle);
  const confirmMessage = useDialogStore((s) => s.confirmMessage);
  const closeConfirm = useDialogStore((s) => s.closeConfirm);

  const promptOpen = useDialogStore((s) => s.promptOpen);
  const promptTitle = useDialogStore((s) => s.promptTitle);
  const promptLabel = useDialogStore((s) => s.promptLabel);
  const promptValue = useDialogStore((s) => s.promptValue);
  const setPromptValue = useDialogStore((s) => s.setPromptValue);
  const closePrompt = useDialogStore((s) => s.closePrompt);

  return (
    <>
      <Dialog open={confirmOpen} onClose={() => closeConfirm(false)} maxWidth="xs" fullWidth>
        <DialogTitle sx={{ fontSize: 14 }}>{confirmTitle}</DialogTitle>
        <DialogContent>
          <Typography variant="body2">{confirmMessage}</Typography>
        </DialogContent>
        <DialogActions>
          <Button onClick={() => closeConfirm(false)} color="inherit" size="small">取消</Button>
          <Button onClick={() => closeConfirm(true)} variant="contained" color="error" size="small" autoFocus>确定</Button>
        </DialogActions>
      </Dialog>
      <Dialog open={promptOpen} onClose={() => closePrompt(null)} maxWidth="xs" fullWidth>
        <DialogTitle sx={{ fontSize: 14 }}>{promptTitle}</DialogTitle>
        <DialogContent>
          <TextField
            label={promptLabel}
            value={promptValue}
            onChange={(e) => setPromptValue(e.target.value)}
            autoFocus
            fullWidth
            size="small"
            onKeyDown={(e) => { if (e.key === 'Enter') closePrompt(promptValue); }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={() => closePrompt(null)} color="inherit" size="small">取消</Button>
          <Button onClick={() => closePrompt(promptValue)} variant="contained" size="small">确定</Button>
        </DialogActions>
      </Dialog>
    </>
  );
}

/**
 * copyWithToast — 复制到剪贴板，失败时弹 toast
 *
 * 替代 navigator.clipboard.writeText(...).catch(err => console.error(...)) 模式。
 * 复制是用户主动操作，失败时必须有反馈。
 */
function copyWithToast(text: string): void {
  navigator.clipboard.writeText(text).catch((err) => {
    console.error('[Clipboard]', err);
    import('@metona-team/metona-toast').then((mod) => mod.default.error('复制失败')).catch(() => {});
  });
}

/**
 * 创建右键菜单项的工厂函数
 */
export function createContextMenuItems(type: ContextMenuType, data?: unknown): ContextMenuItem[] {
  switch (type) {
    case 'message': {
      const content = (data as { content?: string })?.content ?? '';
      return [
        { id: 'copy', icon: Copy, label: '复制', action: () => copyWithToast(content) },
        { id: 'quote', icon: Quote, label: '引用回复', action: () => {
          const input = document.querySelector<HTMLTextAreaElement>('[data-chat-input]');
          if (input) { input.value = content.split('\n').map((l: string) => `> ${l}`).join('\n') + '\n\n'; input.dispatchEvent(new Event('input', { bubbles: true })); input.focus(); }
        }},
      ];
    }

    case 'tool-call': {
      const tc = data as { args?: Record<string, unknown>; result?: unknown } | undefined;
      return [
        { id: 'view-params', icon: Eye, label: '查看参数', action: () => { if (tc?.args) copyWithToast(JSON.stringify(tc.args, null, 2)); }},
        { id: 'view-result', icon: Eye, label: '查看完整结果', action: () => { if (tc?.result) copyWithToast(JSON.stringify(tc.result, null, 2)); }},
        { id: 'copy-result', icon: Copy, label: '复制结果', action: () => { if (tc?.result) copyWithToast(typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result)); }},
        { id: 're-execute', icon: RotateCcw, label: '重新执行', action: () => {
          const m = [...useAgentStore.getState().messages].reverse().find((m) => m.role === 'user');
          if (m) useAgentStore.getState().sendMessage(m.content);
        }},
      ];
    }

    case 'session': {
      const sid = (data as { sessionId?: string })?.sessionId;
      // 修复: 4 个 session 操作统一改为 await IPC + 失败回滚，避免 UI 与 DB 状态不一致
      // 之前 archive 仅调用前端 store 未调用 IPC，rename/pin/delete 未等待 IPC 结果即更新 UI
      const showError = (msg: string) => {
        import('@metona-team/metona-toast').then((mod) => mod.default.error(msg)).catch(() => {});
      };
      return [
        { id: 'rename', icon: Edit, label: '重命名', action: async () => {
          if (!sid) return;
          // 用 MUI Dialog 替代原生 prompt()（Electron 下不可靠，且与 MUI 风格不一致）
          const currentTitle = useSessionStore.getState().sessions.find((x) => x.id === sid)?.title ?? '';
          const t = await useDialogStore.getState().prompt('新会话名称', currentTitle, '重命名会话');
          if (!t?.trim()) return;
          try {
            const r = await window.metona?.sessions?.rename(sid, t.trim());
            if (r?.success) {
              useSessionStore.getState().updateSession(sid, { title: t.trim() });
            } else {
              showError(r?.error ?? '重命名失败');
            }
          } catch (err) {
            console.error('[ContextMenu]', err);
            showError('重命名失败');
          }
        }},
        { id: 'pin', icon: Pin, label: '置顶', action: async () => {
          if (!sid) return;
          const s = useSessionStore.getState().sessions.find((x) => x.id === sid);
          if (!s) return;
          const newPinned = !s.pinned;
          try {
            const r = await window.metona?.sessions?.pin(sid, newPinned);
            if (r?.success) {
              useSessionStore.getState().pinSession(sid, newPinned);
            } else {
              showError(r?.error ?? '置顶失败');
            }
          } catch (err) {
            console.error('[ContextMenu]', err);
            showError('置顶失败');
          }
        }},
        // 修复: archive 之前完全未调用 IPC，UI 改了但 DB 没改，重启后状态丢失
        { id: 'archive', icon: Archive, label: '归档', action: async () => {
          if (!sid) return;
          const s = useSessionStore.getState().sessions.find((x) => x.id === sid);
          const newArchived = s ? !s.archived : true;
          try {
            const r = await window.metona?.sessions?.archive(sid, newArchived);
            if (r?.success) {
              useSessionStore.getState().archiveSession(sid, newArchived);
            } else {
              showError(r?.error ?? '归档失败');
            }
          } catch (err) {
            console.error('[ContextMenu]', err);
            showError('归档失败');
          }
        }},
        { id: 'export', icon: FileDown, label: '导出', action: () => {
          if (sid) window.metona?.sessions.getMessages(sid).then((msgs) => {
            const b = new Blob([JSON.stringify(msgs, null, 2)], { type: 'application/json' });
            const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = `session-${sid}.json`; a.click();
          }).catch((err) => {
            console.error('[ContextMenu]', err);
            showError('导出失败');
          });
        }},
        { id: 'delete', icon: Trash2, label: '删除', action: async () => {
          if (!sid) return;
          // 用 MUI Dialog 替代原生 confirm()（Electron 下不可靠）
          // 与 Sidebar.tsx 的 showDeleteDialog 行为一致
          const ok = await useDialogStore.getState().confirm('确定删除此会话？此操作不可撤销。', '删除会话');
          if (!ok) return;
          try {
            const r = await window.metona?.sessions?.delete(sid);
            if (r?.success) {
              useSessionStore.getState().removeSession(sid);
              // 修复: 删除当前会话时同步清空 agent-store，否则 ChatPanel 和 DetailPanel 仍显示已删除会话的内容
              // 与 Sidebar.tsx 的 confirmDelete 行为一致
              if (useAgentStore.getState().currentSessionId === sid) {
                useAgentStore.setState({
                  currentSessionId: null,
                  messages: [],
                  traceSteps: [],
                  tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 },
                  currentRunId: null,
                  currentIteration: 0,
                });
              }
            } else {
              showError(r?.error ?? '删除失败');
            }
          } catch (err) {
            console.error('[ContextMenu]', err);
            showError('删除失败');
          }
        }},
      ];
    }

    case 'code-block': {
      const code = (data as { code?: string })?.code;
      return [
        { id: 'copy-code', icon: Copy, label: '复制代码', action: () => { if (code) copyWithToast(code); }},
        { id: 'open-editor', icon: Code, label: '在编辑器中打开', action: () => { if (code) window.open(URL.createObjectURL(new Blob([code], { type: 'text/plain' })), '_blank'); }},
      ];
    }

    case 'trace-step': {
      const step = data as { thought?: string; toolCalls?: Array<{ name: string; args: Record<string, unknown> }> } | undefined;
      return [
        { id: 'copy-thought', icon: Copy, label: '复制 Thought', action: () => { if (step?.thought) copyWithToast(step.thought); }},
        { id: 'copy-params', icon: Copy, label: '复制工具参数', action: () => {
          if (step?.toolCalls) copyWithToast(step.toolCalls.map((tc) => `${tc.name}: ${JSON.stringify(tc.args, null, 2)}`).join('\n'));
        }},
        { id: 'export', icon: ExternalLink, label: '导出步骤详情', action: () => {
          if (step) { const b = new Blob([JSON.stringify(step, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = `trace-step-${Date.now()}.json`; a.click(); }
        }},
      ];
    }

    default: return [];
  }
}
