import { Ionicons } from '@expo/vector-icons';
import { BlurView } from 'expo-blur';
import * as ImagePicker from 'expo-image-picker';
import { useLocalSearchParams, useRouter } from 'expo-router';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  ActivityIndicator,
  Alert,
  FlatList,
  Image,
  Keyboard,
  Modal,
  Platform,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from 'react-native';
import Markdown from 'react-native-markdown-display';
import Animated, { FadeInDown, FadeInUp, Layout } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import { HeaderBar } from '@/components/ui/HeaderBar';
import { Colors } from '@/constants/Colors';
import { buildCosKey, buildPublicUrl } from '@/constants/Cos';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useColorScheme } from '@/hooks/useColorScheme';
import { deleteConversation, getConversationDetail, listConversations, type AiConversationListItem } from '@/services/aiCoach';
import { loadAiCoachSessionCache, saveAiCoachSessionCache } from '@/services/aiCoachSession';
import { api, getAuthToken, postTextStream } from '@/services/api';
import { uploadWithRetry } from '@/services/cos';
import { updateUser as updateUserApi } from '@/services/users';
import type { CheckinRecord } from '@/store/checkinSlice';
import { fetchMyProfile, updateProfile } from '@/store/userSlice';
import dayjs from 'dayjs';

type Role = 'user' | 'assistant';

type ChatMessage = {
  id: string;
  role: Role;
  content: string;
};

const COACH_AVATAR = 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/avatar/imageCoach01.jpeg';

