import { useCallback, useEffect, useMemo, useRef, useState, type MutableRefObject } from 'react';
import { CopyOutlined, DeleteOutlined, EditOutlined, ReloadOutlined } from '@ant-design/icons';
import type { BubbleItemType, PromptsItemType } from '@ant-design/x';
import { useXChat, type MessageInfo } from '@ant-design/x-sdk';
import { App } from 'antd';
import type { UploadFile, UploadProps } from 'antd';
import { message } from '../../ui/app-message';
import { useSettingsStore } from '../../store/settings/settingsStore';
import { aiChatApi } from './api';
import { AiMessageContent } from './AiMessageContent';
import { mapHistoryMessage } from './message-mappers';
import { GongxueAiChatProvider } from './provider';
import { mergeArtifactIntoMessage } from './uiArtifacts';
import {
  emptyAssistant,
  MessageHoverActions,
  resolveUserMessageId,
  toConversationData,
  toUploadFile,
  type ConversationData,
} from './AiChatDrawer.helpers';
import type {
  AiAttachment,
  AiChatInput,
  AiChatMessage,
  AiChatMessageStatus,
  AiFormSchema,
  AiReviewSchema,
  AiReviewSection,
  AiReviewSectionType,
  AiSkill,
  AiSseChunk,
} from './types';

interface UseAiChatMessageActionsParams {
  activeConversation: ConversationData | undefined;
  activeId: number | null;
  provider: GongxueAiChatProvider | undefined;
  requestAbortRef: MutableRefObject<Map<number, () => void>>;
  markConversationRunning: (conversationId: number) => void;
  addConversation: (conversation: ConversationData, placement?: 'prepend' | 'append') => boolean;
  setActiveConversationKey: (key: string) => boolean;
  refreshConversations: () => Promise<void>;
  skills: AiSkill[];
  lockedSkill: AiSkill | undefined;
  setImportWizardRunId: (runId: string | null) => void;
}

