import { ChangeEvent, useEffect, useRef, useState, type ClipboardEvent } from 'react';
import { useNetworkStatus } from '../hooks/useNetworkStatus';
import { openAvatarPicker } from '../utils/userAvatar';
import { CHAT_SKILL_OPTIONS, filterChatSkills } from '../utils/chatSkills';
import { AvatarPicker } from './AvatarPicker';
import { ChatSkillPicker } from './ChatSkillPicker';
import { MessageList } from './MessageList';
import { PageSaveDialog } from './PageSaveDialog';
import { VoiceInputButton } from './VoiceInputButton';
import type { CapabilityMap, ChatState, Message, PortalUser, Session, ToolConfirmation } from '../types';
import type { MindSpaceSaveCategory } from '../types';

const MAX_PENDING_IMAGES = 6;

type PendingChatImage = {
  id: string;
  url: string;
  local: boolean;
};

function FileIcon() {
  return (
    <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
      <path
        fill="currentColor"
        d="M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7zm0 0v5h5"
      />
      <path fill="currentColor" d="M9 13h6v1.5H9zm0 3h6v1.5H9zm0-6h3v1.5H9z" opacity=".72" />
    </svg>
  );
}

export function ChatPanel({
  variant,
  user,
  messages,
  chatState,
  pendingTool,
  session,
  capabilities,
  grantedSkills,
  onSubmit,
  onUploadImage,
  onStop,
  onApproveTool,
  onPageSaved,
  onClose,
}: {
  variant: 'full' | 'compact';
  user?: PortalUser | null;
  messages: Message[];
  chatState: ChatState;
  pendingTool: ToolConfirmation | null;
  session: Session | null;
  capabilities?: CapabilityMap;
  grantedSkills?: string[];
  onSubmit: (text: string, imageUrls?: string[]) => void | Promise<void>;
  onUploadImage?: (file: File) => Promise<string>;
  onStop: () => void | Promise<void>;
  onApproveTool: (allow: boolean) => void | Promise<void>;
  onPageSaved?: (result: {
    kind: 'page' | 'category';
    pageId?: string;
    categoryCode?: MindSpaceSaveCategory;
  }) => void;
  onClose?: () => void;
}) {
  const online = useNetworkStatus();
  const [input, setInput] = useState('');
  const [voiceRecording, setVoiceRecording] = useState(false);
  const [pageSource, setPageSource] = useState<Message | null>(null);
  const [voiceNotice, setVoiceNotice] = useState<string | null>(null);
  const [pendingImages, setPendingImages] = useState<PendingChatImage[]>([]);
  const [uploadingImage, setUploadingImage] = useState(false);
  const [imageError, setImageError] = useState<string | null>(null);
  const [voiceStopSignal, setVoiceStopSignal] = useState(0);
  const imageInputRef = useRef<HTMLInputElement>(null);
  const bottomRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef(input);
  const voiceBaseRef = useRef('');
  const pendingImagesRef = useRef(pendingImages);
  const suppressVoiceUpdateRef = useRef(false);

  inputRef.current = input;
  pendingImagesRef.current = pendingImages;

  useEffect(() => {
    return () => {
      pendingImagesRef.current.forEach((item) => {
        if (item.local) URL.revokeObjectURL(item.url);
      });
    };
  }, []);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages, chatState, pendingTool]);

  const busy = chatState === 'streaming' || chatState === 'loading' || chatState === 'connecting';
  const offlineBlocked = !online;
  const publishSkillName = user?.publishSkillName ?? 'static-page-publish';
  const hasPublishSkill = grantedSkills?.includes(publishSkillName) ?? false;
  const canPublish = Boolean(capabilities?.static_publish) || hasPublishSkill;
  const chatSkills = filterChatSkills(CHAT_SKILL_OPTIONS, { grantedSkills, canPublish });
  const compact = variant === 'compact';

  const placeholder = offlineBlocked
    ? '网络断开，恢复连接后可继续输入'
    : chatState === 'connecting'
      ? '正在连接会话…'
      : compact
        ? '在这里继续和 Agent 对话…'
        : '输入任务，例如：列出当前目录文件';

  const mergeVoiceText = (spoken: string) => {
    const base = voiceBaseRef.current.trimEnd();
    const chunk = spoken.trim();
    if (!chunk) return base;
    return base ? `${base} ${chunk}` : chunk;
  };

  const handleVoiceStart = () => {
    suppressVoiceUpdateRef.current = false;
    voiceBaseRef.current = inputRef.current;
    setVoiceNotice(null);
  };

  const handleVoiceLiveTranscript = (text: string) => {
    if (suppressVoiceUpdateRef.current) return;
    setInput(mergeVoiceText(text));
  };

  const handleVoiceTranscript = (text: string) => {
    if (suppressVoiceUpdateRef.current) return;
    setInput(mergeVoiceText(text));
    setVoiceNotice('已识别，可编辑后发送');
  };

  const handleVoiceComplete = () => {
    setVoiceNotice('已识别，可编辑后发送');
  };

  const voiceDisabled = busy || !!pendingTool || chatState === 'error' || offlineBlocked;
  const canSubmit = input.trim() || pendingImages.length > 0;
  const imageAttachmentDisabled = !onUploadImage || busy || uploadingImage || offlineBlocked || !!pendingTool;

  const revokePendingImage = (item: PendingChatImage) => {
    if (item.local) URL.revokeObjectURL(item.url);
  };

  const handleSubmit = async () => {
    const trimmed = input.trim();
    if ((!trimmed && pendingImages.length === 0) || voiceDisabled) return;
    if (uploadingImage || pendingImages.some((item) => item.local)) {
      setImageError('图片仍在上传中，请稍候再发送');
      return;
    }
    const imagesToSend = pendingImages.map((item) => item.url);
    const sentImages = [...pendingImages];

    suppressVoiceUpdateRef.current = true;
    if (voiceRecording) {
      setVoiceStopSignal((value) => value + 1);
    }
    setInput('');
    voiceBaseRef.current = '';
    setVoiceNotice(null);
    setImageError(null);
    try {
      await onSubmit(trimmed, imagesToSend);
      sentImages.forEach(revokePendingImage);
      setPendingImages([]);
    } catch (err) {
      suppressVoiceUpdateRef.current = false;
      setInput(trimmed);
      setPendingImages(sentImages);
      setImageError(err instanceof Error ? err.message : '发送失败，请重试');
      return;
    }
    suppressVoiceUpdateRef.current = false;
  };

  const handleImageInputChange = async (event: ChangeEvent<HTMLInputElement>) => {
    if (!onUploadImage) return;
    const selected = Array.from(event.currentTarget.files ?? []);
    event.currentTarget.value = '';

    if (selected.length === 0) return;
    const imageFiles = selected.filter((file) => file.type.startsWith('image/'));
    if (imageFiles.length === 0) {
      setImageError('请上传图片文件');
      return;
    }

    const remaining = MAX_PENDING_IMAGES - pendingImages.length;
    if (remaining <= 0) {
      setImageError(`最多支持一次性附加 ${MAX_PENDING_IMAGES} 张图片`);
      return;
    }

    const toUpload = imageFiles.slice(0, remaining);
    if (imageFiles.length > remaining) {
      setImageError(`最多支持 ${MAX_PENDING_IMAGES} 张图片，本次仅上传前 ${remaining} 张`);
    } else {
      setImageError(null);
    }

    const placeholders = toUpload.map((file, index) => ({
      id: `${Date.now()}-${index}-${file.name}`,
      url: URL.createObjectURL(file),
      local: true,
    }));
    setPendingImages((prev) => [...prev, ...placeholders]);

    setUploadingImage(true);
    try {
      const uploaded = await Promise.all(toUpload.map((file) => onUploadImage(file)));
      const placeholderIds = placeholders.map((item) => item.id);
      setPendingImages((prev) => {
        const next = prev.map((item) => {
          const index = placeholderIds.indexOf(item.id);
          if (index < 0 || !uploaded[index]) return item;
          revokePendingImage(item);
          return { ...item, url: uploaded[index], local: false };
        });
        const seen = new Set<string>();
        return next.filter((item) => {
          if (seen.has(item.url)) return false;
          seen.add(item.url);
          return true;
        });
      });
    } catch (err) {
      setPendingImages((prev) => {
        const removeIds = new Set(placeholders.map((item) => item.id));
        prev.forEach((item) => {
          if (removeIds.has(item.id)) revokePendingImage(item);
        });
        return prev.filter((item) => !removeIds.has(item.id));
      });
      setImageError(err instanceof Error ? err.message : '图片上传失败');
    } finally {
      setUploadingImage(false);
    }
  };

  const handlePaste = async (event: ClipboardEvent<HTMLTextAreaElement>) => {
    if (!onUploadImage || imageAttachmentDisabled) return;
    const items = Array.from(event.clipboardData.items ?? []);
    const imageItems = items.filter((item) => item.kind === 'file' && item.type.startsWith('image/'));
    if (imageItems.length === 0) return;

    event.preventDefault();

    const files = imageItems
      .map((item) => item.getAsFile())
      .filter((file): file is File => Boolean(file));
    if (files.length === 0) return;

    const remaining = MAX_PENDING_IMAGES - pendingImages.length;
    if (remaining <= 0) {
      setImageError(`最多支持一次性附加 ${MAX_PENDING_IMAGES} 张图片`);
      return;
    }

    const toUpload = files.slice(0, remaining);
    if (files.length > remaining) {
      setImageError(`最多支持 ${MAX_PENDING_IMAGES} 张图片，本次仅粘贴前 ${remaining} 张`);
    } else {
      setImageError(null);
    }

    const placeholders = toUpload.map((file, index) => ({
      id: `${Date.now()}-${index}-${file.name || 'pasted-image'}`,
      url: URL.createObjectURL(file),
      local: true,
    }));
    setPendingImages((prev) => [...prev, ...placeholders]);

    setUploadingImage(true);
    try {
      const uploaded = await Promise.all(toUpload.map((file) => onUploadImage(file)));
      const placeholderIds = placeholders.map((item) => item.id);
      setPendingImages((prev) => {
        const next = prev.map((item) => {
          const index = placeholderIds.indexOf(item.id);
          if (index < 0 || !uploaded[index]) return item;
          revokePendingImage(item);
          return { ...item, url: uploaded[index], local: false };
        });
        const seen = new Set<string>();
        return next.filter((item) => {
          if (seen.has(item.url)) return false;
          seen.add(item.url);
          return true;
        });
      });
    } catch (err) {
      setPendingImages((prev) => {
        const removeIds = new Set(placeholders.map((item) => item.id));
        prev.forEach((item) => {
          if (removeIds.has(item.id)) revokePendingImage(item);
        });
        return prev.filter((item) => !removeIds.has(item.id));
      });
      setImageError(err instanceof Error ? err.message : '图片上传失败');
    } finally {
      setUploadingImage(false);
    }
  };

  const handleRemoveImage = (idToRemove: string) => {
    setPendingImages((prev) => {
      const target = prev.find((item) => item.id === idToRemove);
      if (target) revokePendingImage(target);
      return prev.filter((item) => item.id !== idToRemove);
    });
  };

  return (
    <>
      <main className={compact ? 'space-chat-panel-body' : 'main'}>
        <MessageList
          messages={messages}
          streaming={chatState === 'streaming'}
          onAvatarClick={compact ? undefined : openAvatarPicker}
          onSaveAsPage={(message) => setPageSource(message)}
          publishUserId={user?.id}
          publishUsername={user?.username}
          compact={compact}
        />
        {!compact && (
          <div className="user-avatar-input-host" aria-hidden="true">
            <AvatarPicker variant="compact" />
          </div>
        )}
        <div ref={bottomRef} />
      </main>

      {pendingTool && (
        <div className={`tool-confirm${compact ? ' tool-confirm-compact' : ''}`}>
          <div className="tool-confirm-text">
            允许执行工具 <strong>{pendingTool.toolName}</strong>？
            {pendingTool.prompt && <p>{pendingTool.prompt}</p>}
          </div>
          <div className="tool-confirm-actions">
            <button type="button" onClick={() => void onApproveTool(true)}>
              允许
            </button>
            <button type="button" className="danger" onClick={() => void onApproveTool(false)}>
              拒绝
            </button>
          </div>
        </div>
      )}

      {pageSource?.id && session?.id && (
        <PageSaveDialog
          sessionId={session.id}
          messageId={pageSource.id}
          compact={compact}
          onClose={() => setPageSource(null)}
          onSaved={(result) => {
            setPageSource(null);
            onPageSaved?.(result);
          }}
        />
      )}

      <footer className={compact ? 'space-chat-panel-footer' : 'footer'}>
        {voiceNotice && (
          <div className="voice-notice" role="status">
            {voiceNotice}
          </div>
        )}
        {(pendingImages.length > 0 || imageError) && (
          <div className="chat-image-attachments">
            {imageError && <div className="chat-image-upload-error">{imageError}</div>}
            {pendingImages.length > 0 && (
              <div className="chat-image-attachment-grid">
                {pendingImages.map((item, index) => (
                  <div key={item.id} className="chat-image-attachment-item">
                    <a href={item.url} target="_blank" rel="noreferrer">
                      <img src={item.url} alt={`上传图片 ${index + 1}`} className="chat-image-attachment-thumb" />
                    </a>
                    <button
                      type="button"
                      className="chat-image-attachment-remove"
                      aria-label="移除图片"
                      onClick={() => handleRemoveImage(item.id)}
                    >
                      ×
                    </button>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
        <div className="chat-input-row">
          <div className="chat-input-shell">
            {onUploadImage && (
              <>
                <button
                  type="button"
                  className="chat-image-upload-trigger"
                  title="上传图片"
                  disabled={imageAttachmentDisabled}
                  onClick={() => imageInputRef.current?.click()}
                >
                  <FileIcon />
                </button>
                <input
                  ref={imageInputRef}
                  type="file"
                  accept="image/*"
                  multiple
                  hidden
                  onChange={handleImageInputChange}
                />
              </>
            )}
            <textarea
              className={`input chat-input-field${onUploadImage ? ' chat-input-field-with-image-upload' : ''}`}
              rows={1}
              placeholder={placeholder}
              value={input}
              disabled={voiceDisabled}
              readOnly={voiceRecording}
              onChange={(e) => setInput(e.target.value)}
              onPaste={handlePaste}
              onKeyDown={(e) => {
                if (e.key === 'Enter' && !e.shiftKey) {
                  e.preventDefault();
                  void handleSubmit();
                }
              }}
            />
            <VoiceInputButton
              disabled={voiceDisabled}
              onVoiceStart={handleVoiceStart}
              onLiveTranscript={handleVoiceLiveTranscript}
              onTranscript={handleVoiceTranscript}
              onVoiceComplete={handleVoiceComplete}
              onRecordingChange={setVoiceRecording}
              onError={setVoiceNotice}
              stopSignal={voiceStopSignal}
            />
          </div>
          {chatSkills.length > 0 && (
            <ChatSkillPicker
              skills={chatSkills}
              disabled={voiceDisabled}
              onSelect={onSubmit}
              onPrefill={setInput}
            />
          )}
          {chatState === 'streaming' ? (
            <button type="button" className="danger-btn chat-input-action" onClick={() => void onStop()}>
              停止
            </button>
          ) : (
            <button
              type="button"
              className="send-btn chat-input-action"
              disabled={!canSubmit || voiceDisabled || uploadingImage}
              onClick={() => void handleSubmit()}
            >
              发送
            </button>
          )}
        </div>
        {compact && onClose && (
          <button type="button" className="space-chat-panel-dismiss" onClick={onClose}>
            关闭
          </button>
        )}
      </footer>
    </>
  );
}