export default function AICoachChatScreen() {
  const router = useRouter();
  const params = useLocalSearchParams<{ name?: string }>();
  const insets = useSafeAreaInsets();
  const colorScheme = useColorScheme() ?? 'light';
  // 为了让页面更贴近品牌主题与更亮的观感，这里使用亮色系配色
  const theme = Colors.light;
  const coachName = (params?.name || 'Sarah').toString();
  const [input, setInput] = useState('');
  const [isSending, setIsSending] = useState(false);
  const [isStreaming, setIsStreaming] = useState(false);
  const [conversationId, setConversationId] = useState<string | undefined>(undefined);
  const [messages, setMessages] = useState<ChatMessage[]>([{
    id: 'm_welcome',
    role: 'assistant',
    content: `你好，我是你的普拉提教练 ${coachName}。可以向我咨询训练、体态、康复、柔韧等问题～`,
  }]);
  const [historyVisible, setHistoryVisible] = useState(false);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [historyPage, setHistoryPage] = useState(1);
  const [historyTotal, setHistoryTotal] = useState(0);
  const [historyItems, setHistoryItems] = useState<AiConversationListItem[]>([]);
  const listRef = useRef<FlatList<ChatMessage>>(null);
  const [isAtBottom, setIsAtBottom] = useState(true);
  const didInitialScrollRef = useRef(false);
  const [composerHeight, setComposerHeight] = useState<number>(80);
  const shouldAutoScrollRef = useRef(false);
  const [keyboardOffset, setKeyboardOffset] = useState(0);
  const pendingAssistantIdRef = useRef<string | null>(null);
  const [selectedImages, setSelectedImages] = useState<Array<{
    id: string;
    localUri: string;
    width?: number;
    height?: number;
    progress: number;
    uploadedKey?: string;
    uploadedUrl?: string;
    error?: string;
  }>>([]);
  const [previewImageUri, setPreviewImageUri] = useState<string | null>(null);

  const planDraft = useAppSelector((s) => s.trainingPlan?.draft);
  const checkin = useAppSelector((s) => (s as any).checkin);
  const dispatch = useAppDispatch();
  const userProfile = useAppSelector((s) => (s as any)?.user?.profile);

  const chips = useMemo(() => [
    { key: 'posture', label: '体态评估', action: () => router.push('/ai-posture-assessment') },
    { key: 'plan', label: 'AI制定训练计划', action: () => handleQuickPlan() },
    { key: 'analyze', label: '分析运动记录', action: () => handleAnalyzeRecords() },
    { key: 'weight', label: '记体重', action: () => insertWeightInputCard() },
  ], [router, planDraft, checkin]);

  const scrollToEnd = useCallback(() => {
    requestAnimationFrame(() => {
      listRef.current?.scrollToEnd({ animated: true });
    });
  }, []);

  const handleScroll = useCallback((e: any) => {
    try {
      const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent || {};
      const paddingToBottom = 60;
      const distanceFromBottom = (contentSize?.height || 0) - ((layoutMeasurement?.height || 0) + (contentOffset?.y || 0));
      setIsAtBottom(distanceFromBottom <= paddingToBottom);
    } catch { }
  }, []);

  useEffect(() => {
    // 初次进入或恢复时，保持最新消息可见
    scrollToEnd();
  }, [scrollToEnd]);
  // 启动页面时尝试恢复当次应用会话缓存
  useEffect(() => {
    (async () => {
      try {
        const cached = await loadAiCoachSessionCache();
        if (cached && Array.isArray(cached.messages) && cached.messages.length > 0) {
          setConversationId(cached.conversationId);
          setMessages(cached.messages as any);
          setTimeout(scrollToEnd, 0);
        }
      } catch { }
    })();
  }, [scrollToEnd]);

  // 会话变动时，轻量防抖写入缓存（在本次应用生命周期内可跨页面恢复；下次冷启动会被根布局清空）
  const saveCacheTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    if (saveCacheTimerRef.current) clearTimeout(saveCacheTimerRef.current);
    saveCacheTimerRef.current = setTimeout(() => {
      saveAiCoachSessionCache({ conversationId, messages: messages as any, updatedAt: Date.now() }).catch(() => { });
    }, 150);
    return () => { if (saveCacheTimerRef.current) clearTimeout(saveCacheTimerRef.current); };
    // 仅在 messages 或 conversationId 变化时触发
  }, [messages, conversationId]);


  // 取消对 messages.length 的全局监听滚动，改为在“消息实际追加完成”后再判断与滚动，避免突兀与多次触发

  useEffect(() => {
    // 输入区高度变化时，若用户在底部则轻柔跟随一次
    if (isAtBottom) {
      const id = setTimeout(scrollToEnd, 0);
      return () => clearTimeout(id);
    }
  }, [composerHeight, isAtBottom, scrollToEnd]);

  // 键盘事件：在键盘弹出时，将输入区与悬浮按钮一起上移，避免遮挡
  useEffect(() => {
    let showSub: any = null;
    let hideSub: any = null;
    if (Platform.OS === 'ios') {
      showSub = Keyboard.addListener('keyboardWillChangeFrame', (e: any) => {
        try {
          const height = Math.max(0, (e.endCoordinates?.height ?? 0) - insets.bottom);
          setKeyboardOffset(height);
        } catch { setKeyboardOffset(0); }
      });
      hideSub = Keyboard.addListener('keyboardWillHide', () => setKeyboardOffset(0));
    } else {
      showSub = Keyboard.addListener('keyboardDidShow', (e: any) => {
        try { setKeyboardOffset(Math.max(0, e.endCoordinates?.height ?? 0)); } catch { setKeyboardOffset(0); }
      });
      hideSub = Keyboard.addListener('keyboardDidHide', () => setKeyboardOffset(0));
    }
    return () => {
      try { showSub?.remove?.(); } catch { }
      try { hideSub?.remove?.(); } catch { }
    };
  }, [insets.bottom]);

  const streamAbortRef = useRef<{ abort: () => void } | null>(null);

  useEffect(() => {
    return () => {
      try { streamAbortRef.current?.abort(); } catch { }
    };
  }, []);

  function ensureConversationId(): string {
    if (conversationId && conversationId.trim()) return conversationId;
    const cid = `mobile-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
    setConversationId(cid);
    try { console.log('[AI_CHAT][ui] create temp conversationId', cid); } catch { }
    return cid;
  }

  function convertToServerMessages(history: ChatMessage[]): Array<{ role: 'user' | 'assistant' | 'system'; content: string }> {
    // 仅映射 user/assistant 消息；系统提示由后端自动注入
    return history
      .filter((m) => m.role === 'user' || m.role === 'assistant')
      .map((m) => ({ role: m.role, content: m.content }));
  }

  async function openHistory() {
    if (isStreaming) {
      try { streamAbortRef.current?.abort(); } catch { }
    }
    setHistoryVisible(true);
    await refreshHistory(1);
  }

  async function refreshHistory(page = 1) {
    try {
      setHistoryLoading(true);
      const resp = await listConversations(page, 20);
      setHistoryPage(resp.page);
      setHistoryTotal(resp.total);
      setHistoryItems(resp.items || []);
    } catch (e) {
      Alert.alert('错误', (e as any)?.message || '获取会话列表失败');
    } finally {
      setHistoryLoading(false);
    }
  }

  async function handleSelectConversation(id: string) {
    try {
      if (isStreaming) {
        try { streamAbortRef.current?.abort(); } catch { }
      }
      const detail = await getConversationDetail(id);
      if (!detail || !(detail as any).messages) {
        Alert.alert('提示', '会话不存在或已删除');
        return;
      }
      const mapped: ChatMessage[] = (detail.messages || [])
        .filter((m) => m.role === 'user' || m.role === 'assistant')
        .map((m, idx) => ({ id: `${m.role}_${idx}_${Date.now()}`, role: m.role as Role, content: m.content || '' }));
      setConversationId(detail.conversationId);
      setMessages(mapped.length ? mapped : [{ id: 'm_welcome', role: 'assistant', content: `你好，我是你的普拉提教练 ${coachName}。可以向我咨询训练、体态、康复、柔韧等问题～` }]);
      setHistoryVisible(false);
      setTimeout(scrollToEnd, 0);
    } catch (e) {
      Alert.alert('错误', (e as any)?.message || '加载会话失败');
    }
  }

  function confirmDeleteConversation(id: string) {
    Alert.alert('删除会话', '删除后将无法恢复，确定要删除该会话吗？', [
      { text: '取消', style: 'cancel' },
      {
        text: '删除', style: 'destructive', onPress: async () => {
          try {
            await deleteConversation(id);
            if (conversationId === id) {
              setConversationId(undefined);
              setMessages([{ id: 'm_welcome', role: 'assistant', content: `你好，我是你的普拉提教练 ${coachName}。可以向我咨询训练、体态、康复、柔韧等问题～` }]);
            }
            await refreshHistory(historyPage);
          } catch (e) {
            Alert.alert('错误', (e as any)?.message || '删除失败');
          }
        }
      }
    ]);
  }

  async function sendStream(text: string) {
    const tokenExists = !!getAuthToken();
    try { console.log('[AI_CHAT][ui] send start', { tokenExists, conversationId, textPreview: text.slice(0, 50) }); } catch { }

    // 终止上一次未完成的流
    if (streamAbortRef.current) {
      try { console.log('[AI_CHAT][ui] abort previous stream'); } catch { }
      try { streamAbortRef.current.abort(); } catch { }
      streamAbortRef.current = null;
    }

    // 发送 body：尽量提供历史消息，后端会优先使用 conversationId 关联上下文
    const historyForServer = convertToServerMessages(messages);
    const cid = ensureConversationId();
    const body = {
      conversationId: cid,
      messages: [...historyForServer, { role: 'user' as const, content: text }],
      stream: true,
    };

    // 在 UI 中先放置占位回答，随后持续增量更新
    const assistantId = `a_${Date.now()}`;
    const userMsgId = `u_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;

    const userMsg: ChatMessage = { id: userMsgId, role: 'user', content: text };
    shouldAutoScrollRef.current = isAtBottom;
    setMessages((m) => [...m, userMsg, { id: assistantId, role: 'assistant', content: '' }]);
    pendingAssistantIdRef.current = assistantId;

    setIsSending(true);
    setIsStreaming(true);

    let receivedAnyChunk = false;

    const updateAssistantContent = (delta: string) => {
      setMessages((prev) => {
        const next = prev.map((msg) => {
          if (msg.id === assistantId) {
            return { ...msg, content: msg.content + delta };
          }
          return msg;
        });
        return next;
      });
    };

    const onChunk = (chunk: string) => {
      receivedAnyChunk = true;
      const atBottomNow = isAtBottom;
      updateAssistantContent(chunk);
      if (atBottomNow) {
        // 在底部时，持续开启自动滚动，并主动触发一次滚动以避免极小增量未触发 onContentSizeChange 的情况
        shouldAutoScrollRef.current = true;
        setTimeout(scrollToEnd, 0);
      }
      try { console.log('[AI_CHAT][api] chunk', { length: chunk.length, preview: chunk.slice(0, 40) }); } catch { }
    };

    const onEnd = (cidFromHeader?: string) => {
      setIsSending(false);
      setIsStreaming(false);
      streamAbortRef.current = null;
      if (cidFromHeader && !conversationId) setConversationId(cidFromHeader);
      pendingAssistantIdRef.current = null;
      try { console.log('[AI_CHAT][api] end', { cidFromHeader, hadChunks: receivedAnyChunk }); } catch { }
    };

    const onError = async (err: any) => {
      try { console.warn('[AI_CHAT][api] error', err); } catch { }
      setIsSending(false);
      setIsStreaming(false);
      streamAbortRef.current = null;
      pendingAssistantIdRef.current = null;
      // 流式失败时的降级：尝试一次性非流式
      try {
        const bodyNoStream = { ...body, stream: false };
        try { console.log('[AI_CHAT][fallback] try non-stream'); } catch { }
        const resp = await api.post<{ conversationId?: string; text: string }>('/api/ai-coach/chat', bodyNoStream);
        const textCombined = (resp as any)?.text ?? '';
        if ((resp as any)?.conversationId && !conversationId) {
          setConversationId((resp as any).conversationId);
        }
        setMessages((prev) => prev.map((msg) => msg.id === assistantId ? { ...msg, content: textCombined || '（空响应）' } : msg));
      } catch (e2: any) {
        setMessages((prev) => prev.map((msg) => msg.id === assistantId ? { ...msg, content: '抱歉，请求失败，请稍后再试。' } : msg));
        try { console.warn('[AI_CHAT][fallback] non-stream error', e2); } catch { }
      }
    };

    try {
      const controller = postTextStream('/api/ai-coach/chat', body, { onChunk, onEnd, onError }, { timeoutMs: 120000 });
      streamAbortRef.current = controller;
    } catch (e) {
      onError(e);
    }
  }

  async function send(text: string) {
    if (isSending) return;
    const trimmed = text.trim();
    if (!trimmed && selectedImages.length === 0) return;

    async function ensureImagesUploaded(): Promise<string[]> {
      const urls: string[] = [];
      for (const img of selectedImages) {
        if (img.uploadedUrl) {
          urls.push(img.uploadedUrl);
          continue;
        }
        try {
          const resp = await fetch(img.localUri);
          const blob = await resp.blob();
          const ext = (() => {
            const t = (blob.type || '').toLowerCase();
            if (t.includes('png')) return 'png';
            if (t.includes('webp')) return 'webp';
            if (t.includes('heic')) return 'heic';
            if (t.includes('heif')) return 'heif';
            return 'jpg';
          })();
          const key = buildCosKey({ prefix: 'images/chat', ext });
          const res = await uploadWithRetry({
            key,
            body: blob,
            contentType: blob.type || 'image/jpeg',
            onProgress: ({ percent }: { percent?: number }) => {
              const p = typeof percent === 'number' ? percent : 0;
              setSelectedImages((prev) => prev.map((it) => it.id === img.id ? { ...it, progress: p } : it));
            },
          } as any);
          const url = buildPublicUrl(res.key);
          urls.push(url);
          setSelectedImages((prev) => prev.map((it) => it.id === img.id ? { ...it, uploadedKey: res.key, uploadedUrl: url, progress: 1 } : it));
        } catch (e: any) {
          setSelectedImages((prev) => prev.map((it) => it.id === img.id ? { ...it, error: e?.message || '上传失败' } : it));
          throw e;
        }
      }
      return urls;
    }

    try {
      const urls = await ensureImagesUploaded();
      const mdImages = urls.map((u) => `![image](${u})`).join('\n\n');
      const composed = [trimmed, mdImages].filter(Boolean).join('\n\n');
      setInput('');
      setSelectedImages([]);
      await sendStream(composed);
    } catch (e: any) {
      Alert.alert('上传失败', e?.message || '图片上传失败，请稍后重试');
    }
  }

  function handleQuickPlan() {
    const goalMap: Record<string, string> = {
      postpartum_recovery: '产后恢复',
      fat_loss: '减脂塑形',
      posture_correction: '体态矫正',
      core_strength: '核心力量',
      flexibility: '柔韧灵活',
      rehab: '康复保健',
      stress_relief: '释压放松',
    };
    const goalText = planDraft?.goal ? goalMap[planDraft.goal] : '整体提升';
    const freq = planDraft?.mode === 'sessionsPerWeek'
      ? `${planDraft?.sessionsPerWeek ?? 3}次/周`
      : (planDraft?.daysOfWeek?.length ? `${planDraft.daysOfWeek.length}次/周` : '3次/周');
    const prefer = planDraft?.preferredTimeOfDay ? `偏好${planDraft.preferredTimeOfDay}` : '时间灵活';
    const prompt = `请根据我的目标“${goalText}”、频率“${freq}”、${prefer}，制定1周的普拉提训练计划，包含每次训练主题、时长、主要动作与注意事项，并给出恢复建议。`;
    send(prompt);
  }

  function buildTrainingSummary(): string {
    const entries = Object.values(checkin?.byDate || {}) as CheckinRecord[];
    if (!entries.length) return '';
    const recent = entries.sort((a: any, b: any) => String(b.date).localeCompare(String(a.date))).slice(0, 14);
    let totalSessions = 0;
    let totalExercises = 0;
    let totalCompleted = 0;
    const categoryCount: Record<string, number> = {};
    const exerciseCount: Record<string, number> = {};
    for (const rec of recent) {
      if (!rec?.items?.length) continue;
      totalSessions += 1;
      for (const it of rec.items) {
        totalExercises += 1;
        if (it.completed) totalCompleted += 1;
        categoryCount[it.category] = (categoryCount[it.category] || 0) + 1;
        exerciseCount[it.name] = (exerciseCount[it.name] || 0) + 1;
      }
    }
    const topCategories = Object.entries(categoryCount).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([k, v]) => `${k}×${v}`);
    const topExercises = Object.entries(exerciseCount).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([k, v]) => `${k}×${v}`);
    return [
      `统计周期：最近${recent.length}天（按有记录日计 ${totalSessions} 天）`,
      `记录条目：${totalExercises}，完成标记：${totalCompleted}`,
      topCategories.length ? `高频类别：${topCategories.join('，')}` : '',
      topExercises.length ? `高频动作：${topExercises.join('，')}` : '',
    ].filter(Boolean).join('\n');
  }

  function handleAnalyzeRecords() {
    const summary = buildTrainingSummary();
    if (!summary) {
      send('我还没有可分析的打卡记录，请先在“每日打卡”添加并完成一些训练记录，然后帮我分析近期训练表现与改进建议。');
      return;
    }
    const prompt = `请基于以下我的近期训练记录进行分析，输出：1）整体训练负荷与节奏；2）动作与肌群的均衡性（指出偏多/偏少）；3）容易忽视的恢复与热身建议；4）后续一周的优化建议（频次/时长/动作方向）。\n\n${summary}`;
    send(prompt);
  }

  const pickImages = useCallback(async () => {
    try {
      const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.Images,
        allowsMultipleSelection: true,
        selectionLimit: 4,
        quality: 0.9,
      } as any);
      if ((result as any).canceled) return;
      const assets = (result as any).assets || [];
      const next = assets.map((a: any) => ({
        id: `${a.assetId || a.fileName || a.uri}_${Math.random().toString(36).slice(2, 8)}`,
        localUri: a.uri,
        width: a.width,
        height: a.height,
        progress: 0,
      }));
      setSelectedImages((prev) => {
        const merged = [...prev, ...next];
        return merged.slice(0, 4);
      });
      setTimeout(scrollToEnd, 0);
    } catch (e: any) {
      Alert.alert('错误', e?.message || '选择图片失败');
    }
  }, [scrollToEnd]);

  const removeSelectedImage = useCallback((id: string) => {
    setSelectedImages((prev) => prev.filter((it) => it.id !== id));
  }, []);

  function renderItem({ item }: { item: ChatMessage }) {
    const isUser = item.role === 'user';
    return (
      <Animated.View
        entering={isUser ? FadeInUp.springify().damping(18) : FadeInDown.springify().damping(18)}
        layout={Layout.springify().damping(18)}
        style={[styles.row, { justifyContent: isUser ? 'flex-end' : 'flex-start' }]}
      >
        {!isUser && (
          <Image source={{ uri: COACH_AVATAR }} style={styles.avatar} />
        )}
        <View
          style={[
            styles.bubble,
            {
              backgroundColor: isUser ? theme.primary : 'rgba(187,242,70,0.16)',
              borderTopLeftRadius: isUser ? 16 : 6,
              borderTopRightRadius: isUser ? 6 : 16,
            },
          ]}
        >
          {renderBubbleContent(item)}
        </View>
        {false}
      </Animated.View>
    );
  }

  function renderBubbleContent(item: ChatMessage) {
    if (!item.content?.trim() && isStreaming && pendingAssistantIdRef.current === item.id) {
      return <Text style={[styles.bubbleText, { color: '#687076' }]}>正在思考…</Text>;
    }
    if (item.content?.startsWith('__WEIGHT_INPUT_CARD__')) {
      const preset = (() => {
        const m = item.content.split('\n')?.[1];
        const v = parseFloat(m || '');
        return isNaN(v) ? '' : String(v);
      })();
      return (
        <View style={{ gap: 8 }}>
          <Text style={[styles.bubbleText, { color: '#192126', fontWeight: '700' }]}>记录今日体重</Text>
          <View style={styles.weightRow}>
            <TextInput
              placeholder="例如 60.5"
              keyboardType="decimal-pad"
              defaultValue={preset}
              placeholderTextColor={'#687076'}
              style={styles.weightInput}
              onSubmitEditing={(e) => handleSubmitWeight(e.nativeEvent.text)}
              returnKeyType="done"
              submitBehavior="blurAndSubmit"
            />
            <Text style={styles.weightUnit}>kg</Text>
            <TouchableOpacity accessibilityRole="button" style={styles.weightSaveBtn} onPress={() => handleSubmitWeight((preset || '').toString())}>
              <Text style={{ color: '#192126', fontWeight: '700' }}>保存</Text>
            </TouchableOpacity>
          </View>
          <Text style={{ color: '#687076', fontSize: 12 }}>按回车或点击保存，即可将该体重同步到账户并发送到对话。</Text>
        </View>
      );
    }
    return (
      <Markdown style={markdownStyles} mergeStyle>
        {item.content}
      </Markdown>
    );
  }

  function insertWeightInputCard() {
    const id = `wcard_${Date.now()}`;
    const preset = userProfile?.weight ? Number(userProfile.weight) : undefined;
    const payload = `__WEIGHT_INPUT_CARD__\n${preset ?? ''}`;
    setMessages((prev) => [...prev, { id, role: 'assistant', content: payload }]);
    setTimeout(scrollToEnd, 0);
  }

  async function handleSubmitWeight(text?: string) {
    const val = parseFloat(String(text ?? '').trim());
    if (isNaN(val) || val <= 0 || val > 500) {
      Alert.alert('请输入有效体重', '请填写合理的公斤数，例如 60.5');
      return;
    }
    try {
      // 本地更新
      dispatch(updateProfile({ weight: val }));
      // 后端同步（若有 userId 则更稳妥；后端实现容错）
      try {
        const userId = (userProfile as any)?.userId || (userProfile as any)?.id || (userProfile as any)?._id;
        if (userId) {
          await updateUserApi({ userId, weight: val });
          await dispatch(fetchMyProfile() as any);
        }
      } catch (e) {
        // 不阻断对话体验
      }
      // 在对话中插入“确认消息”并发送给教练
      const textMsg = `记录了今日体重：${val} kg。`;
      await send(textMsg);
    } catch (e: any) {
      Alert.alert('保存失败', e?.message || '请稍后重试');
    }
  }

  return (
    <View style={[styles.screen, { backgroundColor: theme.background }]}>
      <HeaderBar
        title={`教练 ${coachName}`}
        onBack={() => router.back()}
        tone="light"
        transparent
        right={(
          <TouchableOpacity accessibilityRole="button" onPress={openHistory} style={[styles.backButton, { backgroundColor: 'rgba(187,242,70,0.2)' }]}>
            <Ionicons name="time-outline" size={18} color={theme.onPrimary} />
          </TouchableOpacity>
        )}
      />

      <FlatList
        ref={listRef}
        data={messages}
        keyExtractor={(m) => m.id}
        renderItem={renderItem}
        onLayout={() => {
          // 确保首屏布局后也尝试滚动
          if (!didInitialScrollRef.current) {
            didInitialScrollRef.current = true;
            setTimeout(scrollToEnd, 0);
            requestAnimationFrame(scrollToEnd);
          }
        }}
        contentContainerStyle={{ paddingHorizontal: 14, paddingTop: 8 }}
        ListFooterComponent={() => (
          <View style={{ height: insets.bottom + keyboardOffset + composerHeight + (isAtBottom ? 0 : 56) + 16 }} />
        )}
        onContentSizeChange={() => {
          // 首次内容变化强制滚底，其余仅在接近底部时滚动
          if (!didInitialScrollRef.current) {
            didInitialScrollRef.current = true;
            setTimeout(scrollToEnd, 0);
            requestAnimationFrame(scrollToEnd);
            return;
          }
          if (shouldAutoScrollRef.current) {
            shouldAutoScrollRef.current = false;
            setTimeout(scrollToEnd, 0);
          }
        }}
        onScroll={handleScroll}
        scrollEventThrottle={16}
        showsVerticalScrollIndicator={false}
      />

      <BlurView
        intensity={18}
        tint={'light'}
        style={[styles.composerWrap, { paddingBottom: insets.bottom + 10, bottom: keyboardOffset }]}
        onLayout={(e) => {
          const h = e.nativeEvent.layout.height;
          if (h && Math.abs(h - composerHeight) > 0.5) setComposerHeight(h);
        }}
      >
        <ScrollView
          horizontal
          showsHorizontalScrollIndicator={false}
          decelerationRate="fast"
          snapToAlignment="start"
          style={styles.chipsRowScroll}
          contentContainerStyle={{ paddingHorizontal: 6, gap: 8 }}
        >
          {chips.map((c) => (
            <TouchableOpacity key={c.key} style={[styles.chip, { borderColor: 'rgba(187,242,70,0.35)', backgroundColor: 'rgba(187,242,70,0.12)' }]} onPress={c.action}>
              <Text style={[styles.chipText, { color: '#192126' }]}>{c.label}</Text>
            </TouchableOpacity>
          ))}
        </ScrollView>

        {!!selectedImages.length && (
          <ScrollView
            horizontal
            showsHorizontalScrollIndicator={false}
            style={styles.imagesRow}
            contentContainerStyle={{ paddingHorizontal: 6, gap: 8 }}
          >
            {selectedImages.map((img) => (
              <View key={img.id} style={styles.imageThumbWrap}>
                <TouchableOpacity accessibilityRole="imagebutton" onPress={() => setPreviewImageUri(img.uploadedUrl || img.localUri)}>
                  <Image source={{ uri: img.uploadedUrl || img.localUri }} style={styles.imageThumb} />
                </TouchableOpacity>
                {!!(img.progress > 0 && img.progress < 1) && (
                  <View style={styles.imageProgressOverlay}>
                    <Text style={styles.imageProgressText}>{Math.round((img.progress || 0) * 100)}%</Text>
                  </View>
                )}
                <TouchableOpacity accessibilityRole="button" onPress={() => removeSelectedImage(img.id)} style={styles.imageRemoveBtn}>
                  <Ionicons name="close" size={12} color="#fff" />
                </TouchableOpacity>
              </View>
            ))}
          </ScrollView>
        )}

        <View style={[styles.inputRow, { borderColor: 'rgba(187,242,70,0.35)', backgroundColor: 'rgba(187,242,70,0.08)' }]}>
          <TouchableOpacity
            accessibilityRole="button"
            onPress={pickImages}
            style={[styles.mediaBtn, { backgroundColor: 'rgba(187,242,70,0.16)' }]}
          >
            <Ionicons name="image-outline" size={18} color={'#192126'} />
          </TouchableOpacity>
          <TextInput
            placeholder="问我任何与普拉提相关的问题..."
            placeholderTextColor={theme.textMuted}
            style={[styles.input, { color: '#192126' }]}
            value={input}
            onChangeText={setInput}
            multiline
            onSubmitEditing={() => send(input)}
            blurOnSubmit={false}
          />
          <TouchableOpacity
            accessibilityRole="button"
            disabled={(!input.trim() && selectedImages.length === 0) || isSending}
            onPress={() => send(input)}
            style={[
              styles.sendBtn,
              { backgroundColor: theme.primary, opacity: (input.trim() || selectedImages.length > 0) && !isSending ? 1 : 0.5 }
            ]}
          >
            {isSending ? (
              <ActivityIndicator color={theme.onPrimary} />
            ) : (
              <Ionicons name="arrow-up" size={18} color={theme.onPrimary} />
            )}
          </TouchableOpacity>
        </View>
      </BlurView>

      {!isAtBottom && (
        <TouchableOpacity
          accessibilityRole="button"
          onPress={scrollToEnd}
          style={[styles.scrollToBottomFab, { bottom: insets.bottom + composerHeight + 16, backgroundColor: theme.primary }]}
        >
          <Ionicons name="chevron-down" size={18} color={theme.onPrimary} />
        </TouchableOpacity>
      )}

      <Modal transparent visible={historyVisible} animationType="fade" onRequestClose={() => setHistoryVisible(false)}>
        <TouchableOpacity activeOpacity={1} style={styles.modalBackdrop} onPress={() => setHistoryVisible(false)}>
          <View style={[styles.modalSheet, { backgroundColor: '#FFFFFF' }]}>
            <View style={styles.modalHeader}>
              <Text style={styles.modalTitle}>历史会话</Text>
              <TouchableOpacity accessibilityRole="button" onPress={() => refreshHistory(historyPage)} style={styles.modalRefreshBtn}>
                <Ionicons name="refresh" size={16} color="#192126" />
              </TouchableOpacity>
            </View>
            {historyLoading ? (
              <View style={{ paddingVertical: 20, alignItems: 'center' }}>
                <ActivityIndicator />
                <Text style={{ marginTop: 8, color: '#687076' }}>加载中...</Text>
              </View>
            ) : (
              <ScrollView style={{ maxHeight: 360 }}>
                {historyItems.length === 0 ? (
                  <Text style={{ padding: 16, color: '#687076' }}>暂无会话</Text>
                ) : (
                  historyItems.map((it) => (
                    <View key={it.conversationId} style={styles.historyRow}>
                      <TouchableOpacity
                        style={{ flex: 1 }}
                        onPress={() => handleSelectConversation(it.conversationId)}
                      >
                        <Text style={styles.historyTitle} numberOfLines={1}>{it.title || '未命名会话'}</Text>
                        <Text style={styles.historyMeta}>
                          {dayjs(it.lastMessageAt || it.createdAt).format('YYYY/MM/DD HH:mm')}
                        </Text>
                      </TouchableOpacity>
                      <TouchableOpacity accessibilityRole="button" onPress={() => confirmDeleteConversation(it.conversationId)} style={styles.historyDeleteBtn}>
                        <Ionicons name="trash-outline" size={16} color="#FF4444" />
                      </TouchableOpacity>
                    </View>
                  ))
                )}
              </ScrollView>
            )}
            <View style={styles.modalFooter}>
              <TouchableOpacity accessibilityRole="button" onPress={() => setHistoryVisible(false)} style={styles.modalCloseBtn}>
                <Text style={{ color: '#192126', fontWeight: '600' }}>关闭</Text>
              </TouchableOpacity>
            </View>
          </View>
        </TouchableOpacity>
      </Modal>
      <Modal transparent visible={!!previewImageUri} animationType="fade" onRequestClose={() => setPreviewImageUri(null)}>
        <TouchableOpacity activeOpacity={1} style={styles.previewBackdrop} onPress={() => setPreviewImageUri(null)}>
          <View style={styles.previewBox}>
            {previewImageUri ? (
              <Image source={{ uri: previewImageUri }} style={styles.previewImage} resizeMode="contain" />
            ) : null}
          </View>
        </TouchableOpacity>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 16,
    paddingBottom: 10,
  },
  backButton: {
    width: 32,
    height: 32,
    borderRadius: 16,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(255,255,255,0.06)'
  },
  headerTitle: {
    fontSize: 20,
    fontWeight: '800',
  },
  row: {
    flexDirection: 'row',
    alignItems: 'flex-end',
    gap: 8,
    marginVertical: 6,
  },
  avatar: {
    width: 28,
    height: 28,
    borderRadius: 14,
    alignItems: 'center',
    justifyContent: 'center',
  },
  avatarText: {
    color: '#192126',
    fontSize: 12,
    fontWeight: '800',
  },
  bubble: {
    maxWidth: '82%',
    paddingHorizontal: 12,
    paddingVertical: 10,
    borderRadius: 16,
  },
  bubbleText: {
    fontSize: 15,
    lineHeight: 22,
  },
  weightRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 8,
  },
  weightInput: {
    flex: 1,
    height: 36,
    borderWidth: 1,
    borderColor: 'rgba(0,0,0,0.08)',
    borderRadius: 8,
    paddingHorizontal: 10,
    backgroundColor: 'rgba(255,255,255,0.9)',
    color: '#192126',
  },
  weightUnit: {
    color: '#192126',
    fontWeight: '700',
  },
  weightSaveBtn: {
    height: 36,
    paddingHorizontal: 12,
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(187,242,70,0.6)'
  },
  // markdown 基础样式承载容器的字体尺寸保持与气泡一致
  composerWrap: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    paddingTop: 8,
    paddingHorizontal: 10,
    borderTopWidth: 0,
  },
  chipsRow: {
    flexDirection: 'row',
    gap: 8,
    paddingHorizontal: 6,
    marginBottom: 8,
  },
  chipsRowScroll: {
    marginBottom: 8,
  },
  chip: {
    paddingHorizontal: 10,
    height: 34,
    borderRadius: 18,
    borderWidth: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'transparent',
  },
  chipText: {
    fontSize: 13,
    fontWeight: '600',
  },
  imagesRow: {
    maxHeight: 92,
    marginBottom: 8,
  },
  imageThumbWrap: {
    width: 72,
    height: 72,
    borderRadius: 12,
    overflow: 'hidden',
    position: 'relative',
    backgroundColor: 'rgba(0,0,0,0.06)'
  },
  imageThumb: {
    width: '100%',
    height: '100%'
  },
  imageRemoveBtn: {
    position: 'absolute',
    right: 4,
    top: 4,
    width: 20,
    height: 20,
    borderRadius: 10,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(0,0,0,0.45)'
  },
  imageProgressOverlay: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    backgroundColor: 'rgba(0,0,0,0.35)',
    alignItems: 'center',
    justifyContent: 'center',
  },
  imageProgressText: {
    color: '#fff',
    fontWeight: '700'
  },
  inputRow: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 8,
    borderWidth: 1,
    borderRadius: 16,
    backgroundColor: 'rgba(0,0,0,0.04)'
  },
  mediaBtn: {
    width: 40,
    height: 40,
    borderRadius: 12,
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 6,
  },
  input: {
    flex: 1,
    fontSize: 15,
    maxHeight: 120,
    minHeight: 40,
    paddingHorizontal: 8,
    paddingVertical: 6,
    textAlignVertical: 'center',
  },
  sendBtn: {
    width: 40,
    height: 40,
    borderRadius: 20,
    alignItems: 'center',
    justifyContent: 'center',
  },
  scrollToBottomFab: {
    position: 'absolute',
    right: 16,
    width: 40,
    height: 40,
    borderRadius: 20,
    alignItems: 'center',
    justifyContent: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.15,
    shadowRadius: 4,
    elevation: 2,
  },
  modalBackdrop: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.35)',
    padding: 16,
    justifyContent: 'flex-end',
  },
  modalSheet: {
    borderTopLeftRadius: 16,
    borderTopRightRadius: 16,
    paddingHorizontal: 12,
    paddingTop: 10,
    paddingBottom: 12,
  },
  modalHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 4,
    paddingBottom: 8,
  },
  modalTitle: {
    fontSize: 16,
    fontWeight: '800',
    color: '#192126',
  },
  modalRefreshBtn: {
    width: 28,
    height: 28,
    borderRadius: 14,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(0,0,0,0.06)'
  },
  historyRow: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 10,
    paddingHorizontal: 8,
    borderRadius: 10,
  },
  historyTitle: {
    fontSize: 15,
    color: '#192126',
    fontWeight: '600',
  },
  historyMeta: {
    marginTop: 2,
    fontSize: 12,
    color: '#687076',
  },
  historyDeleteBtn: {
    width: 28,
    height: 28,
    borderRadius: 14,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'rgba(255,68,68,0.08)'
  },
  modalFooter: {
    paddingTop: 8,
    alignItems: 'flex-end',
  },
  modalCloseBtn: {
    paddingHorizontal: 14,
    paddingVertical: 8,
    borderRadius: 10,
    backgroundColor: 'rgba(0,0,0,0.06)'
  },
  previewBackdrop: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.85)',
    alignItems: 'center',
    justifyContent: 'center',
    padding: 16,
  },
  previewBox: {
    width: '100%',
    height: '80%',
    borderRadius: 12,
    overflow: 'hidden',
  },
  previewImage: {
    width: '100%',
    height: '100%',
  },
});

const markdownStyles = {
  body: {
    color: '#192126',
    fontSize: 15,
    lineHeight: 22,
  },
  paragraph: {
    marginTop: 2,
    marginBottom: 2,
  },
  bullet_list: {
    marginVertical: 4,
  },
  ordered_list: {
    marginVertical: 4,
  },
  list_item: {
    flexDirection: 'row',
  },
  code_inline: {
    backgroundColor: 'rgba(0,0,0,0.06)',
    borderRadius: 4,
    paddingHorizontal: 4,
    paddingVertical: 2,
  },
  code_block: {
    backgroundColor: 'rgba(0,0,0,0.06)',
    borderRadius: 8,
    paddingHorizontal: 8,
    paddingVertical: 6,
  },
  fence: {
    backgroundColor: 'rgba(0,0,0,0.06)',
    borderRadius: 8,
    paddingHorizontal: 8,
    paddingVertical: 6,
  },
  heading1: { fontSize: 20, fontWeight: '800', marginVertical: 6 },
  heading2: { fontSize: 18, fontWeight: '800', marginVertical: 6 },
  heading3: { fontSize: 16, fontWeight: '800', marginVertical: 6 },
  link: { color: '#246BFD' },
} as const;


