import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Taro, { useDidShow } from "@tarojs/taro";
import {
  AgentCatalogAction,
  AgentGrant,
  AgentModelOption,
  bootstrapOfficialSession,
  chatWithAgent,
  clearAgentSessionToken,
  createConversation,
  executeAgentAction,
  getAgentCatalog,
  getConversationMessages,
  getModelsWithFallback,
  listAgentGrants,
  listConversations,
  parseJsonLike,
  requestAgentAction,
  submitAgentMessageFeedback,
  updateAgentGrantScope
} from "../../../api/agent";
import { apiRequest } from "../../../api/client";
import type { ChatMessage, ClarificationOption, ConversationDraft, ConversationItem, DraftMode, MessageAction, PendingAction, UserBranch } from "../types";
import { DRAFT_MAX_AGE_MS, LIVE_DRAFT_STORAGE_KEY, LIVE_MODEL_STORAGE_KEY, UI_TEXT } from "../constants";
import { getTotalUnread, markConversationRead, incrementUnread, pruneUnread } from "../../../utils/unread";


import {
  shortTime, normalizeDisplayText, toMessageId, emptyDraft,
  normalizeConversation, normalizeConversationMessage, roleLabelFromMe, parseUserBranchesFromMe,
  previewLinesFromText, suggestionPreview, activityPreview, mutualAidPreview,
  activityRegisterPreview, activityListItemPreview, buildFallbackReply,
  formatActionResult, actionButtonsForResult, buildPendingDescription,
  pickKV,
} from "../utils/formatters";
import {
  isSuggestionIntent, isActivityIntent, isActivityRegisterIntent,
  isMutualAidIntent, isActivityListIntent, isShowDraftIntent,
  isPendingInspectIntent, isDraftEditIntent, rewriteDraftEditText,
  detectMutualAidType, extractActivityId, getNavigationTarget,
  hasStructuredSuggestionFields, hasStructuredActivityFields, hasStructuredMutualAidFields,
} from "../utils/intent";
import {
  buildSuggestionDraftFromTopic, buildActivityDraftFromTopic,
  buildMutualAidDraftFromTopic, buildSuggestionPayload,
  buildActivityPayload, buildMutualAidPayload,
} from "../utils/draft_builder";
import {
  applySuggestionDraft, applyActivityDraft, applyMutualAidDraft,
  suggestionMissing, activityMissing, mutualAidMissing,
  validateSuggestionDraft, validateActivityDraft, validateMutualAidDraft,
} from "../utils/draft_apply";

type GrantScope = {
  allowedActions?: string[];
};

function flattenAgentCatalog(data: unknown) {
  const directory = (data as { directory?: Record<string, AgentCatalogAction[]> } | null)?.directory || {};
  return Object.values(directory)
    .flat()
    .filter((item) => item && item.actionCode && (item.implementationStatus || "active") === "active")
    .sort((a, b) => {
      const moduleCompare = String(a.module || "").localeCompare(String(b.module || ""), "zh-CN");
      if (moduleCompare !== 0) return moduleCompare;
      return String(a.displayName || a.actionCode).localeCompare(String(b.displayName || b.actionCode), "zh-CN");
    });
}

function grantIdOf(grant: AgentGrant) {
  return Number(grant.id || 0);
}

function grantAllowedActions(grant: AgentGrant) {
  const rawScope = grant.grant_scope_json ?? grant.grantScopeJson;
  const scope = parseJsonLike<GrantScope>(rawScope, {});
  return Array.isArray(scope.allowedActions) ? scope.allowedActions.filter(Boolean).map(String) : [];
}

function isGrantActive(grant: AgentGrant) {
  return String(grant.grant_status ?? grant.grantStatus ?? "active") === "active";
}

function isReadOnlyAction(action: AgentCatalogAction) {
  const code = String(action.actionCode || "");
  const risk = String(action.riskLevel || "").toUpperCase();
  return risk === "L1" || code.includes(".read") || code.includes(".list") || code.startsWith("me.");
}

function actionRiskText(action: AgentCatalogAction) {
  const map: Record<string, string> = {
    L1: "只读",
    L2: "草稿",
    L3: "需确认",
    L4: "高风险"
  };
  const risk = String(action.riskLevel || "").toUpperCase();
  return map[risk] || risk || (action.requiresApproval ? "需确认" : "可用");
}

function actionDisplayName(action?: AgentCatalogAction, fallback?: string) {
  return normalizeDisplayText(action?.displayName || fallback || action?.actionCode || "", "未命名能力");
}

const FIELD_LABELS: Record<string, string> = {
  title: "标题",
  body: "正文",
  summary: "摘要",
  branchId: "分会",
  branchIds: "分会",
  activityId: "活动ID",
  surveyId: "问卷ID",
  answers: "问卷答案",
  postId: "互助ID",
  serviceTeamId: "服务团ID",
  serviceProjectId: "服务项目ID",
  friendshipId: "好友申请ID",
  targetUserId: "目标用户ID",
  realName: "真实姓名",
  phone: "手机号",
  category: "申请类别",
  recordType: "记录类型",
  projectTitle: "项目名称",
  performedAt: "履职时间",
  displayName: "展示名称",
  tags: "标签",
  postType: "互助类型"
};

const TARGET_ID_FIELDS = new Set(["activityId", "surveyId", "postId", "serviceTeamId", "serviceProjectId", "friendshipId"]);
const NUMERIC_FIELDS = new Set(["activityId", "surveyId", "postId", "serviceTeamId", "serviceProjectId", "friendshipId", "targetUserId", "branchId"]);
const ARRAY_FIELDS = new Set(["branchIds", "tags", "answers"]);
const REQUIRED_FIELDS_BY_ACTION: Record<string, string[]> = {
  "content.suggestion.submit": ["title", "body"],
  "content.suggestion.create_draft": ["title", "body"],
  "member_application.submit": ["realName", "phone", "category", "branchIds"],
  "performance_record.submit": ["recordType", "projectTitle", "performedAt"],
  "survey.answer.prepare": ["surveyId"],
  "survey.submit": ["surveyId", "answers"],
  "social_profile.update_submit": ["displayName"],
  "social_tags.update_submit": ["tags"],
  "friendship.request.send": ["targetUserId"],
  "friendship.request.accept": ["friendshipId"],
  "friendship.request.reject": ["friendshipId"],
  "mutual_aid.detail.read": ["postId"],
  "mutual_aid.post.submit": ["postType", "title", "branchId"],
  "service_team.detail.read": ["serviceTeamId"],
  "service_team.demand.submit": ["serviceTeamId"],
  "service_project.detail.read": ["serviceProjectId"],
  "service_project.submit": ["title", "branchId"]
};

function fieldLabel(field: string) {
  return FIELD_LABELS[field] || field;
}

function isMissingValue(value: unknown) {
  if (value === null || value === undefined) return true;
  if (typeof value === "string") return value.trim() === "";
  if (Array.isArray(value)) return value.length === 0;
  return false;
}

function pendingFieldValue(pending: PendingAction, field: string) {
  if (TARGET_ID_FIELDS.has(field) && pending.targetId) {
    return pending.targetId;
  }
  const payload = pending.payload || {};
  return payload[field] ?? (field === "activityId" ? pending.targetId : undefined);
}

function missingRequiredFieldsForPending(pending: PendingAction, capability?: AgentCatalogAction) {
  const catalogRequired = Array.isArray(capability?.requiredFields) ? capability.requiredFields : [];
  const required = catalogRequired.length > 0 ? catalogRequired : (REQUIRED_FIELDS_BY_ACTION[pending.actionCode] || []);
  return required.filter((field) => isMissingValue(pendingFieldValue(pending, field)));
}

function missingFieldQuestion(pending: PendingAction, missingFields: string[]) {
  const labels = missingFields.map(fieldLabel).join("、");
  return `这个工具还缺少必要信息：${labels}。你想怎么补充？`;
}

