/**
 * Sidebar — 左侧边栏
 *
 * 始终显示，包含会话列表、新建会话按钮、搜索。
 */

import { useState, useEffect, useMemo } from 'react';
import { Box, Typography, Button, IconButton, TextField, Stack, Collapse, List, ListItemButton, ListItemIcon, ListItemText, Badge, Divider, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import Fuse from 'fuse.js';
import { Plus, Search, MessageSquare, Pin, Wrench, ChevronDown, ChevronRight, Trash2 } from 'lucide-react';
import { useSessionStore, type Session } from '@renderer/stores/session-store';
import { useAgentStore } from '@renderer/stores/agent-store';
import { formatRelativeTime } from '@renderer/lib/formatters';
import { LAYOUT } from '@renderer/lib/constants';

export function Sidebar(): React.JSX.Element {
  const sessions = useSessionStore((s) => s.sessions);
  const currentSessionId = useSessionStore((s) => s.currentSessionId);
  const setCurrentSession = useSessionStore((s) => s.setCurrentSession);
  const loadSessionMessages = useAgentStore((s) => s.setCurrentSession);
  const searchQuery = useSessionStore((s) => s.searchQuery);
  const setSearchQuery = useSessionStore((s) => s.setSearchQuery);
  const agentStatus = useAgentStore((s) => s.agentStatus);

  useEffect(() => {
    // M-27 修复: 添加 cancelled 标志防止组件卸载后 setState
    let cancelled = false;
    if (window.metona?.sessions?.list) {
      window.metona.sessions.list().then((list) => {
        if (cancelled) return;
        useSessionStore.getState().setSessions((list as Array<{ id: string; title: string; createdAt: number; updatedAt: number; messageCount: number; pinned: boolean; archived: boolean }>).map((s) => ({ id: s.id, title: s.title, createdAt: s.createdAt, updatedAt: s.updatedAt, messageCount: s.messageCount, pinned: s.pinned, archived: s.archived })));
      }).catch((err) => {
        // M-11 修复: 记录错误而非静默吞掉，便于诊断
        console.error('[Sidebar] Failed to load sessions:', err);
      });
    }
    return () => { cancelled = true; };
  }, []);

  // L-13 修复: 使用 useMemo 缓存 filteredSessions，避免每次渲染都重建 Fuse 索引
  const filteredSessions = useMemo(() => {
    let list = sessions.filter((s) => !s.archived);
    if (searchQuery) { const fuse = new Fuse(list, { keys: ['title'], threshold: 0.4, ignoreLocation: true }); list = fuse.search(searchQuery).map((r) => r.item); }
    return list.sort((a, b) => a.pinned === b.pinned ? b.updatedAt - a.updatedAt : a.pinned ? -1 : 1);
  }, [sessions, searchQuery]);

  const handleNewSession = async () => {
    if (window.metona?.sessions?.create) {
      try {
        const r = await window.metona.sessions.create() as { id: string; title: string; createdAt: number; updatedAt: number; messageCount: number; pinned: boolean; archived: boolean };
        useSessionStore.getState().addSession({ id: r.id, title: r.title, createdAt: r.createdAt, updatedAt: r.updatedAt, messageCount: r.messageCount, pinned: r.pinned, archived: r.archived });
        setCurrentSession(r.id);
        loadSessionMessages(r.id);
        return;
      } catch (err) {
        // M-9 修复: 显示错误提示而非静默吞错后创建本地假会话
        // 之前的行为：catch 后继续创建本地 s_${Date.now()} 会话，但该会话在主进程不存在，下次刷新消失
        console.error('[Sidebar] Failed to create session:', err);
        import('@metona-team/metona-toast').then((mod) => {
          mod.default.error('创建会话失败，请检查数据库状态');
        }).catch(() => {});
        return;  // 不创建本地假会话
      }
    }
    const newSession: Session = { id: `s_${Date.now()}`, title: '新会话', createdAt: Date.now(), updatedAt: Date.now(), messageCount: 0, pinned: false, archived: false };
    useSessionStore.getState().addSession(newSession);
    setCurrentSession(newSession.id);
    loadSessionMessages(newSession.id);
  };

  return (
    <Box component="aside" sx={{ flexShrink: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', bgcolor: 'background.paper', borderRight: 1, borderColor: 'divider', width: LAYOUT.SIDEBAR_WIDTH }}>
      <Box sx={{ p: 1.5, flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <Button variant="outlined" fullWidth size="small" onClick={handleNewSession} sx={{ mb: 1.5, fontSize: 12 }}>
          <Plus size={14} style={{ marginRight: 8 }} /> 新建会话
        </Button>

        <TextField
          size="small"
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
          placeholder="搜索会话..."
          slotProps={{
            input: {
              startAdornment: <Search size={14} style={{ color: '#8b8fa7', marginRight: 8 }} />,
            },
          }}
          sx={{ mb: 1.5, '& .MuiOutlinedInput-root': { fontSize: 12, borderRadius: 1.5, height: 34 } }}
        />

        <Box sx={{ flex: 1, overflowY: 'auto' }}>
          {filteredSessions.length === 0 ? (
            <Typography variant="caption" sx={{ textAlign: 'center', py: 4, display: 'block', color: 'text.secondary' }}>{searchQuery ? '无匹配结果' : '暂无会话'}</Typography>
          ) : filteredSessions.map((session) => (
            <SessionItem key={session.id} session={session} isActive={session.id === currentSessionId} isAgentActive={session.id === currentSessionId && (agentStatus === 'thinking' || agentStatus === 'executing')} onClick={() => { setCurrentSession(session.id); loadSessionMessages(session.id); }} />
          ))}
        </Box>

        <Divider sx={{ mt: 'auto', mb: 1 }} />
        <ToolManagerPanel />
      </Box>
    </Box>
  );
}

function SessionItem({ session, isActive, isAgentActive, onClick }: { session: Session; isActive: boolean; isAgentActive: boolean; onClick: () => void }) {
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);

  const handleDelete = (e: React.MouseEvent) => {
    e.stopPropagation();
    setShowDeleteDialog(true);
  };

  const confirmDelete = async () => {
    // M-10 修复: 乐观更新失败时回滚，避免 UI 与数据库状态不一致
    // 之前行为：catch 静默吞错，但下一行已 removeSession，导致用户以为已删除实际未删
    if (window.metona?.sessions?.delete) {
      try {
        await window.metona.sessions.delete(session.id);
      } catch (err) {
        console.error('[Sidebar] Failed to delete session:', err);
        import('@metona-team/metona-toast').then((mod) => {
          mod.default.error('删除会话失败，请重试');
        }).catch(() => {});
        // 不调用 removeSession，保留会话在 UI 中（与数据库状态一致）
        setShowDeleteDialog(false);
        return;
      }
    }
    useSessionStore.getState().removeSession(session.id);
    // 修复: 删除当前会话时同步清空 agent-store，否则 ChatPanel 和 DetailPanel 仍显示已删除会话的内容
    // 与 SettingsModal LogsSettings 的 clearSessions 分支一致
    if (useAgentStore.getState().currentSessionId === session.id) {
      useAgentStore.setState({
        currentSessionId: null,
        messages: [],
        traceSteps: [],
        tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 },
        currentRunId: null,
        currentIteration: 0,
      });
    }
    setShowDeleteDialog(false);
  };

  return (
    <>
      <ListItemButton onClick={onClick} selected={isActive} dense sx={{ borderRadius: 1.5, mb: 0.25, px: 1.5, py: 0.75, borderRight: isActive ? '2px solid' : '2px solid transparent', borderColor: isActive ? 'primary.main' : 'transparent', '&:hover .delete-btn': { opacity: 1 } }}>
        <ListItemIcon sx={{ minWidth: 24 }}>
          {session.pinned ? <Pin size={10} style={{ color: '#818cf8' }} /> : isAgentActive ? <Badge color="success" variant="dot" sx={{ '& .MuiBadge-dot': { animation: 'pulse 2s infinite', width: 8, height: 8 } }}><MessageSquare size={12} style={{ color: '#8b8fa7' }} /></Badge> : <MessageSquare size={12} style={{ color: '#8b8fa7' }} />}
        </ListItemIcon>
        <ListItemText primary={<Typography variant="body2" sx={{ fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: isActive ? 'text.primary' : 'text.secondary' }}>{session.title}</Typography>}
          secondary={<Typography variant="caption" sx={{ fontSize: 10 }}>{formatRelativeTime(session.updatedAt)}{session.messageCount > 0 ? ` · ${session.messageCount} 条` : ''}</Typography>}
        />
        <IconButton
          className="delete-btn"
          size="small"
          onClick={handleDelete}
          sx={{ opacity: 0, transition: 'opacity 150ms', color: 'text.disabled', '&:hover': { color: 'error.main' }, width: 20, height: 20 }}
        >
          <Trash2 size={12} />
        </IconButton>
      </ListItemButton>

      <Dialog open={showDeleteDialog} onClose={() => setShowDeleteDialog(false)} maxWidth="xs" fullWidth>
        <DialogTitle sx={{ fontSize: 14 }}>删除会话</DialogTitle>
        <DialogContent>
          <Typography variant="body2" sx={{ color: 'text.secondary' }}>
            确定删除会话「{session.title}」？此操作不可恢复。
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button size="small" onClick={() => setShowDeleteDialog(false)} sx={{ color: 'text.secondary' }}>取消</Button>
          <Button size="small" color="error" variant="contained" onClick={confirmDelete}>删除</Button>
        </DialogActions>
      </Dialog>
    </>
  );
}

function ToolManagerPanel() {
  const [expanded, setExpanded] = useState(false);
  const [tools, setTools] = useState<Array<{ name: string; description: string; category: string; riskLevel: string; requiresPermission: boolean; enabled: boolean }>>([]);

  useEffect(() => {
    // M-54 修复: 添加 cancelled 标志，防止组件卸载后 setState
    let cancelled = false;
    if (window.metona?.tools?.list) {
      window.metona.tools.list().then((list) => {
        if (!cancelled) setTools(list as MetonaToolInfo[]);
      }).catch((err) => {
        console.error('[Sidebar]', err);
        if (!cancelled) {
          import('@metona-team/metona-toast').then((mod) => mod.default.error('加载工具列表失败')).catch(() => {});
        }
      });
    }
    return () => { cancelled = true; };
  }, []);

  const readyCount = tools.filter((t) => t.enabled).length;
  const riskColors: Record<string, string> = { safe: 'success.main', low: 'info.main', medium: 'warning.main', high: 'error.main' };
  const riskLabels: Record<string, string> = { safe: 'SAFE', low: 'LOW', medium: 'MEDIUM', high: 'HIGH' };
  return (
    <Box>
      <ListItemButton onClick={() => setExpanded(!expanded)} dense sx={{ borderRadius: 1, px: 1.5, py: 0.75 }}>
        <ListItemIcon sx={{ minWidth: 24 }}>{expanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />}<Wrench size={12} style={{ marginLeft: 4 }} /></ListItemIcon>
        <ListItemText primary={<Typography variant="body2" sx={{ fontSize: 12 }}>工具管理</Typography>} />
        <Typography variant="caption" sx={{ color: readyCount > 0 ? 'success.main' : 'text.disabled', fontSize: 10 }}>{readyCount} 就绪</Typography>
      </ListItemButton>
      <Collapse in={expanded}>
        <List dense disablePadding sx={{ pl: 3, maxHeight: 200, overflowY: 'auto', pr: 0.5, '&::-webkit-scrollbar': { width: 6 }, '&::-webkit-scrollbar-track': { borderRadius: 3 }, '&::-webkit-scrollbar-thumb': { bgcolor: 'divider', borderRadius: 3, '&:hover': { bgcolor: 'action.hover' } } }}>
          {tools.map((t) => (
            <ListItemButton key={t.name} dense sx={{ py: 0.25, px: 1, borderRadius: 1 }}>
              <Box sx={{ width: 6, height: 6, borderRadius: '50%', bgcolor: t.enabled ? riskColors[t.riskLevel] ?? 'text.disabled' : 'text.disabled', mr: 1, flexShrink: 0 }} />
              <Typography variant="caption" sx={{ flex: 1, fontSize: 11, color: t.enabled ? 'text.secondary' : 'text.disabled' }}>{t.name}</Typography>
              <Typography variant="caption" sx={{ fontSize: 9, color: t.enabled ? (riskColors[t.riskLevel] ?? 'text.disabled') : 'text.disabled' }}>{riskLabels[t.riskLevel] ?? t.riskLevel}</Typography>
            </ListItemButton>
          ))}
        </List>
      </Collapse>
    </Box>
  );
}
