import { ArrowUp, ChevronLeft, Clock3, Command, Download, Eye, FileText, Mic, Paperclip, Pencil, Plus, RefreshCw, RotateCcw, Square, Trash2, X } from 'lucide-react';
import type { Components } from 'react-markdown';
import type { ChangeEventHandler, KeyboardEventHandler, RefObject } from 'react';

import { LucentIconButton } from '../../../components/lucent/LucentIconButton';
import nanobotLogo from '../../../assets/nanobot-logo.png';
import type { ChatMessage } from '../../../types/bot';
import { normalizeAssistantMessageText, normalizeUserMessageText } from '../messageParser';
import { normalizeDashboardAttachmentPath } from '../shared/workspaceMarkdown';
import type { StagedSubmissionDraft } from '../types';
import { formatDateInputValue, workspaceFileAction } from '../utils';
import { DashboardConversationMessages } from './DashboardConversationMessages';
import './DashboardChatPanel.css';

interface DashboardChatPanelLabels {
  badReply: string;
  botDisabledHint: string;
  botStarting: string;
  botStopping: string;
  chatDisabled: string;
  close: string;
  controlCommandsHide: string;
  controlCommandsShow: string;
  copyPrompt: string;
  copyReply: string;
  deleteMessage: string;
  disabledPlaceholder: string;
  download: string;
  editPrompt: string;
  fileNotPreviewable: string;
  goodReply: string;
  inputPlaceholder: string;
  interrupt: string;
  noConversation: string;
  previewTitle: string;
  stagedSubmissionAttachmentCount: (count: number) => string;
  stagedSubmissionEmpty: string;
  stagedSubmissionRestore: string;
  stagedSubmissionRemove: string;
  quoteReply: string;
  quotedReplyLabel: string;
  send: string;
  thinking: string;
  uploadFile: string;
  uploadingFile: string;
  user: string;
  voiceStart: string;
  voiceStop: string;
  voiceTranscribing: string;
  you: string;
}

interface DashboardChatPanelProps {
  conversation: ChatMessage[];
  isZh: boolean;
  labels: DashboardChatPanelLabels;
  chatScrollRef: RefObject<HTMLDivElement | null>;
  onChatScroll: () => void;
  expandedProgressByKey: Record<string, boolean>;
  expandedUserByKey: Record<string, boolean>;
  deletingMessageIdMap: Record<number, boolean>;
  feedbackSavingByMessageId: Record<number, boolean>;
  markdownComponents: Components;
  workspaceDownloadExtensionSet: ReadonlySet<string>;
  onToggleProgressExpand: (key: string) => void;
  onToggleUserExpand: (key: string) => void;
  onEditUserPrompt: (text: string) => void;
  onCopyUserPrompt: (text: string) => Promise<void> | void;
  onDeleteConversationMessage: (message: ChatMessage) => Promise<void> | void;
  onOpenWorkspacePath: (path: string) => Promise<void> | void;
  onSubmitAssistantFeedback: (message: ChatMessage, direction: 'up' | 'down') => Promise<void> | void;
  onQuoteAssistantReply: (message: ChatMessage) => void;
  onCopyAssistantReply: (text: string) => Promise<void> | void;
  isThinking: boolean;
  canChat: boolean;
  isChatEnabled: boolean;
  speechEnabled: boolean;
  selectedBotEnabled: boolean;
  selectedBotControlState?: 'starting' | 'stopping' | 'enabling' | 'disabling';
  quotedReply: { text: string } | null;
  onClearQuotedReply: () => void;
  stagedSubmissions: StagedSubmissionDraft[];
  onRestoreStagedSubmission: (stagedSubmissionId: string) => void;
  onRemoveStagedSubmission: (stagedSubmissionId: string) => void;
  pendingAttachments: string[];
  onRemovePendingAttachment: (path: string) => void;
  attachmentUploadPercent: number | null;
  isUploadingAttachments: boolean;
  filePickerRef: RefObject<HTMLInputElement | null>;
  allowedAttachmentExtensions: string[];
  onPickAttachments: ChangeEventHandler<HTMLInputElement>;
  controlCommandPanelOpen: boolean;
  controlCommandPanelRef: RefObject<HTMLDivElement | null>;
  onToggleControlCommandPanel: () => void;
  activeControlCommand: string;
  canSendControlCommand: boolean;
  isInterrupting: boolean;
  onSendControlCommand: (command: '/restart' | '/new') => Promise<void> | void;
  onInterruptExecution: () => Promise<void> | void;
  chatDateTriggerRef: RefObject<HTMLButtonElement | null>;
  hasSelectedBot: boolean;
  chatDateJumping: boolean;
  onToggleChatDatePicker: () => void;
  chatDatePickerOpen: boolean;
  chatDatePanelPosition: { bottom: number; right: number } | null;
  chatDateValue: string;
  onChatDateValueChange: (value: string) => void;
  onCloseChatDatePicker: () => void;
  onJumpConversationToDate: () => Promise<void> | void;
  command: string;
  onCommandChange: (value: string) => void;
  composerTextareaRef: RefObject<HTMLTextAreaElement | null>;
  onComposerKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;
  isVoiceRecording: boolean;
  isVoiceTranscribing: boolean;
  isCompactMobile: boolean;
  voiceCountdown: number;
  onVoiceInput: () => Promise<void> | void;
  onTriggerPickAttachments: () => Promise<void> | void;
  submitActionMode: 'interrupt' | 'send' | 'stage';
  onSubmitAction: () => Promise<void> | void;
}