function missingFieldOptions(missingFields: string[]): ClarificationOption[] {
  return [
    {
      index: 1,
      label: `补充${fieldLabel(missingFields[0] || "信息")}`,
      description: "在输入框里补充字段和值，例如：标题：青年创新服务",
      action: "clarify_free_input"
    },
    {
      index: 2,
      label: "查看当前卡片",
      description: "先看已经整理出的内容",
      action: "show_pending_preview"
    },
    {
      index: 3,
      label: "取消执行",
      description: "放弃这次工具调用",
      action: "cancel_execute"
    },
    {
      index: 4,
      label: "自由输入",
      description: "用自己的话补充更多上下文",
      action: "clarify_free_input",
      freeInput: true
    }
  ];
}

function normalizeFieldInput(field: string, raw: string) {
  const value = normalizeDisplayText(raw);
  if (ARRAY_FIELDS.has(field)) {
    const parsed = parseJsonLike<unknown>(value, null);
    if (Array.isArray(parsed)) return parsed;
    const parts = value.split(/[,\n，、]/).map((item) => item.trim()).filter(Boolean);
    if (field === "branchIds") return parts.map(Number).filter((item) => Number.isFinite(item) && item > 0);
    if (field === "answers") return parts.length ? parts.map((item) => ({ textAnswer: item })) : [];
    return parts;
  }
  if (NUMERIC_FIELDS.has(field)) {
    const match = value.match(/\d+/);
    return match ? Number(match[0]) : undefined;
  }
  return value;
}

function fieldFromInputLabel(label: string, missingFields: string[]) {
  const normalized = normalizeDisplayText(label).toLowerCase();
  return missingFields.find((field) => {
    const fieldText = field.toLowerCase();
    const labelText = fieldLabel(field).toLowerCase();
    return normalized === fieldText || normalized === labelText || normalized.includes(labelText) || normalized.includes(fieldText);
  }) || "";
}

function mergePendingPayloadFromText(pending: PendingAction, rawText: string, missingFields: string[]) {
  const payload = { ...(pending.payload || {}) };
  const trimmed = normalizeDisplayText(rawText);
  const parsed = parseJsonLike<Record<string, unknown>>(trimmed, {});
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && Object.keys(parsed).length > 0) {
    missingFields.forEach((field) => {
      if (!isMissingValue(parsed[field])) {
        payload[field] = parsed[field];
      }
    });
  }
  trimmed.split(/\n/).forEach((line) => {
    const kv = pickKV(line);
    if (!kv) return;
    const field = fieldFromInputLabel(kv.key, missingFields);
    if (field) {
      payload[field] = normalizeFieldInput(field, kv.value);
    }
  });
  if (missingFields.length === 1 && isMissingValue(payload[missingFields[0]])) {
    payload[missingFields[0]] = normalizeFieldInput(missingFields[0], trimmed);
  }
  const targetField = missingFields.find((field) => TARGET_ID_FIELDS.has(field));
  const targetValue = targetField ? Number(payload[targetField] || 0) : 0;
  return {
    ...pending,
    targetId: targetValue > 0 ? targetValue : pending.targetId,
    payload
  };
}

function numberedQuestion(question: string, options: ClarificationOption[]) {
  return [
    question,
    ...options.map((option) => `${option.index}. ${option.label}`)
  ].join("\n");
}

function createDefaultToolClarification(text: string): { question: string; options: ClarificationOption[] } | null {
  const normalized = normalizeDisplayText(text);
  const hasConcreteIntent = isSuggestionIntent(normalized)
    || isActivityIntent(normalized)
    || isActivityRegisterIntent(normalized)
    || isMutualAidIntent(normalized)
    || isActivityListIntent(normalized);
  if (hasConcreteIntent) return null;
  const looksActionLike = /(帮我|我要|我想|想|需要|可以).*(发布|提交|创建|发起|办理|处理|写|查|找|推荐|报名|参加)/.test(normalized)
    || /^(发布|提交|创建|发起|办理|处理|写|查|找|推荐|报名|参加)/.test(normalized);
  if (!looksActionLike) return null;
  return {
    question: "我不确定你要办哪一种事项，先选一个方向吧：",
    options: [
      { index: 1, label: "写建言献策", description: "整理标题、摘要、正文，再生成确认卡", action: "clarify_start:suggestion" },
      { index: 2, label: "发布企业互助", description: "发布资源或需求，提交前会给你确认", action: "clarify_start:mutualAid" },
      { index: 3, label: "发起活动", description: "整理活动草稿，再走提交审核", action: "clarify_start:activity" },
      { index: 4, label: "自由输入", description: "你用自己的话补充具体要求", action: "clarify_free_input", freeInput: true }
    ]
  };
}

function choiceFromUserText(text: string, options: ClarificationOption[] | null) {
  if (!options?.length) return null;
  const normalized = normalizeDisplayText(text);
  const numberMatch = normalized.match(/^([1-4])(?:[.。:：、\s]|$)/);
  if (numberMatch) {
    const index = Number(numberMatch[1]);
    return options.find((option) => option.index === index) || null;
  }
  return options.find((option) => normalized === option.label || normalized.includes(option.label)) || null;
}

function branchChoiceOptions(branches: UserBranch[]) {
  const visible = branches.slice(0, 3);
  return [
    ...visible.map((branch, index) => ({
      index: index + 1,
      label: branch.name,
      description: `使用 ${branch.name} 继续办理`,
      action: `select_branch:${branch.id}`
    })),
    {
      index: 4,
      label: "自由输入",
      description: "输入分会名称或补充你的要求",
      action: "clarify_free_input",
      freeInput: true
    }
  ];
}