export function useAiChatMessageActions({
  activeConversation,
  activeId,
  provider,
  requestAbortRef,
  markConversationRunning,
  addConversation,
  setActiveConversationKey,
  refreshConversations,
  skills,
  lockedSkill,
  setImportWizardRunId,
}: UseAiChatMessageActionsParams) {
  const { modal } = App.useApp();
  const [input, setInput] = useState('');
  const [attachments, setAttachments] = useState<AiAttachment[]>([]);
  const [editingMessageId, setEditingMessageId] = useState<number | string | null>(null);
  const deepThinking = useSettingsStore((state) => state.aiChat.deepThinking);
  const setDeepThinking = useSettingsStore((state) => state.setAiChatDeepThinking);
  const requestingRef = useRef(false);
  const abortRef = useRef<() => void>(() => undefined);
  const attachmentsRef = useRef<AiAttachment[]>([]);
  const pendingDraftConversationIdRef = useRef<number | null>(null);
  const messagesRef = useRef<MessageInfo<AiChatMessage>[]>([]);

  const {
    messages,
    onRequest,
    onReload,
    isRequesting,
    abort,
    setMessage,
    removeMessage,
    queueRequest,
  } = useXChat<AiChatMessage, AiChatMessage, AiChatInput, AiSseChunk>({
    provider,
    conversationKey: activeConversation?.key || 'no-conversation',
    defaultMessages: async () => {
      if (!activeId) return [];
      const page = await aiChatApi.listMessages(activeId);
      return page.items.map(mapHistoryMessage);
    },
    requestPlaceholder: emptyAssistant(),
    requestFallback: (
      params: Partial<AiChatInput>,
      { error, messageInfo }: { error: Error; messageInfo: MessageInfo<AiChatMessage> },
    ) => ({
      ...(params.reloadMessage || messageInfo?.message || emptyAssistant()),
      error: error.name === 'AbortError' ? undefined : '连接中断，请稍后重试',
      cancelled: error.name === 'AbortError',
    }),
  });

  useEffect(() => {
    if (!provider) return;
    provider.onExternalReview = (messageId, review) => {
      setMessage(messageId, (info) => ({
        message: {
          ...info.message,
          reviews: (info.message.reviews ?? []).some((item) => item.id === review.id)
            ? (info.message.reviews ?? []).map((item) => (item.id === review.id ? review : item))
            : [...(info.message.reviews ?? []), review],
        },
      }));
    };
    provider.onExternalArtifact = (messageId, artifact) => {
      setMessage(messageId, (info) => ({
        message: mergeArtifactIntoMessage(info.message, artifact),
      }));
    };
  }, [provider, setMessage]);

  requestingRef.current = isRequesting;
  abortRef.current = abort;
  attachmentsRef.current = attachments;
  messagesRef.current = messages;

  const stopRequest = useCallback(() => {
    if (requestingRef.current) abortRef.current();
  }, []);

  const requestWithStatus = useCallback(
    (params: AiChatInput) => {
      if (!activeId || !provider) return;
      requestAbortRef.current.set(activeId, () => provider.request.abort());
      markConversationRunning(activeId);
      onRequest(params);
    },
    [activeId, markConversationRunning, onRequest, provider, requestAbortRef],
  );

  const reloadWithStatus = useCallback(
    (messageInfo: MessageInfo<AiChatMessage>) => {
      if (!activeId || !provider || typeof messageInfo.message.id !== 'number') return;
      requestAbortRef.current.set(activeId, () => provider.request.abort());
      markConversationRunning(activeId);
      onReload(messageInfo.id, {
        message: '',
        attachmentIds: [],
        skillKey: activeConversation?.lockedSkillKey ?? null,
        clientRequestId: crypto.randomUUID(),
        reasoningEffort: deepThinking ? 'high' : null,
        regenerateMessageId: messageInfo.message.id,
        reloadMessage: messageInfo.message,
      });
    },
    [
      activeConversation?.lockedSkillKey,
      activeId,
      deepThinking,
      markConversationRunning,
      onReload,
      provider,
      requestAbortRef,
    ],
  );

  const discardPendingAttachments = useCallback(() => {
    const pending = attachmentsRef.current;
    attachmentsRef.current = [];
    setAttachments([]);
    for (const attachment of pending) {
      void aiChatApi.deleteAttachment(attachment.id).catch(() => undefined);
    }
  }, []);

  const submit = useCallback(
    (value: string) => {
      const text = value.trim();
      if (!text || isRequesting) return;
      const submittedAttachments = attachmentsRef.current;
      attachmentsRef.current = [];
      setAttachments([]);
      setInput('');
      const params: AiChatInput = {
        message: text,
        attachmentIds: submittedAttachments.map((item) => item.id),
        skillKey: activeConversation?.lockedSkillKey ?? null,
        clientRequestId: crypto.randomUUID(),
        reasoningEffort: deepThinking ? 'high' : null,
        localAttachments: submittedAttachments,
      };
      if (activeId != null) {
        requestWithStatus(params);
        return;
      }
      // 草稿态：先创建 session，再发送第一条消息
      void (async () => {
        try {
          const created = toConversationData(await aiChatApi.createConversation());
          addConversation(created, 'prepend');
          pendingDraftConversationIdRef.current = created.id;
          markConversationRunning(created.id);
          // 通过 XChat 的队列机制发送：等会话 key 切换并加载完成后再真正发出，
          // 保证消息写入新会话的 store，界面能正常显示对话内容。
          queueRequest(created.key, params);
          setActiveConversationKey(created.key);
        } catch {
          message.error('创建会话失败，请重试');
          attachmentsRef.current = submittedAttachments;
          setAttachments(submittedAttachments);
          setInput(text);
        }
      })();
    },
    [
      activeConversation?.lockedSkillKey,
      activeId,
      addConversation,
      deepThinking,
      isRequesting,
      markConversationRunning,
      queueRequest,
      requestWithStatus,
      setActiveConversationKey,
    ],
  );

  // 草稿 session 创建完成、provider 就绪后注册中止句柄
  useEffect(() => {
    if (activeId == null || !provider) return;
    if (activeId !== pendingDraftConversationIdRef.current) return;
    pendingDraftConversationIdRef.current = null;
    requestAbortRef.current.set(activeId, () => provider.request.abort());
  }, [activeId, provider, requestAbortRef]);

  const reloadMessage = useCallback(
    (messageInfo: MessageInfo<AiChatMessage>) => {
      reloadWithStatus(messageInfo);
    },
    [reloadWithStatus],
  );

  const copyMessage = useCallback((message: AiChatMessage) => {
    if (!message.content) return;
    void navigator.clipboard.writeText(message.content);
  }, []);

  const confirmDeleteMessage = useCallback(
    (messageInfo: MessageInfo<AiChatMessage>) => {
      if (!activeId || isRequesting) return;
      const messageId = resolveUserMessageId(messageInfo, messagesRef.current);
      if (messageId == null) return;
      const scopeLabel =
        messageInfo.message.role === 'user' ? '这条消息及其 AI 回答' : '这条 AI 回答';
      modal.confirm({
        title: '删除消息',
        content: `将删除${scopeLabel}，此操作不可恢复。`,
        okText: '删除',
        okButtonProps: { danger: true },
        cancelText: '取消',
        onOk: async () => {
          try {
            const result = await aiChatApi.deleteMessage(activeId, messageId);
            const storeIds = new Map<number, number | string>();
            for (const item of messagesRef.current) {
              if (typeof item.message.id === 'number') {
                storeIds.set(item.message.id, item.id);
              }
            }
            // 当前会话内新发送的用户消息没有服务端 ID，但可映射到本地 msg_N key
            storeIds.set(messageId, messageInfo.id);
            for (const id of result.deletedIds) removeMessage(storeIds.get(id) ?? id);
            void refreshConversations();
          } catch (error) {
            console.error('删除消息失败', error);
            message.error('删除消息失败');
          }
        },
      });
    },
    [activeId, isRequesting, refreshConversations, removeMessage, modal],
  );

  const confirmEditMessage = useCallback(
    (messageInfo: MessageInfo<AiChatMessage>, value: string) => {
      if (!activeId) return;
      const content = value.trim();
      if (!content) {
        message.warning('消息内容不能为空');
        return;
      }
      const messageId = resolveUserMessageId(messageInfo, messagesRef.current);
      if (messageId == null) {
        message.warning('消息尚未同步，请稍后重试');
        return;
      }
      setEditingMessageId(null);
      if (content === messageInfo.message.content) return;

      // 编辑旧消息会删除其后的全部消息并重新生成，需先告知用户
      const index = messagesRef.current.findIndex((item) => item.id === messageInfo.id);
      const followingCount = index >= 0 ? messagesRef.current.length - index - 1 : 0;
      const doEdit = () => {
        setMessage(messageInfo.id, (info) => ({
          message: {
            ...info.message,
            content,
            metadata: { ...info.message.metadata, edited: true },
          },
        }));
        if (index >= 0) {
          for (const item of messagesRef.current.slice(index + 1)) removeMessage(item.id);
        }
        requestWithStatus({
          message: content,
          attachmentIds: [],
          skillKey: activeConversation?.lockedSkillKey ?? null,
          clientRequestId: crypto.randomUUID(),
          reasoningEffort: deepThinking ? 'high' : null,
          editMessageId: messageId,
        });
      };
      if (followingCount > 0) {
        modal.confirm({
          title: '编辑消息将删除后续内容',
          content: `编辑这条消息会删除其后的 ${followingCount} 条消息并重新生成回答，此操作不可恢复。`,
          okText: '继续编辑',
          cancelText: '取消',
          onOk: doEdit,
        });
        return;
      }
      doEdit();
    },
    [
      activeConversation?.lockedSkillKey,
      activeId,
      deepThinking,
      modal,
      removeMessage,
      requestWithStatus,
      setMessage,
    ],
  );

  const submitForm = useCallback(
    async (form: AiFormSchema, values: Record<string, unknown>): Promise<void> => {
      if (!activeId) throw new Error('当前会话不可用，请稍后重试');
      if (isRequesting) throw new Error('请等待当前 AI 回复完成后再提交表单');
      requestWithStatus({
        message: '表单提交',
        attachmentIds: [],
        skillKey: activeConversation?.lockedSkillKey ?? null,
        clientRequestId: crypto.randomUUID(),
        reasoningEffort: deepThinking ? 'high' : null,
        formSubmission: { formId: form.id, values, formTitle: form.title },
      });
    },
    [activeConversation?.lockedSkillKey, activeId, deepThinking, isRequesting, requestWithStatus],
  );

  const submitReview = useCallback(
    async (reviewId: string, reviewTitle?: string): Promise<void> => {
      if (!activeId) throw new Error('当前会话不可用，请稍后重试');
      if (isRequesting) throw new Error('请等待当前 AI 回复完成后再确认导入');
      requestWithStatus({
        message: '确认批量导入',
        attachmentIds: [],
        skillKey: activeConversation?.lockedSkillKey ?? null,
        clientRequestId: crypto.randomUUID(),
        reasoningEffort: deepThinking ? 'high' : null,
        reviewSubmission: { reviewId, reviewTitle },
      });
    },
    [activeConversation?.lockedSkillKey, activeId, deepThinking, isRequesting, requestWithStatus],
  );

  const confirmReviewStep = useCallback(
    async (
      messageId: number | undefined,
      reviewId: string,
      sectionKey: AiReviewSection['key'],
    ): Promise<AiReviewSchema> => {
      const updated = await aiChatApi.confirmReviewStep(reviewId, sectionKey);
      const apply = (review: AiReviewSchema) => {
        if (provider?.onExternalReview && typeof messageId === 'number') {
          provider.onExternalReview(messageId, review);
        } else if (typeof messageId === 'number') {
          setMessage(messageId, (info) => {
            const reviews = info.message.reviews ?? [];
            const exists = reviews.some((item) => item.id === review.id);
            return {
              message: {
                ...info.message,
                reviews: exists
                  ? reviews.map((item) => (item.id === review.id ? review : item))
                  : [...reviews, review],
              },
            };
          });
        }
      };
      apply(updated);
      return updated;
    },
    [provider, setMessage],
  );

  const confirmReviewGroup = useCallback(
    async (
      messageId: number | undefined,
      reviewId: string,
      type: AiReviewSectionType,
    ): Promise<AiReviewSchema> => {
      const updated = await aiChatApi.confirmReviewGroup(reviewId, type);
      if (provider?.onExternalReview && typeof messageId === 'number') {
        provider.onExternalReview(messageId, updated);
      } else if (typeof messageId === 'number') {
        setMessage(messageId, (info) => {
          const reviews = info.message.reviews ?? [];
          const exists = reviews.some((item) => item.id === updated.id);
          return {
            message: {
              ...info.message,
              reviews: exists
                ? reviews.map((item) => (item.id === updated.id ? updated : item))
                : [...reviews, updated],
            },
          };
        });
      }
      return updated;
    },
    [provider, setMessage],
  );

  const customUpload = useCallback<NonNullable<UploadProps['customRequest']>>(async (options) => {
    const file = options.file as File;
    if (attachmentsRef.current.length >= 5) {
      const error = new Error('每条消息最多添加 5 个附件');
      options.onError?.(error);
      message.warning(error.message);
      return;
    }
    try {
      const uploaded = await aiChatApi.uploadAttachment(file, (percent) => {
        options.onProgress?.({ percent });
      });
      setAttachments((items) => [...items, uploaded]);
      options.onSuccess?.(uploaded, file);
    } catch (error) {
      options.onError?.(error instanceof Error ? error : new Error('附件上传失败'));
      message.error('附件上传失败');
    }
  }, []);

  const removeAttachment = useCallback(async (file: UploadFile<AiAttachment>) => {
    const attachment = file.response;
    if (!attachment) return true;
    try {
      await aiChatApi.deleteAttachment(attachment.id);
      setAttachments((items) => items.filter((item) => item.id !== attachment.id));
      return true;
    } catch {
      message.error('删除附件失败');
      return false;
    }
  }, []);

  const uploadItems = useMemo(() => attachments.map(toUploadFile), [attachments]);
  const promptItems = useMemo<PromptsItemType[]>(
    () =>
      (lockedSkill ? [lockedSkill] : skills)
        .flatMap((skill) =>
          skill.examples.slice(0, lockedSkill ? 4 : 1).map((example) => ({ skill, example })),
        )
        .slice(0, 5)
        .map(({ skill, example }) => ({
          key: `${skill.key}-${example}`,
          label: example,
          description: skill.name,
        })),
    [lockedSkill, skills],
  );

  const bubbleItems = useMemo<BubbleItemType[]>(
    () =>
      messages.map((info) => ({
        key: info.id,
        role: info.message.role === 'assistant' ? 'assistant' : 'user',
        status: info.status,
        content: info.message,
        extra:
          info.status !== 'loading' && info.status !== 'updating' && !isRequesting ? (
            info.message.role === 'user' ? (
              editingMessageId === info.id ? undefined : (
                <MessageHoverActions
                  items={[
                    {
                      key: 'copy',
                      title: '复制',
                      icon: <CopyOutlined />,
                      onClick: () => copyMessage(info.message),
                    },
                    {
                      key: 'edit',
                      title: '编辑',
                      icon: <EditOutlined />,
                      onClick: () => setEditingMessageId(info.id),
                    },
                    {
                      key: 'delete',
                      title: '删除',
                      icon: <DeleteOutlined />,
                      danger: true,
                      onClick: () => void confirmDeleteMessage(info),
                    },
                  ]}
                />
              )
            ) : (
              <MessageHoverActions
                items={[
                  {
                    key: 'copy',
                    title: '复制',
                    icon: <CopyOutlined />,
                    onClick: () => copyMessage(info.message),
                  },
                  {
                    key: 'reload',
                    title: '重新生成',
                    icon: <ReloadOutlined />,
                    onClick: () => reloadMessage(info),
                  },
                ]}
              />
            )
          ) : undefined,
        contentRender: (content: AiChatMessage) => (
          <AiMessageContent
            message={content}
            status={info.status as AiChatMessageStatus}
            editing={content.role === 'user' && editingMessageId === info.id}
            onEditConfirm={
              content.role === 'user' ? (value) => confirmEditMessage(info, value) : undefined
            }
            onEditCancel={content.role === 'user' ? () => setEditingMessageId(null) : undefined}
            onSubmitForm={submitForm}
            onSubmitReview={submitReview}
            onConfirmReviewStep={confirmReviewStep}
            onConfirmReviewGroup={confirmReviewGroup}
            onOpenImportWizard={setImportWizardRunId}
          />
        ),
      })),
    [
      copyMessage,
      confirmDeleteMessage,
      confirmEditMessage,
      confirmReviewGroup,
      confirmReviewStep,
      editingMessageId,
      isRequesting,
      messages,
      reloadMessage,
      setImportWizardRunId,
      submitForm,
      submitReview,
    ],
  );

  return {
    input,
    setInput,
    attachments,
    setAttachments,
    editingMessageId,
    setEditingMessageId,
    deepThinking,
    setDeepThinking,
    isRequesting,
    messages,
    stopRequest,
    submit,
    reloadMessage,
    copyMessage,
    confirmDeleteMessage,
    confirmEditMessage,
    submitForm,
    submitReview,
    confirmReviewStep,
    confirmReviewGroup,
    customUpload,
    removeAttachment,
    discardPendingAttachments,
    uploadItems,
    promptItems,
    bubbleItems,
  };
}