export function DashboardChatPanel({
  conversation,
  isZh,
  labels,
  chatScrollRef,
  onChatScroll,
  expandedProgressByKey,
  expandedUserByKey,
  deletingMessageIdMap,
  feedbackSavingByMessageId,
  markdownComponents,
  workspaceDownloadExtensionSet,
  onToggleProgressExpand,
  onToggleUserExpand,
  onEditUserPrompt,
  onCopyUserPrompt,
  onDeleteConversationMessage,
  onOpenWorkspacePath,
  onSubmitAssistantFeedback,
  onQuoteAssistantReply,
  onCopyAssistantReply,
  isThinking,
  canChat,
  isChatEnabled,
  speechEnabled,
  selectedBotEnabled,
  selectedBotControlState,
  quotedReply,
  onClearQuotedReply,
  stagedSubmissions,
  onRestoreStagedSubmission,
  onRemoveStagedSubmission,
  pendingAttachments,
  onRemovePendingAttachment,
  attachmentUploadPercent,
  isUploadingAttachments,
  filePickerRef,
  allowedAttachmentExtensions,
  onPickAttachments,
  controlCommandPanelOpen,
  controlCommandPanelRef,
  onToggleControlCommandPanel,
  activeControlCommand,
  canSendControlCommand,
  isInterrupting,
  onSendControlCommand,
  onInterruptExecution,
  chatDateTriggerRef,
  hasSelectedBot,
  chatDateJumping,
  onToggleChatDatePicker,
  chatDatePickerOpen,
  chatDatePanelPosition,
  chatDateValue,
  onChatDateValueChange,
  onCloseChatDatePicker,
  onJumpConversationToDate,
  command,
  onCommandChange,
  composerTextareaRef,
  onComposerKeyDown,
  isVoiceRecording,
  isVoiceTranscribing,
  isCompactMobile,
  voiceCountdown,
  onVoiceInput,
  onTriggerPickAttachments,
  submitActionMode,
  onSubmitAction,
}: DashboardChatPanelProps) {
  const showInterruptSubmitAction = submitActionMode === 'interrupt';
  const hasComposerDraft = Boolean(String(command || '').trim()) || pendingAttachments.length > 0 || Boolean(quotedReply);
  return (
    <div className={`ops-chat-frame ${isChatEnabled ? '' : 'is-disabled'}`}>
      <div className="ops-chat-scroll" ref={chatScrollRef} onScroll={onChatScroll}>
        {conversation.length === 0 ? (
          <div className="ops-chat-empty">
            {labels.noConversation}
          </div>
        ) : (
          <DashboardConversationMessages
            conversation={conversation}
            isZh={isZh}
            labels={{
              badReply: labels.badReply,
              copyPrompt: labels.copyPrompt,
              copyReply: labels.copyReply,
              deleteMessage: labels.deleteMessage,
              download: labels.download,
              editPrompt: labels.editPrompt,
              fileNotPreviewable: labels.fileNotPreviewable,
              goodReply: labels.goodReply,
              previewTitle: labels.previewTitle,
              quoteReply: labels.quoteReply,
              quotedReplyLabel: labels.quotedReplyLabel,
              user: labels.user,
              you: labels.you,
            }}
            expandedProgressByKey={expandedProgressByKey}
            expandedUserByKey={expandedUserByKey}
            deletingMessageIdMap={deletingMessageIdMap}
            feedbackSavingByMessageId={feedbackSavingByMessageId}
            markdownComponents={markdownComponents}
            workspaceDownloadExtensionSet={workspaceDownloadExtensionSet}
            onToggleProgressExpand={onToggleProgressExpand}
            onToggleUserExpand={onToggleUserExpand}
            onEditUserPrompt={onEditUserPrompt}
            onCopyUserPrompt={onCopyUserPrompt}
            onDeleteConversationMessage={onDeleteConversationMessage}
            onOpenWorkspacePath={onOpenWorkspacePath}
            onSubmitAssistantFeedback={onSubmitAssistantFeedback}
            onQuoteAssistantReply={onQuoteAssistantReply}
            onCopyAssistantReply={onCopyAssistantReply}
          />
        )}

        {isThinking ? (
          <div className="ops-chat-row is-assistant">
            <div className="ops-chat-item is-assistant">
              <div className="ops-avatar bot" title="Nanobot">
                <img src={nanobotLogo} alt="Nanobot" />
              </div>
              <div className="ops-thinking-bubble">
                <div className="ops-thinking-cloud">
                  <span className="dot" />
                  <span className="dot" />
                  <span className="dot" />
                </div>
                <div className="ops-thinking-text">{labels.thinking}</div>
              </div>
            </div>
          </div>
        ) : null}

        <div />
      </div>

      <div className="ops-chat-dock">
        {stagedSubmissions.length > 0 ? (
          <div className="ops-staged-submission-queue" aria-live="polite">
            {stagedSubmissions.map((stagedSubmission, index) => (
              <div key={stagedSubmission.id} className="ops-staged-submission-item">
                <span className="ops-staged-submission-index mono">{index + 1}</span>
                <div className="ops-staged-submission-body">
                  <div className="ops-staged-submission-text">
                    {normalizeUserMessageText(stagedSubmission.command) || labels.stagedSubmissionEmpty}
                  </div>
                  {(stagedSubmission.quotedReply || stagedSubmission.attachments.length > 0) ? (
                    <div className="ops-staged-submission-meta">
                      {stagedSubmission.quotedReply ? (
                        <span className="ops-staged-submission-pill">{labels.quotedReplyLabel}</span>
                      ) : null}
                      {stagedSubmission.attachments.length > 0 ? (
                        <span className="ops-staged-submission-pill">
                          {labels.stagedSubmissionAttachmentCount(stagedSubmission.attachments.length)}
                        </span>
                      ) : null}
                    </div>
                  ) : null}
                </div>
                <div className="ops-staged-submission-actions">
                  <button
                    type="button"
                    className="ops-staged-submission-icon-btn"
                    onClick={() => onRestoreStagedSubmission(stagedSubmission.id)}
                    aria-label={labels.stagedSubmissionRestore}
                    title={labels.stagedSubmissionRestore}
                  >
                    <Pencil size={14} />
                  </button>
                  <button
                    type="button"
                    className="ops-staged-submission-icon-btn"
                    onClick={() => onRemoveStagedSubmission(stagedSubmission.id)}
                    aria-label={labels.stagedSubmissionRemove}
                    title={labels.stagedSubmissionRemove}
                  >
                    <Trash2 size={14} />
                  </button>
                </div>
              </div>
            ))}
          </div>
        ) : null}
        {(quotedReply || pendingAttachments.length > 0) ? (
          <div className="ops-chat-top-context">
            {quotedReply ? (
              <div className="ops-composer-quote" aria-live="polite">
                <div className="ops-composer-quote-head">
                  <span>{labels.quotedReplyLabel}</span>
                  <button
                    type="button"
                    className="ops-chat-inline-action ops-no-tip-icon-btn"
                    onClick={onClearQuotedReply}
                  >
                    <X size={12} />
                  </button>
                </div>
                <div className="ops-composer-quote-text">{normalizeAssistantMessageText(quotedReply.text)}</div>
              </div>
            ) : null}
            {pendingAttachments.length > 0 ? (
              <div className="ops-pending-files">
                {pendingAttachments.map((path) => (
                  <span key={path} className="ops-pending-chip mono">
                    {(() => {
                      const filePath = normalizeDashboardAttachmentPath(path);
                      const fileAction = workspaceFileAction(filePath, workspaceDownloadExtensionSet);
                      const filename = filePath.split('/').pop() || filePath;
                      return (
                        <a
                          className="ops-attach-link mono ops-pending-open"
                          href="#"
                          onClick={(event) => {
                            event.preventDefault();
                            event.stopPropagation();
                            void onOpenWorkspacePath(filePath);
                          }}
                        >
                          {fileAction === 'download' ? (
                            <Download size={12} className="ops-attach-link-icon" />
                          ) : fileAction === 'preview' ? (
                            <Eye size={12} className="ops-attach-link-icon" />
                          ) : (
                            <FileText size={12} className="ops-attach-link-icon" />
                          )}
                          <span className="ops-attach-link-name">{filename}</span>
                        </a>
                      );
                    })()}
                    <button
                      type="button"
                      className="ops-chat-inline-action ops-no-tip-icon-btn"
                      onClick={(event) => {
                        event.preventDefault();
                        event.stopPropagation();
                        onRemovePendingAttachment(path);
                      }}
                    >
                      <X size={12} />
                    </button>
                  </span>
                ))}
              </div>
            ) : null}
          </div>
        ) : null}
        {isUploadingAttachments ? (
          <div className="ops-upload-progress" aria-live="polite">
            <div className={`ops-upload-progress-track ${attachmentUploadPercent === null ? 'is-indeterminate' : ''}`}>
              <div
                className="ops-upload-progress-fill"
                style={{ width: `${Math.max(3, Number(attachmentUploadPercent ?? 24))}%` }}
              />
            </div>
            <span className="ops-upload-progress-text mono">
              {attachmentUploadPercent === null
                ? labels.uploadingFile
                : `${labels.uploadingFile} ${attachmentUploadPercent}%`}
            </span>
          </div>
        ) : null}
        <div className="ops-composer">
          <input
            ref={filePickerRef}
            type="file"
            multiple
            accept={allowedAttachmentExtensions.length > 0 ? allowedAttachmentExtensions.join(',') : undefined}
            onChange={onPickAttachments}
            className="ops-hidden-file-input"
          />
          <div className={`ops-composer-shell ${controlCommandPanelOpen ? 'is-command-open' : ''}`}>
            <div className="ops-composer-float-controls" ref={controlCommandPanelRef}>
              <div className={`ops-control-command-drawer ${controlCommandPanelOpen ? 'is-open' : ''}`}>
                <button
                  type="button"
                  className="ops-control-command-chip"
                  disabled={!canSendControlCommand || Boolean(activeControlCommand) || isInterrupting}
                  onClick={() => void onSendControlCommand('/restart')}
                  aria-label="/restart"
                  title="/restart"
                >
                  {activeControlCommand === '/restart' ? <RefreshCw size={11} className="animate-spin" /> : <RotateCcw size={11} />}
                  <span className="mono">/restart</span>
                </button>
                <button
                  type="button"
                  className="ops-control-command-chip"
                  disabled={!canSendControlCommand || Boolean(activeControlCommand) || isInterrupting}
                  onClick={() => void onSendControlCommand('/new')}
                  aria-label="/new"
                  title="/new"
                >
                  {activeControlCommand === '/new' ? <RefreshCw size={11} className="animate-spin" /> : <Plus size={11} />}
                  <span className="mono">/new</span>
                </button>
                <button
                  type="button"
                  className="ops-control-command-chip"
                  disabled={!hasSelectedBot || !canChat || Boolean(activeControlCommand) || isInterrupting}
                  onClick={() => void onInterruptExecution()}
                  aria-label="/stop"
                  title="/stop"
                >
                  {isInterrupting ? <RefreshCw size={11} className="animate-spin" /> : <Square size={11} />}
                  <span className="mono">/stop</span>
                </button>
                <button
                  type="button"
                  className="ops-control-command-chip"
                  ref={chatDateTriggerRef}
                  disabled={!hasSelectedBot || chatDateJumping}
                  onClick={onToggleChatDatePicker}
                  aria-label={isZh ? '按日期定位对话' : 'Jump to date'}
                  title={isZh ? '按日期定位对话' : 'Jump to date'}
                >
                  {chatDateJumping ? <RefreshCw size={11} className="animate-spin" /> : <Clock3 size={11} />}
                  <span className="mono">/time</span>
                </button>
              </div>
              {chatDatePickerOpen ? (
                <div
                  className="ops-control-date-panel"
                  style={chatDatePanelPosition ? { bottom: chatDatePanelPosition.bottom, right: chatDatePanelPosition.right } : undefined}
                >
                  <label className="ops-control-date-label">
                    <span>{isZh ? '选择日期' : 'Select date'}</span>
                    <input
                      className="input ops-control-date-input"
                      type="date"
                      value={chatDateValue}
                      max={formatDateInputValue(Date.now())}
                      onChange={(event) => onChatDateValueChange(event.target.value)}
                    />
                  </label>
                  <div className="ops-control-date-actions">
                    <button
                      type="button"
                      className="btn btn-secondary btn-sm"
                      onClick={onCloseChatDatePicker}
                    >
                      {isZh ? '取消' : 'Cancel'}
                    </button>
                    <button
                      type="button"
                      className="btn btn-primary btn-sm"
                      disabled={chatDateJumping || !chatDateValue}
                      onClick={() => void onJumpConversationToDate()}
                    >
                      {chatDateJumping ? <RefreshCw size={14} className="animate-spin" /> : null}
                      <span className={chatDateJumping ? 'ops-control-date-submit-label' : undefined}>
                        {isZh ? '跳转' : 'Jump'}
                      </span>
                    </button>
                  </div>
                </div>
              ) : null}
              <button
                type="button"
                className={`ops-control-command-toggle ${controlCommandPanelOpen ? 'is-open' : ''}`}
                onClick={onToggleControlCommandPanel}
                aria-label={controlCommandPanelOpen ? labels.controlCommandsHide : labels.controlCommandsShow}
                title={controlCommandPanelOpen ? labels.controlCommandsHide : labels.controlCommandsShow}
              >
                {controlCommandPanelOpen ? <Command size={12} /> : <ChevronLeft size={13} />}
              </button>
            </div>
            <textarea
              ref={composerTextareaRef}
              className="input ops-composer-input"
              value={command}
              onChange={(event) => onCommandChange(event.target.value)}
              onKeyDown={onComposerKeyDown}
              disabled={!canChat || isVoiceRecording || isVoiceTranscribing}
              placeholder={canChat ? labels.inputPlaceholder : labels.disabledPlaceholder}
            />
            <div className="ops-composer-tools-right">
              {(isVoiceRecording || isVoiceTranscribing) ? (
                <div className="ops-voice-inline" aria-live="polite">
                  <div className={`ops-voice-wave ${isVoiceRecording ? 'is-live' : ''} ${isCompactMobile ? 'is-mobile' : 'is-desktop'}`}>
                    {Array.from({ length: isCompactMobile ? 1 : 5 }).map((_, segmentIdx) => (
                      <div key={`vw-segment-${segmentIdx}`} className="ops-voice-wave-segment">
                        {Array.from({ length: isCompactMobile ? 28 : 18 }).map((_, idx) => {
                          const delayIndex = isCompactMobile ? idx : (segmentIdx * 18) + idx;
                          return (
                            <i
                              key={`vw-inline-${segmentIdx}-${idx}`}
                              style={{ animationDelay: `${(delayIndex % 14) * 0.06}s` }}
                            />
                          );
                        })}
                      </div>
                    ))}
                  </div>
                  <div className="ops-voice-countdown mono">
                    {isVoiceRecording ? `${voiceCountdown}s` : labels.voiceTranscribing}
                  </div>
                </div>
              ) : null}
              <button
                className={`ops-composer-inline-btn ${isVoiceRecording ? 'is-recording' : ''}`}
                disabled={!canChat || !speechEnabled || isVoiceTranscribing}
                onClick={() => void onVoiceInput()}
                aria-label={isVoiceRecording ? labels.voiceStop : labels.voiceStart}
                title={isVoiceTranscribing ? labels.voiceTranscribing : isVoiceRecording ? labels.voiceStop : labels.voiceStart}
              >
                {isVoiceTranscribing ? (
                  <RefreshCw size={16} className="animate-spin" />
                ) : isVoiceRecording ? (
                  <Square size={16} />
                ) : (
                  <Mic size={16} />
                )}
              </button>
              <LucentIconButton
                className="ops-composer-inline-btn"
                disabled={!canChat || isUploadingAttachments || isVoiceRecording || isVoiceTranscribing}
                onClick={() => void onTriggerPickAttachments()}
                tooltip={isUploadingAttachments ? labels.uploadingFile : labels.uploadFile}
                aria-label={isUploadingAttachments ? labels.uploadingFile : labels.uploadFile}
              >
                <Paperclip size={16} className={isUploadingAttachments ? 'animate-spin' : ''} />
              </LucentIconButton>
              <button
                className={`ops-composer-submit-btn ${showInterruptSubmitAction ? 'is-interrupt' : ''}`}
                disabled={
                  submitActionMode === 'interrupt'
                    ? isInterrupting
                    : (
                      submitActionMode === 'stage'
                      ? (
                        isVoiceRecording
                        || isVoiceTranscribing
                        || !hasComposerDraft
                      )
                      : (
                      !isChatEnabled
                      || isVoiceRecording
                      || isVoiceTranscribing
                      || !hasComposerDraft
                      )
                    )
                }
                onClick={() => void onSubmitAction()}
                aria-label={showInterruptSubmitAction ? labels.interrupt : labels.send}
                title={showInterruptSubmitAction ? labels.interrupt : labels.send}
              >
                {showInterruptSubmitAction ? <Square size={15} /> : <ArrowUp size={18} />}
              </button>
            </div>
          </div>
        </div>
      </div>
      {!canChat ? (
        <div className="ops-chat-disabled-mask">
          <div className="ops-chat-disabled-card">
            {selectedBotControlState === 'starting'
              ? labels.botStarting
              : selectedBotControlState === 'stopping'
                ? labels.botStopping
                : !selectedBotEnabled
                  ? labels.botDisabledHint
                  : labels.chatDisabled}
          </div>
        </div>
      ) : null}
    </div>
  );
}