export function useLivePage() {
  const [models, setModels] = useState<AgentModelOption[]>([]);
  const [selectedModel, setSelectedModel] = useState("qwen-turbo");
  const [conversations, setConversations] = useState<ConversationItem[]>([]);
  const [activeConversationId, setActiveConversationId] = useState<number>(0);
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [inputValue, setInputValue] = useState("");
  const [loading, setLoading] = useState(false);
  const [bootstrapping, setBootstrapping] = useState(false);
  const [showHistory, setShowHistory] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [showModelList, setShowModelList] = useState(false);
  const [actingRole, setActingRole] = useState("已登录用户");
  const [actingUserName, setActingUserName] = useState("当前用户");
  const [userBranches, setUserBranches] = useState<UserBranch[]>([]);
  const [draft, setDraft] = useState<ConversationDraft>(emptyDraft());
  const [pending, setPending] = useState<PendingAction | null>(null);
  const [pendingClarification, setPendingClarification] = useState<ClarificationOption[] | null>(null);
  const [metricsText, setMetricsText] = useState("");
  const [agentGrants, setAgentGrants] = useState<AgentGrant[]>([]);
  const [agentCatalog, setAgentCatalog] = useState<AgentCatalogAction[]>([]);
  const [selectedActionsByGrant, setSelectedActionsByGrant] = useState<Record<number, string[]>>({});
  const [permissionLoading, setPermissionLoading] = useState(false);
  const [permissionSavingId, setPermissionSavingId] = useState<number | null>(null);
  const [permissionNotice, setPermissionNotice] = useState("");
  const [permissionError, setPermissionError] = useState("");
  const inflightRef = useRef<Record<string, boolean>>({});
  const bottomRef = useRef("live-bottom-anchor");
  const selectedModelRef = useRef(selectedModel);
  const activeConversationIdRef = useRef(activeConversationId);
  const bootstrappingRef = useRef(false);
  const bootstrappedRef = useRef(false);

  useEffect(() => {
    selectedModelRef.current = selectedModel;
  }, [selectedModel]);

  useEffect(() => {
    activeConversationIdRef.current = activeConversationId;
  }, [activeConversationId]);

  useEffect(() => {
    bootstrappingRef.current = bootstrapping;
  }, [bootstrapping]);

  useEffect(() => {
    try {
      if (selectedModel) {
        Taro.setStorageSync(LIVE_MODEL_STORAGE_KEY, selectedModel);
      }
      if (draft.mode === "none" && !pending) {
        Taro.removeStorageSync(LIVE_DRAFT_STORAGE_KEY);
        return;
      }
      Taro.setStorageSync(LIVE_DRAFT_STORAGE_KEY, {
        draft,
        pending,
        savedAt: Date.now()
      });
    } catch {
      // ignore local storage errors
    }
  }, [draft, pending, selectedModel]);

  const selectedModelLabel = useMemo(() => {
    return UI_TEXT.modelNames[selectedModel] || models.find((item) => item.code === selectedModel)?.label || UI_TEXT.modelFallback;
  }, [models, selectedModel]);

  const agentCatalogByCode = useMemo(() => {
    const map = new Map<string, AgentCatalogAction>();
    agentCatalog.forEach((item) => map.set(item.actionCode, item));
    return map;
  }, [agentCatalog]);

  const readOnlyCatalogActions = useMemo(() => agentCatalog.filter(isReadOnlyAction), [agentCatalog]);

  const operableCatalogActions = useMemo(() => agentCatalog.filter((item) => !isReadOnlyAction(item)), [agentCatalog]);

  const permissionSummary = useMemo(() => {
    const allSelected = agentGrants.flatMap((grant) => {
      const id = grantIdOf(grant);
      return selectedActionsByGrant[id] || grantAllowedActions(grant);
    });
    const unique = Array.from(new Set(allSelected));
    return {
      grants: agentGrants.length,
      readOnly: unique.filter((action) => {
        const item = agentCatalogByCode.get(action);
        return item ? isReadOnlyAction(item) : action.includes(".read") || action.startsWith("me.");
      }).length,
      operable: unique.filter((action) => {
        const item = agentCatalogByCode.get(action);
        return item ? !isReadOnlyAction(item) : !(action.includes(".read") || action.startsWith("me."));
      }).length
    };
  }, [agentCatalogByCode, agentGrants, selectedActionsByGrant]);

  const appendMessage = useCallback((message: ChatMessage) => {
    setMessages((prev) => [...prev, message]);
  }, []);

  const createAssistantMessage = useCallback((
    text: string,
    actions?: MessageAction[],
    draftLines?: string[],
    variant: ChatMessage["variant"] = "default",
    pageTarget?: string | null,
    sources?: ChatMessage["sources"],
    meta?: { messageId?: number; feedbackRating?: ChatMessage["feedbackRating"] },
    choiceOptions?: ClarificationOption[]
  ) => {
    appendMessage({
      id: meta?.messageId ? toMessageId("server", meta.messageId) : toMessageId("assistant", `${Date.now()}-${Math.random()}`),
      messageId: meta?.messageId,
      role: "assistant",
      text,
      time: shortTime(),
      actions,
      choiceOptions,
      draftLines,
      variant,
      pageTarget: pageTarget || undefined,
      sources: sources && sources.length > 0 ? sources : undefined,
      feedbackRating: meta?.feedbackRating || ""
    });
  }, [appendMessage]);

  const createChoiceMessage = useCallback((question: string, options: ClarificationOption[]) => {
    const normalizedOptions = options.slice(0, 4).map((option, index) => ({
      ...option,
      index: option.index || index + 1
    }));
    setPendingClarification(normalizedOptions);
    createAssistantMessage(
      numberedQuestion(question, normalizedOptions),
      undefined,
      undefined,
      "clarify",
      undefined,
      undefined,
      undefined,
      normalizedOptions
    );
  }, [createAssistantMessage]);

  const actionCapabilityFor = useCallback((actionCode: string) => {
    return agentCatalogByCode.get(actionCode);
  }, [agentCatalogByCode]);

  const createFreshConversation = useCallback(async (modelCode: string) => {
    const created = await createConversation({ title: "新会话", model: modelCode });
    const nextId = Number(created.id || 0);
    if (nextId > 0) {
      const normalized = normalizeConversation(created);
      setConversations((prev) => [normalized, ...prev.filter((item) => item.id !== nextId)]);
      setActiveConversationId(nextId);
      activeConversationIdRef.current = nextId;
      setMessages([]);
    }
    return nextId;
  }, []);

  const ensureConversation = useCallback(async () => {
    const currentConversationId = activeConversationIdRef.current;
    if (currentConversationId > 0) return currentConversationId;
    return createFreshConversation(selectedModelRef.current || "qwen-turbo");
  }, [createFreshConversation]);

  const refreshConversations = useCallback(async (focusId?: number) => {
    try {
      const rows = await listConversations();
      const items = rows.map(normalizeConversation);
      setConversations(items);
      const currentModel = selectedModelRef.current || "qwen-turbo";
      const currentConversationId = activeConversationIdRef.current;
      const modelMatchedId = items.find((item) => item.modelCode === currentModel)?.id || 0;
      const nextId = focusId || currentConversationId || modelMatchedId || items[0]?.id || 0;
      if (nextId > 0) {
        setActiveConversationId(nextId);
        activeConversationIdRef.current = nextId;
      }
      if (items.length === 0 && !focusId && currentConversationId <= 0) {
        await createFreshConversation(currentModel);
      }
    } catch {
      // keep local state
    }
  }, [createFreshConversation]);

  const messageCursorRef = useRef<number | undefined>(undefined);

  const loadConversationMessages = useCallback(async (conversationId: number, append = false) => {
    if (conversationId <= 0) return;
    try {
      const cursor = append ? messageCursorRef.current : undefined;
      const rows = await getConversationMessages(conversationId, 40, cursor);
      if (rows.length > 0) {
        messageCursorRef.current = rows[0].id;
      }
      const normalized = rows.map(normalizeConversationMessage);
      if (append) {
        setMessages((prev) => [...normalized, ...prev]);
      } else {
        setMessages(normalized);
      }
    } catch {
      if (!append) setMessages([]);
    }
  }, []);

  const loadAgentPermissions = useCallback(async () => {
    setPermissionLoading(true);
    setPermissionError("");
    try {
      const [catalogData, grantRows] = await Promise.all([
        getAgentCatalog(),
        listAgentGrants()
      ]);
      const catalogRows = flattenAgentCatalog(catalogData);
      const selected: Record<number, string[]> = {};
      grantRows.forEach((grant) => {
        const id = grantIdOf(grant);
        if (id > 0) selected[id] = grantAllowedActions(grant);
      });
      setAgentCatalog(catalogRows);
      setAgentGrants(grantRows);
      setSelectedActionsByGrant(selected);
    } catch (e) {
      setAgentCatalog([]);
      setAgentGrants([]);
      setSelectedActionsByGrant({});
      setPermissionError(normalizeDisplayText(e instanceof Error ? e.message : e, "权限信息加载失败"));
    } finally {
      setPermissionLoading(false);
    }
  }, []);

  const loadMe = useCallback(async () => {
    try {
      const me = await apiRequest("/api/me");
      setActingRole(roleLabelFromMe(me));
      setActingUserName(normalizeDisplayText(me?.name || me?.nickname || me?.realName || "当前用户"));
      setUserBranches(parseUserBranchesFromMe(me));
    } catch {
      setActingRole("游客");
      setActingUserName("当前用户");
      setUserBranches([]);
    }
  }, []);

  const bootstrapPage = useCallback(async () => {
    if (bootstrappingRef.current) return;
    bootstrappingRef.current = true;
    setBootstrapping(true);
    let resolvedModel = selectedModelRef.current || "qwen-turbo";
    let needsFreshConversation = false;
    try {
      try {
        await bootstrapOfficialSession();
      } catch {
        // ignore bootstrap failure and continue fallback mode
      }
      await loadMe();
      void loadAgentPermissions();
      try {
        const modelRows = await getModelsWithFallback();
        setModels(modelRows);
        if (modelRows.length > 0) {
          resolvedModel = modelRows.some((item) => item.code === selectedModelRef.current) ? selectedModelRef.current : modelRows[0].code;
          setSelectedModel(resolvedModel);
          selectedModelRef.current = resolvedModel;
        }
      } catch {
        setModels([]);
      }
      try {
        const previousModel = String(Taro.getStorageSync(LIVE_MODEL_STORAGE_KEY) || "");
        if (previousModel === "kimi-latest") {
          Taro.setStorageSync(LIVE_MODEL_STORAGE_KEY, "qwen-turbo");
        }
        const rows = await listConversations();
        const items = rows.map(normalizeConversation);
        const hasMatchingConversation = items.some((item) => item.modelCode === resolvedModel);
        needsFreshConversation = Boolean((previousModel && previousModel !== resolvedModel) || !hasMatchingConversation);

        setConversations(items);

        if (needsFreshConversation) {
          Taro.removeStorageSync(LIVE_DRAFT_STORAGE_KEY);
          setDraft(emptyDraft());
          setPending(null);
          await createFreshConversation(resolvedModel);
        } else {
          const preferredId = items.find((item) => item.modelCode === resolvedModel)?.id || items[0]?.id || 0;
          if (preferredId > 0) {
            setActiveConversationId(preferredId);
          } else {
            await createFreshConversation(resolvedModel);
          }
        }
        Taro.setStorageSync(LIVE_MODEL_STORAGE_KEY, resolvedModel);
      } catch {
        await refreshConversations();
      }
      try {
        const cached = !needsFreshConversation ? Taro.getStorageSync(LIVE_DRAFT_STORAGE_KEY) : null;
        if (cached && typeof cached === "object") {
          // 检查草稿是否过期
          const age = Date.now() - (Number(cached.savedAt) || 0);
          if (age > DRAFT_MAX_AGE_MS) {
            Taro.removeStorageSync(LIVE_DRAFT_STORAGE_KEY);
          } else {
          let restored = false;
          if (cached.draft && typeof cached.draft === "object") {
            setDraft(cached.draft as ConversationDraft);
            restored = true;
          }
          if (cached.pending && typeof cached.pending === "object") {
            setPending(cached.pending as PendingAction);
            restored = true;
          }
          if (restored) {
            createAssistantMessage("已为你恢复上次未完成的草稿或待确认动作。你可以继续补充、查看草稿，或通过按钮执行/取消。");
          }
          } // end age check
        }
      } catch {
        // ignore local storage errors
      }
    } finally {
      bootstrappingRef.current = false;
      bootstrappedRef.current = true;
      setBootstrapping(false);
    }
  }, [createAssistantMessage, createFreshConversation, loadAgentPermissions, loadMe, refreshConversations]);

  useEffect(() => {
    void bootstrapPage();
  }, [bootstrapPage]);

  useDidShow(() => {
    void loadMe();
    if (!bootstrappedRef.current || bootstrappingRef.current || activeConversationIdRef.current <= 0) {
      void bootstrapPage();
    }
  });

  // Mark current conversation as read when entering/re-entering
  useEffect(() => {
    if (activeConversationId > 0) {
      markConversationRead(activeConversationId);
    }
  }, [activeConversationId]);

  useEffect(() => {
    if (activeConversationId > 0) {
      void loadConversationMessages(activeConversationId);
    }
  }, [activeConversationId, loadConversationMessages]);

  const openSettings = useCallback(async () => {
    setShowSettings(true);
    setPermissionNotice("");
    await loadAgentPermissions();
  }, [loadAgentPermissions]);

  const getGrantSelectedActions = useCallback((grant: AgentGrant) => {
    const id = grantIdOf(grant);
    return selectedActionsByGrant[id] || grantAllowedActions(grant);
  }, [selectedActionsByGrant]);

  const isGrantActionSelected = useCallback((grant: AgentGrant, actionCode: string) => {
    return getGrantSelectedActions(grant).includes(actionCode);
  }, [getGrantSelectedActions]);

  const toggleGrantAction = useCallback((grant: AgentGrant, actionCode: string, checked: boolean) => {
    const id = grantIdOf(grant);
    if (id <= 0 || !isGrantActive(grant)) return;
    const catalogItem = agentCatalogByCode.get(actionCode);
    if (catalogItem?.allowedByRole === false) return;
    setPermissionNotice("");
    setPermissionError("");
    setSelectedActionsByGrant((current) => {
      const previous = current[id] || grantAllowedActions(grant);
      const next = checked
        ? Array.from(new Set([...previous, actionCode]))
        : previous.filter((action) => action !== actionCode);
      return { ...current, [id]: next };
    });
  }, [agentCatalogByCode]);

  const saveGrantScope = useCallback(async (grant: AgentGrant) => {
    const id = grantIdOf(grant);
    if (id <= 0) return;
    const selected = selectedActionsByGrant[id] || grantAllowedActions(grant);
    if (selected.length === 0) {
      setPermissionError("至少保留一个授权能力。");
      return;
    }
    setPermissionSavingId(id);
    setPermissionNotice("");
    setPermissionError("");
    try {
      const result = await updateAgentGrantScope(id, selected);
      const cancelled = Number(result?.cancelledPendingActionCount || 0);
      if (Array.isArray(result?.removedActions) && result.removedActions.length > 0) {
        clearAgentSessionToken();
      }
      setPermissionNotice(cancelled > 0 ? `权限已保存，已取消 ${cancelled} 个不再允许的待执行动作。` : "权限范围已保存。");
      await loadAgentPermissions();
    } catch (e) {
      setPermissionError(normalizeDisplayText(e instanceof Error ? e.message : e, "权限保存失败"));
    } finally {
      setPermissionSavingId(null);
    }
  }, [loadAgentPermissions, selectedActionsByGrant]);

  const jumpByAction = useCallback((action: string) => {
    if (action.startsWith("goto_my_suggestions")) {
      const suffix = action.includes(":") ? `?highlightId=${action.split(":")[1]}` : "";
      void Taro.navigateTo({ url: `/pages/my-suggestions/index${suffix}` });
      return;
    }
    if (action.startsWith("goto_my_created_activities")) {
      const suffix = action.includes(":") ? `?highlightId=${action.split(":")[1]}` : "";
      void Taro.navigateTo({ url: `/pages/my-created-activities/index${suffix}` });
      return;
    }
    if (action.startsWith("goto_my_registered_activities")) {
      const suffix = action.includes(":") ? `?highlightId=${action.split(":")[1]}` : "";
      void Taro.navigateTo({ url: `/pages/my-registered-activities/index${suffix}` });
      return;
    }
    if (action.startsWith("goto_my_mutual_aid_resources")) {
      const suffix = action.includes(":") ? `?highlightId=${action.split(":")[1]}` : "";
      void Taro.navigateTo({ url: `/pages/my-mutual-aid-resources/index${suffix}` });
      return;
    }
    if (action.startsWith("goto_my_mutual_aid_needs")) {
      const suffix = action.includes(":") ? `?highlightId=${action.split(":")[1]}` : "";
      void Taro.navigateTo({ url: `/pages/my-mutual-aid-needs/index${suffix}` });
      return;
    }
  }, []);

  const executePending = useCallback(async () => {
    if (!pending) return;
    const capability = actionCapabilityFor(pending.actionCode);
    const missingFields = missingRequiredFieldsForPending(pending, capability);
    if (missingFields.length > 0) {
      createChoiceMessage(missingFieldQuestion(pending, missingFields), missingFieldOptions(missingFields));
      return;
    }
    const cid = await ensureConversation();
    const requestId = `local_${Date.now()}`;
    createAssistantMessage(
      pending.actionCode === "content.suggestion.submit"
        ? "已确认，正在提交建言，请稍候..."
        : pending.actionCode === "activity.create_draft"
          ? "已确认，正在创建活动草稿并提交审核，请稍候..."
          : pending.actionCode === "mutual_aid.post.submit"
            ? "已确认，正在提交企业互助，请稍候..."
            : pending.actionCode === "activity.register.submit"
              ? "已确认，正在为你提交活动报名，请稍候..."
            : "已确认，正在执行，请稍候..."
    );
    try {
      const req = await requestAgentAction({
        actionCode: pending.actionCode,
        targetId: pending.targetId,
        payload: pending.payload || {},
        reason: pending.reason || "",
        conversationId: cid
      }, requestId);

      if (!req.executionToken) {
        createAssistantMessage(`动作已创建（#${req.actionRequestId}），当前状态：${req.status || "pending_approval"}。`, undefined, undefined, "pending");
        setPending(null);
        setDraft(emptyDraft());
        return;
      }

      const result = await executeAgentAction(req.actionRequestId, req.executionToken, requestId);

      if (result.actionCode === "activity.create_draft") {
        const activityId = Number((result.result as any)?.activityId || 0);
        if (activityId > 0) {
              const submitReq = await requestAgentAction({
                actionCode: "activity.submit_for_review",
                targetId: activityId,
                reason: "用户确认提交活动审核",
                conversationId: cid
              }, requestId);

          if (submitReq.executionToken) {
            try {
              const submitResult = await executeAgentAction(submitReq.actionRequestId, submitReq.executionToken, requestId);
              const text = formatActionResult("activity.submit_for_review", { activityId, title: (pending.payload as any)?.title }, pending.payload);
              createAssistantMessage(`${text}
动作号：#${submitReq.actionRequestId}
状态：${submitResult.status || "success"}`, actionButtonsForResult("activity.submit_for_review", pending.payload, String(activityId), activityId), undefined, "success");
            } catch (submitErr) {
              createAssistantMessage(`活动草稿已创建（ID: ${activityId}），但提交审核失败：${normalizeDisplayText(submitErr instanceof Error ? submitErr.message : submitErr, "请手动到活动管理提交审核")}`, actionButtonsForResult("activity.submit_for_review", pending.payload, String(activityId), activityId), undefined, "pending");
            }
          } else {
            createAssistantMessage(`活动草稿已创建（ID: ${activityId}）。
提交审核动作已创建（#${submitReq.actionRequestId}），请先审批后执行。`, actionButtonsForResult("activity.submit_for_review", pending.payload, String(activityId), activityId), undefined, "pending");
          }
        } else {
          createAssistantMessage("活动草稿已创建，但暂未拿到活动编号，请稍后到“我的-我发布的活动”核对。", actionButtonsForResult("activity.submit_for_review"), undefined, "success");
        }
      } else {
        const refId = String((result.result as any)?.id || (result.result as any)?.contentId || (result.result as any)?.postId || "").trim();
        const text = formatActionResult(result.actionCode, result.result, pending.payload);
        createAssistantMessage(`${text}
动作号：#${req.actionRequestId}
状态：${result.status || "success"}`, actionButtonsForResult(result.actionCode, pending.payload, refId, pending.targetId), undefined, "success");
      }

      await refreshConversations(cid);
      setPending(null);
      setDraft(emptyDraft());
    } catch (e) {
      createAssistantMessage(`执行失败：${normalizeDisplayText(e instanceof Error ? e.message : e, "请稍后重试")}`);
    }
  }, [actionCapabilityFor, createAssistantMessage, createChoiceMessage, ensureConversation, pending, refreshConversations]);

  const maybeAppendDraftPreview = useCallback(() => {
    if (draft.mode === "suggestion" && draft.suggestion) {
      const preview = suggestionPreview(draft.suggestion);
      createAssistantMessage(`当前建言草稿：
${preview}`, undefined, previewLinesFromText(preview));
      return true;
    }
    if (draft.mode === "activity" && draft.activity) {
      const preview = activityPreview(draft.activity);
      createAssistantMessage(`当前活动草稿：
${preview}`, undefined, previewLinesFromText(preview));
      return true;
    }
    if (draft.mode === "mutualAid" && draft.mutualAid) {
      const preview = mutualAidPreview(draft.mutualAid);
      createAssistantMessage(`当前互助草稿：
${preview}`, undefined, previewLinesFromText(preview));
      return true;
    }
    if (draft.mode === "activityRegister" && draft.activityRegister) {
      const preview = activityRegisterPreview(draft.activityRegister);
      createAssistantMessage(`当前报名信息：
${preview}`, undefined, previewLinesFromText(preview));
      return true;
    }
    createAssistantMessage("当前没有正在编辑的草稿。");
    return false;
  }, [createAssistantMessage, draft]);

  const startSuggestionFlow = useCallback((rawText: string) => {
    if (pending) {
      createAssistantMessage(`我先结束上一条待确认动作，再为你重新整理建言草稿。
${buildPendingDescription(pending)}`);
      setPending(null);
    }
    if (userBranches.length === 0) {
      createAssistantMessage("当前账号未加入分会，暂不能提交建言。请先申请加入分会。");
      return;
    }
    if (userBranches.length > 1) {
      setDraft({ mode: "suggestion", suggestion: {} });
      createChoiceMessage("你有多个分会，请先选择一个分会：", branchChoiceOptions(userBranches));
      return;
    }
    const branch = userBranches[0];
    if (!hasStructuredSuggestionFields(rawText)) {
      const generated = buildSuggestionDraftFromTopic(rawText, branch);
      setDraft({ mode: "suggestion", suggestion: generated });
      const preview = suggestionPreview(generated);
      setPending({
        actionCode: "content.suggestion.submit",
        payload: buildSuggestionPayload(generated),
        reason: "用户确认提交建言",
        previewText: `当前建言草稿：
${preview}`
      });
      createAssistantMessage(`我先帮你起草了一版建言，你可以直接确认提交，也可以继续告诉我怎么改：
${preview}`, [
        { label: UI_TEXT.confirm, action: "confirm_execute" },
        { label: UI_TEXT.preview, action: "show_pending_preview" },
        { label: UI_TEXT.cancel, action: "cancel_execute" }
      ], previewLinesFromText(preview), "pending");
      return;
    }
    setDraft({ mode: "suggestion", suggestion: { branchName: branch.name, branchIds: [branch.id] } });
    createAssistantMessage(
      `好的，我来帮你写建言。已默认分会：${branch.name}
请按下面格式回复（全部必填）：
1. 标题
2. 摘要
3. 正文
4. 分会（默认当前分会）

你发来后，我会先生成执行方案，再请你确认提交。`
    );
  }, [createAssistantMessage, createChoiceMessage, pending, userBranches]);

  const startActivityFlow = useCallback((rawText: string) => {
    if (pending) {
      createAssistantMessage(`我先结束上一条待确认动作，再为你重新整理活动草稿。
${buildPendingDescription(pending)}`);
      setPending(null);
    }
    if (userBranches.length === 0) {
      createAssistantMessage("当前账号未加入分会，暂不能发起活动。请先申请加入分会。");
      return;
    }
    if (userBranches.length > 1) {
      setDraft({ mode: "activity", activity: {} });
      createChoiceMessage("你有多个分会，请先选择一个分会：", branchChoiceOptions(userBranches));
      return;
    }
    const branch = userBranches[0];
    if (!hasStructuredActivityFields(rawText)) {
      const generated = buildActivityDraftFromTopic(rawText, branch);
      setDraft({ mode: "activity", activity: generated });
      const missing = activityMissing(generated);
      const issues = validateActivityDraft(generated);
      const preview = activityPreview(generated);
      if (missing.length === 0 && issues.length === 0) {
        setPending({
          actionCode: "activity.create_draft",
          payload: buildActivityPayload(generated),
          reason: "用户确认提交活动",
          previewText: `当前活动草稿：
${preview}`
        });
        createAssistantMessage(`我先帮你起草了一版活动，你可以直接确认提交，也可以继续告诉我怎么改：
${preview}`, [
          { label: UI_TEXT.confirm, action: "confirm_execute" },
          { label: UI_TEXT.preview, action: "show_pending_preview" },
          { label: UI_TEXT.cancel, action: "cancel_execute" }
        ], previewLinesFromText(preview), "pending");
        return;
      }
      createAssistantMessage(
        `我先帮你起草了一版活动，还需要你补充以下信息：${missing.join("、") || "无"}。
${issues.length > 0 ? `另外请修正：${issues.join("；")}
` : ""}${preview}`,
        undefined,
        previewLinesFromText(preview)
      );
      return;
    }
    setDraft({ mode: "activity", activity: { branchName: branch.name, branchIds: [branch.id] } });
    createAssistantMessage(
      `好的，我来帮你发起活动。已默认分会：${branch.name}
请按下面格式回复：
1. 活动标题
2. 活动简介
3. 开始时间（如 2026-05-30 14:00）
4. 结束时间（如 2026-05-30 16:00）
5. 活动地点
6. 参与人数上限（可选）
7. 报名截止时间（可选）
8. 分会（可选，默认当前分会）

你发来后，我会先生成发布方案，再请你确认提交。`
    );
  }, [createAssistantMessage, createChoiceMessage, pending, userBranches]);

  const startMutualAidFlow = useCallback((rawText: string) => {
    if (pending) {
      createAssistantMessage(`我先结束上一条待确认动作，再为你重新整理互助草稿。
${buildPendingDescription(pending)}`);
      setPending(null);
    }
    if (userBranches.length === 0) {
      createAssistantMessage("当前账号未加入分会，暂不能发布企业互助。请先申请加入分会。");
      return;
    }
    if (userBranches.length > 1) {
      setDraft({ mode: "mutualAid", mutualAid: { postType: detectMutualAidType(rawText) } });
      createChoiceMessage("你有多个分会，请先选择一个分会：", branchChoiceOptions(userBranches));
      return;
    }
    const branch = userBranches[0];
    if (!hasStructuredMutualAidFields(rawText)) {
      const generated = buildMutualAidDraftFromTopic(rawText, branch);
      setDraft({ mode: "mutualAid", mutualAid: generated });
      const missing = mutualAidMissing(generated);
      const issues = validateMutualAidDraft(generated);
      const preview = mutualAidPreview(generated);
      if (missing.length === 0 && issues.length === 0) {
        setPending({
          actionCode: "mutual_aid.post.submit",
          payload: buildMutualAidPayload(generated),
          reason: "用户确认提交企业互助",
          previewText: `当前互助草稿：
${preview}`
        });
        createAssistantMessage(`我先帮你起草了一版企业互助，你可以直接确认提交，也可以继续告诉我怎么改：
${preview}`, [
          { label: UI_TEXT.confirm, action: "confirm_execute" },
          { label: UI_TEXT.preview, action: "show_pending_preview" },
          { label: UI_TEXT.cancel, action: "cancel_execute" }
        ], previewLinesFromText(preview), "pending");
        return;
      }
      createAssistantMessage(
        `我先帮你起草了一版企业互助，还需要你补充以下信息：${missing.join("、") || "无"}。
${issues.length > 0 ? `另外请修正：${issues.join("；")}
` : ""}${preview}`,
        undefined,
        previewLinesFromText(preview)
      );
      return;
    }
    setDraft({ mode: "mutualAid", mutualAid: { postType: detectMutualAidType(rawText), branchName: branch.name, branchId: branch.id } });
    createAssistantMessage(
      `好的，我来帮你发布企业互助。已默认分会：${branch.name}
请按下面格式回复：
1. 互助类型（资源/需求）
2. 标题
3. 详情描述
4. 联系电话（可选）
5. 联系微信（可选）
6. 标签（可选）
7. 发布人身份（可选）
8. 分会（可选，默认当前分会）

你发来后，我会先生成发布方案，再请你确认提交。`
    );
  }, [createAssistantMessage, createChoiceMessage, pending, userBranches]);

  const startActivityRegisterFlow = useCallback(async (rawText: string) => {
    if (pending) {
      createAssistantMessage(`我先结束上一条待确认动作，再为你重新整理活动报名。
${buildPendingDescription(pending)}`);
      setPending(null);
    }
    const activityId = extractActivityId(rawText);
    if (activityId <= 0) {
      setDraft({ mode: "activityRegister", activityRegister: {} });
      createAssistantMessage("好的，我来帮你报名活动。请先提供目标活动ID，例如：活动ID: 123。确认活动后，我会只通过按钮让你执行或取消报名。");
      return;
    }
    const cid = await ensureConversation();
    createAssistantMessage(`我先为你核对活动信息（活动ID: ${activityId}），请稍候...`);
    try {
      const requestKey = `register_prepare_${Date.now()}`;
      const req = await requestAgentAction({
        actionCode: "activity.register.prepare",
        targetId: activityId,
        reason: "用户发起活动报名前置确认",
        conversationId: cid
      }, requestKey);
      if (!req.executionToken) {
        createAssistantMessage(`报名准备动作已创建（#${req.actionRequestId}），当前状态：${req.status || "pending_approval"}。`);
        await refreshConversations(cid);
        return;
      }
      const result = await executeAgentAction(req.actionRequestId, req.executionToken, requestKey);
      const activity = (result.result as any)?.activity || {};
      const previewDraft: import("../types").ActivityRegisterDraft = {
        activityId,
        title: normalizeDisplayText(activity?.title || activity?.name || `活动${activityId}`),
        startsAt: normalizeDisplayText(activity?.startsAt || activity?.starts_at || ""),
        location: normalizeDisplayText(activity?.location || "")
      };
      const preview = activityRegisterPreview(previewDraft);
      setDraft({ mode: "activityRegister", activityRegister: previewDraft });
      setPending({
        actionCode: "activity.register.submit",
        targetId: activityId,
        reason: "用户确认报名活动",
        previewText: `当前报名信息：
${preview}`
      });
      createAssistantMessage(`我已找到目标活动，请确认是否执行报名：
${preview}`, [
        { label: UI_TEXT.confirm, action: "confirm_execute" },
        { label: UI_TEXT.preview, action: "show_pending_preview" },
        { label: UI_TEXT.cancel, action: "cancel_execute" }
      ], previewLinesFromText(preview), "pending");
      await refreshConversations(cid);
    } catch (e) {
      createAssistantMessage(`活动报名准备失败：${normalizeDisplayText(e instanceof Error ? e.message : e, "请稍后重试")}`);
    }
  }, [createAssistantMessage, ensureConversation, pending, refreshConversations]);

  const handleDraftInput = useCallback((rawText: string) => {
    if (draft.mode === "suggestion") {
      const merged = applySuggestionDraft(draft.suggestion || {}, rawText, userBranches);
      if (!merged.branchIds?.length && userBranches.length === 1) {
        merged.branchIds = [userBranches[0].id];
        merged.branchName = userBranches[0].name;
      }
      setDraft({ mode: "suggestion", suggestion: merged });
      const missing = suggestionMissing(merged);
      const issues = validateSuggestionDraft(merged);
      if (missing.length === 0 && issues.length === 0) {
        const preview = suggestionPreview(merged);
        setPending({
          actionCode: "content.suggestion.submit",
          payload: buildSuggestionPayload(merged),
          reason: "用户确认提交建言",
          previewText: `当前建言草稿：
${preview}`
        });
        createAssistantMessage(`建言草稿已整理完成，请确认提交：
${preview}`, [
          { label: UI_TEXT.confirm, action: "confirm_execute" },
          { label: UI_TEXT.preview, action: "show_pending_preview" },
          { label: UI_TEXT.cancel, action: "cancel_execute" }
        ], previewLinesFromText(preview), "pending");
      } else {
        createAssistantMessage(
          ["我已记录你的建言信息。", missing.length ? `还缺少：${missing.join("、")}` : "", issues.length ? `请修正：${issues.join("；")}` : "", "你可以继续补充，我会自动合并。"].filter(Boolean).join("\n")
        );
      }
      return true;
    }

    if (draft.mode === "activity") {
      const merged = applyActivityDraft(draft.activity || {}, rawText, userBranches);
      if (!merged.branchIds?.length && userBranches.length === 1) {
        merged.branchIds = [userBranches[0].id];
        merged.branchName = userBranches[0].name;
      }
      setDraft({ mode: "activity", activity: merged });
      const missing = activityMissing(merged);
      const issues = validateActivityDraft(merged);
      if (missing.length === 0 && issues.length === 0) {
        const preview = activityPreview(merged);
        setPending({
          actionCode: "activity.create_draft",
          payload: buildActivityPayload(merged),
          reason: "用户确认提交活动",
          previewText: `当前活动草稿：
${preview}`
        });
        createAssistantMessage(`活动草稿已整理完成，请确认提交：
${preview}`, [
          { label: UI_TEXT.confirm, action: "confirm_execute" },
          { label: UI_TEXT.preview, action: "show_pending_preview" },
          { label: UI_TEXT.cancel, action: "cancel_execute" }
        ], previewLinesFromText(preview), "pending");
      } else {
        createAssistantMessage(
          ["我已记录你的活动信息。", missing.length ? `还缺少：${missing.join("、")}` : "", issues.length ? `请修正：${issues.join("；")}` : "", "你可以继续补充，我会自动合并。"].filter(Boolean).join("\n")
        );
      }
      return true;
    }

    if (draft.mode === "mutualAid") {
      const merged = applyMutualAidDraft(draft.mutualAid || {}, rawText, userBranches);
      if (!merged.branchId && userBranches.length === 1) {
        merged.branchId = userBranches[0].id;
        merged.branchName = userBranches[0].name;
      }
      setDraft({ mode: "mutualAid", mutualAid: merged });
      const missing = mutualAidMissing(merged);
      const issues = validateMutualAidDraft(merged);
      if (missing.length === 0 && issues.length === 0) {
        const preview = mutualAidPreview(merged);
        setPending({
          actionCode: "mutual_aid.post.submit",
          payload: buildMutualAidPayload(merged),
          reason: "用户确认提交企业互助",
          previewText: `当前互助草稿：
${preview}`
        });
        createAssistantMessage(`互助草稿已整理完成，请确认提交：
${preview}`, [
          { label: UI_TEXT.confirm, action: "confirm_execute" },
          { label: UI_TEXT.preview, action: "show_pending_preview" },
          { label: UI_TEXT.cancel, action: "cancel_execute" }
        ], previewLinesFromText(preview), "pending");
      } else {
        createAssistantMessage(
          ["我已记录你的互助信息。", missing.length ? `还缺少：${missing.join("、")}` : "", issues.length ? `请修正：${issues.join("；")}` : "", "你可以继续补充，我会自动合并。"].filter(Boolean).join("\n")
        );
      }
      return true;
    }

    if (draft.mode === "activityRegister") {
      const activityId = extractActivityId(rawText);
      if (activityId <= 0) {
        createAssistantMessage("还没有识别到有效的活动ID。请按“活动ID: 123”这样的格式回复，我再为你生成报名确认卡。");
        return true;
      }
      void startActivityRegisterFlow(`活动ID: ${activityId}`);
      return true;
    }

    return false;
  }, [createAssistantMessage, draft, startActivityRegisterFlow, userBranches]);

  const handleGenericPendingInput = useCallback((rawText: string) => {
    if (!pending) return false;
    const capability = actionCapabilityFor(pending.actionCode);
    const missingFields = missingRequiredFieldsForPending(pending, capability);
    if (missingFields.length === 0) return false;

    const nextPending = mergePendingPayloadFromText(pending, rawText, missingFields);
    const nextMissing = missingRequiredFieldsForPending(nextPending, capability);
    setPending(nextPending);
    if (nextMissing.length === 0) {
      const preview = buildPendingDescription(nextPending);
      createAssistantMessage(`已补齐必要信息，请确认是否执行：
${preview}`, [
        { label: UI_TEXT.confirm, action: "confirm_execute" },
        { label: UI_TEXT.preview, action: "show_pending_preview" },
        { label: UI_TEXT.cancel, action: "cancel_execute" }
      ], previewLinesFromText(preview), "pending");
      return true;
    }
    createChoiceMessage(missingFieldQuestion(nextPending, nextMissing), missingFieldOptions(nextMissing));
    return true;
  }, [actionCapabilityFor, createAssistantMessage, createChoiceMessage, pending]);

  const askBackend = useCallback(async (rawText: string, cid: number) => {
    try {
      // Dynamic context window: ~4k chars budget, keep recent messages first
      const MAX_CONTEXT_CHARS = 4000;
      let charCount = 0;
      const selected: typeof messages = [];
      for (let i = messages.length - 1; i >= 0; i--) {
        const next = charCount + (messages[i].text?.length || 0);
        if (next > MAX_CONTEXT_CHARS && selected.length >= 2) break;
        charCount = next;
        selected.unshift(messages[i]);
      }
      const payloadMessages = selected.map((item) => ({ role: item.role, content: item.text }));
      payloadMessages.push({ role: "user", content: rawText });
      const result = await chatWithAgent({
        conversationId: cid,
        model: selectedModel,
        messages: payloadMessages
      }, `chat_${Date.now()}`);

      if (result.conversationId && result.conversationId !== cid) {
        setActiveConversationId(result.conversationId);
      }
      const reply = normalizeDisplayText(result.reply || result.error || "", buildFallbackReply(rawText, UI_TEXT.appName));
      // Check for navigation target from user message or backend reply
      const navTarget = getNavigationTarget(rawText) || getNavigationTarget(reply) || (result as any).pageTarget || (result as any).navigation || null;
      const assistantMessageId = Number(result.assistantMessage?.id || 0) || undefined;
      createAssistantMessage(reply, undefined, undefined, undefined, navTarget, result.sources, {
        messageId: assistantMessageId,
        feedbackRating: result.assistantMessage?.feedbackRating || ""
      });
      const metrics = result.metrics;
      if (metrics) {
        const parts: string[] = [];
        if (typeof metrics.elapsedMs === "number") parts.push(`时延 ${metrics.elapsedMs}ms`);
        if (typeof metrics.knowledgeCount === "number") parts.push(`知识 ${metrics.knowledgeCount}`);
        if (typeof metrics.memoryCount === "number") parts.push(`记忆 ${metrics.memoryCount}`);
        if (typeof metrics.sourceCount === "number" && metrics.sourceCount > 0) parts.push(`来源 ${metrics.sourceCount}`);
        setMetricsText(parts.join(" / "));
      }
      await refreshConversations(result.conversationId || cid);
    } catch (e) {
      createAssistantMessage(normalizeDisplayText(e instanceof Error ? e.message : e, "当前智能对话暂时不可用。"));
    }
  }, [createAssistantMessage, messages, refreshConversations, selectedModel]);

  const handleMessageFeedback = useCallback(async (messageId: number | undefined, rating: "up" | "down" | "neutral") => {
    const cid = activeConversationIdRef.current;
    if (!messageId || cid <= 0) {
      void Taro.showToast({ title: "这条回复暂不能评价", icon: "none" });
      return;
    }
    setMessages((prev) => prev.map((item) => item.messageId === messageId
      ? { ...item, feedbackRating: rating, feedbackPending: true }
      : item
    ));
    try {
      const saved = await submitAgentMessageFeedback(cid, messageId, { rating });
      setMessages((prev) => prev.map((item) => item.messageId === messageId
        ? { ...item, feedbackRating: saved.rating || rating, feedbackPending: false }
        : item
      ));
      void Taro.showToast({ title: rating === "down" ? "已收到反馈" : "谢谢反馈", icon: "none" });
    } catch (e) {
      setMessages((prev) => prev.map((item) => item.messageId === messageId
        ? { ...item, feedbackPending: false }
        : item
      ));
      void Taro.showToast({ title: normalizeDisplayText(e instanceof Error ? e.message : e, "反馈提交失败"), icon: "none" });
    }
  }, []);

  const handleActionClick = useCallback(async (action: string) => {
    if (action.startsWith("clarify_start:")) {
      setPendingClarification(null);
      const kind = action.split(":")[1] || "";
      if (kind === "suggestion") {
        startSuggestionFlow("帮我写一篇建言献策");
        return;
      }
      if (kind === "activity") {
        startActivityFlow("我要发起活动");
        return;
      }
      if (kind === "mutualAid") {
        startMutualAidFlow("帮我发布一条企业互助");
        return;
      }
    }
    if (action === "clarify_free_input") {
      setPendingClarification(null);
      createAssistantMessage("好的，你可以直接在输入框里补充你的具体要求。我会先确认清楚，再生成工具卡片。");
      setInputValue("");
      return;
    }
    if (action === "confirm_execute") {
      await executePending();
      return;
    }
    if (action === "cancel_execute") {
      setPending(null);
      createAssistantMessage("已取消本次执行。");
      return;
    }
    if (action === "show_pending_preview") {
      createAssistantMessage(buildPendingDescription(pending), undefined, pending?.previewText ? previewLinesFromText(pending.previewText) : undefined);
      return;
    }
    if (action.startsWith("prepare_register_activity:")) {
      const activityId = Number(action.split(":")[1] || 0);
      if (activityId > 0) {
        await startActivityRegisterFlow(`活动ID: ${activityId}`);
      }
      return;
    }
    if (action.startsWith("select_branch:")) {
      setPendingClarification(null);
      const branchId = Number(action.split(":")[1] || 0);
      const branch = userBranches.find((item) => item.id === branchId);
      if (!branch) return;
      if (draft.mode === "suggestion") {
        setDraft({ mode: "suggestion", suggestion: { ...(draft.suggestion || {}), branchName: branch.name, branchIds: [branch.id] } });
        createAssistantMessage(`已选择分会：${branch.name}
请继续填写建言内容（全部必填）：
1. 标题
2. 摘要
3. 正文`);
        return;
      }
      if (draft.mode === "activity") {
        setDraft({ mode: "activity", activity: { ...(draft.activity || {}), branchName: branch.name, branchIds: [branch.id] } });
        createAssistantMessage(`已选择分会：${branch.name}
请继续填写活动信息：
1. 活动标题
2. 活动简介
3. 开始时间
4. 结束时间
5. 活动地点`);
        return;
      }
      if (draft.mode === "mutualAid") {
        setDraft({ mode: "mutualAid", mutualAid: { ...(draft.mutualAid || {}), branchName: branch.name, branchId: branch.id } });
        createAssistantMessage(`已选择分会：${branch.name}
请继续填写互助信息：
1. 互助类型（资源/需求）
2. 标题
3. 详情描述`);
      }
      return;
    }
    if (action.startsWith('navigate:')) {
      const url = action.slice('navigate:'.length);
      void Taro.navigateTo({ url });
      return;
    }
    jumpByAction(action);
  }, [
    createAssistantMessage,
    draft,
    executePending,
    jumpByAction,
    pending,
    startActivityFlow,
    startActivityRegisterFlow,
    startMutualAidFlow,
    startSuggestionFlow,
    userBranches
  ]);

  const handleSend = useCallback(async (preset?: string) => {
    const rawText = normalizeDisplayText(preset ?? inputValue);
    const normalizedText = isDraftEditIntent(rawText) ? rewriteDraftEditText(rawText, draft.mode, draft) : rawText;
    if (!rawText) return;
    const inflightKey = `${activeConversationId || 0}:${normalizedText}`;
    if (inflightRef.current[inflightKey]) return;
    inflightRef.current[inflightKey] = true;
    setInputValue("");
    appendMessage({
      id: toMessageId("user", `${Date.now()}-${Math.random()}`),
      role: "user",
      text: rawText,
      time: shortTime()
    });

    try {
      const selectedChoice = choiceFromUserText(rawText, pendingClarification);
      if (selectedChoice) {
        await handleActionClick(selectedChoice.action);
        return;
      }
      if (pendingClarification) {
        setPendingClarification(null);
      }
      if (normalizedText === "确认提交" && pending) {
        createAssistantMessage("请点击下方“确认提交”按钮继续。为避免误操作，输入文字不会直接触发真实发布或报名。");
        return;
      }
      if (normalizedText === "取消" && pending) {
        createAssistantMessage("请点击下方“取消”按钮结束本次待执行动作。为避免误操作，输入文字不会直接取消真实发布或报名。");
        return;
      }
      if (isShowDraftIntent(normalizedText)) {
        maybeAppendDraftPreview();
        return;
      }
      if (isPendingInspectIntent(normalizedText)) {
        createAssistantMessage(buildPendingDescription(pending));
        return;
      }
      if (handleGenericPendingInput(normalizedText)) {
        return;
      }
      if (handleDraftInput(normalizedText)) {
        return;
      }
      const clarification = createDefaultToolClarification(normalizedText);
      if (clarification) {
        createChoiceMessage(clarification.question, clarification.options);
        return;
      }
      if (isSuggestionIntent(normalizedText)) {
        startSuggestionFlow(normalizedText);
        return;
      }
      if (isActivityIntent(normalizedText)) {
        startActivityFlow(normalizedText);
        return;
      }
      if (isActivityRegisterIntent(normalizedText)) {
        await startActivityRegisterFlow(normalizedText);
        return;
      }
      if (isMutualAidIntent(normalizedText)) {
        startMutualAidFlow(normalizedText);
        return;
      }
      if (isActivityListIntent(normalizedText)) {
        const cid = await ensureConversation();
        createAssistantMessage("我将为你查询最新可见活动并返回前 5 条。");
        try {
          const req = await requestAgentAction({ actionCode: "activity.list_visible", payload: { page: 1, pageSize: 5 }, reason: normalizedText, conversationId: cid }, `list_${Date.now()}`);
          if (req.executionToken) {
            const ex = await executeAgentAction(req.actionRequestId, req.executionToken, `list_${Date.now()}`);
            const publicActivities = Array.isArray((ex.result as any)?.publicActivities) ? (ex.result as any).publicActivities : [];
            const lines = publicActivities.slice(0, 5).map((item: any, index: number) => activityListItemPreview(item, index));
            const registerActions = publicActivities
              .slice(0, 5)
              .map((item: any, index: number) => {
                const activityId = Number(item?.id || item?.activityId || 0);
                if (activityId <= 0) return null;
                const title = normalizeDisplayText(item?.title || item?.name || `活动${index + 1}`);
                return { label: `报名：${title}`, action: `prepare_register_activity:${activityId}` };
              })
              .filter(Boolean) as MessageAction[];
            createAssistantMessage(
              lines.length > 0 ? `我查到以下活动：
${lines.join("\n")}

如果你要报名，可以直接点下方对应按钮。` : "暂未查到合适的公开活动。",
              registerActions.length > 0 ? registerActions : undefined
            );
          } else {
            createAssistantMessage(`查询动作已创建（#${req.actionRequestId}），当前状态：${req.status || "pending_approval"}。`);
          }
          await refreshConversations(cid);
        } catch (e) {
          createAssistantMessage(`查询失败：${normalizeDisplayText(e instanceof Error ? e.message : e, "请稍后重试")}`);
        }
        return;
      }

      const cid = await ensureConversation();
      await askBackend(normalizedText, cid);
    } finally {
      inflightRef.current[inflightKey] = false;
    }
  }, [
    activeConversationId,
    appendMessage,
    askBackend,
    createAssistantMessage,
    createChoiceMessage,
    ensureConversation,
    executePending,
    handleActionClick,
    handleDraftInput,
    handleGenericPendingInput,
    inputValue,
    maybeAppendDraftPreview,
    pending,
    pendingClarification,
    refreshConversations,
    startActivityRegisterFlow,
    startActivityFlow,
    startMutualAidFlow,
    startSuggestionFlow
  ]);

  const renderHero = messages.length === 0 && !loading;


  return {
    models, setModels, selectedModel, setSelectedModel, showModelList, setShowModelList,
    conversations, setConversations, activeConversationId, setActiveConversationId,
    activeConversationIdRef,
    messages, setMessages, inputValue, setInputValue, loading, bootstrapping,
    showHistory, setShowHistory, showSettings, setShowSettings,
    actingRole, actingUserName, userBranches,
    draft, setDraft, pending, setPending,
    metricsText,
    agentGrants, agentCatalog, readOnlyCatalogActions, operableCatalogActions,
    selectedActionsByGrant, permissionLoading, permissionSavingId, permissionNotice, permissionError,
    permissionSummary,
    selectedModelLabel,
    createAssistantMessage, createFreshConversation, ensureConversation,
    refreshConversations, loadConversationMessages,
    executePending,
    maybeAppendDraftPreview,
    startSuggestionFlow, startActivityFlow, startMutualAidFlow, startActivityRegisterFlow,
    handleDraftInput, askBackend, handleMessageFeedback, handleActionClick, handleSend,
    openSettings, jumpByAction, bootstrapPage,
    loadAgentPermissions, getGrantSelectedActions, isGrantActionSelected, toggleGrantAction, saveGrantScope,
    isGrantActive, actionRiskText, actionDisplayName, actionCapabilityFor,
    normalizeConversation,
    bottomRef,
  };
}
