import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
import { AttachmentPreviewList } from '@renderer/components/team/attachments/AttachmentPreviewList';
import { DropZoneOverlay } from '@renderer/components/team/attachments/DropZoneOverlay';
import { ActionModeSelector } from '@renderer/components/team/messages/ActionModeSelector';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from '@renderer/components/ui/dialog';
import { Label } from '@renderer/components/ui/label';
import { MemberSelect } from '@renderer/components/ui/MemberSelect';
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { useAttachments } from '@renderer/hooks/useAttachments';
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
import { useStore } from '@renderer/store';
import { chipToken, serializeChipsWithText } from '@renderer/types/inlineChip';
import { buildReplyBlock } from '@renderer/utils/agentMessageFormatting';
import { removeChipTokenFromText } from '@renderer/utils/chipUtils';
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
import { MAX_TEXT_LENGTH } from '@shared/constants';
import { AlertCircle, ImagePlus, Send, X } from 'lucide-react';

import { MemberBadge } from '../MemberBadge';

import type { ActionMode } from '@renderer/components/team/messages/ActionModeSelector';
import type { InlineChip } from '@renderer/types/inlineChip';
import type { MentionSuggestion } from '@renderer/types/mention';
import type { AttachmentPayload, ResolvedTeamMember, SendMessageResult } from '@shared/types';

interface QuotedMessage {
  from: string;
  text: string;
}

interface SendMessageDialogProps {
  open: boolean;
  teamName: string;
  members: ResolvedTeamMember[];
  defaultRecipient?: string;
  /** Pre-filled message text (e.g. from editor selection action) */
  defaultText?: string;
  /** Pre-filled inline code chip (from editor selection action) */
  defaultChip?: InlineChip;
  quotedMessage?: QuotedMessage;
  isTeamAlive?: boolean;
  sending: boolean;
  sendError: string | null;
  lastResult: SendMessageResult | null;
  onSend: (
    member: string,
    text: string,
    summary?: string,
    attachments?: AttachmentPayload[],
    actionMode?: ActionMode
  ) => void;
  onClose: () => void;
}

// Sticky action mode — survives dialog close/reopen (component remount)
let stickyActionMode: ActionMode = 'do';

export const SendMessageDialog = ({
  open,
  teamName,
  members,
  defaultRecipient,
  defaultText,
  defaultChip,
  quotedMessage,
  isTeamAlive,
  sending,
  sendError,
  lastResult,
  onSend,
  onClose,
}: SendMessageDialogProps): React.JSX.Element => {
  const colorMap = useMemo(() => buildMemberColorMap(members), [members]);
  const projectPath = useStore((s) => s.selectedTeamData?.config.projectPath ?? null);
  const [quote, setQuote] = useState<QuotedMessage | undefined>(undefined);
  const [quoteExpanded, setQuoteExpanded] = useState(false);
  const [member, setMember] = useState('');
  const textDraft = useDraftPersistence({ key: `sendMessage:${teamName}:text` });
  const chipDraft = useChipDraftPersistence(`sendMessage:${teamName}:chips`);
  const prevOpenRef = useRef(false);
  const prevResultRef = useRef<SendMessageResult | null>(null);

  const [isDragOver, setIsDragOver] = useState(false);
  const dragCounterRef = useRef(0);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const [imageRestrictionError, setImageRestrictionError] = useState<string | null>(null);
  const imageRestrictionTimerRef = useRef(0);
  const [actionMode, setActionModeState] = useState<ActionMode>(stickyActionMode);
  const actionModeRef = useRef<ActionMode>(stickyActionMode);
  const setActionMode = useCallback((mode: ActionMode) => {
    actionModeRef.current = mode;
    stickyActionMode = mode;
    setActionModeState(mode);
  }, []);

  const {
    attachments,
    error: attachmentError,
    canAddMore,
    addFiles,
    removeAttachment,
    clearAttachments,
    handlePaste,
    handleDrop,
  } = useAttachments({ persistenceKey: `sendMessage:${teamName}:attachments` });

  const selectedMember = members.find((m) => m.name === member);
  const isLeadRecipient = selectedMember?.role === 'lead' || selectedMember?.name === 'team-lead';
  const hasTeammates = members.length > 1;
  const canDelegate = hasTeammates && isLeadRecipient;
  const shouldAutoDelegate = canDelegate;
  const supportsAttachments = isLeadRecipient && !!isTeamAlive;
  const canAttach = supportsAttachments && canAddMore;

  // Auto-switch to delegate when lead recipient is selected, but don't
  // override user's explicit choice on dialog open.
  const prevShouldAutoDelegateRef = useRef(shouldAutoDelegate);
  useEffect(() => {
    if (!canDelegate && actionMode === 'delegate') {
      setActionMode('do');
      return;
    }

    // Skip the initial mount — honour the sticky mode
    if (prevShouldAutoDelegateRef.current === shouldAutoDelegate) return;
    prevShouldAutoDelegateRef.current = shouldAutoDelegate;

    if (shouldAutoDelegate) {
      setActionMode('delegate');
    } else {
      setActionModeState((prev) => (prev === 'delegate' ? 'do' : prev));
    }
  }, [actionMode, canDelegate, setActionMode, shouldAutoDelegate]);

  const [pendingAutoClose, setPendingAutoClose] = useState(false);
  // Reset form on open transition (avoid setState in render)
  useEffect(() => {
    if (open && !prevOpenRef.current) {
      setMember(defaultRecipient ?? '');
      setQuote(quotedMessage);
      setQuoteExpanded(false);
      prevResultRef.current = lastResult;
      if (defaultChip) {
        const token = chipToken(defaultChip);
        textDraft.setValue(token + '\n');
        chipDraft.setChips([defaultChip]);
      } else if (defaultText) {
        textDraft.setValue(defaultText);
      }
    }
    prevOpenRef.current = open;
  }, [
    open,
    defaultRecipient,
    defaultText,
    defaultChip,
    quotedMessage,
    lastResult,
    textDraft,
    chipDraft,
  ]);

  // Track whether auto-close is needed (avoid setState in render)
  useEffect(() => {
    if (!open) return;
    if (lastResult && lastResult !== prevResultRef.current) {
      prevResultRef.current = lastResult;
      setMember('');
      setPendingAutoClose(true);
    }
  }, [open, lastResult]);

  // Side effects (onClose mutates parent state) must run in useEffect, not render phase
  useEffect(() => {
    if (pendingAutoClose) {
      textDraft.clearDraft();
      chipDraft.clearChipDraft();
      clearAttachments();
      setPendingAutoClose(false);
      onClose();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps -- only trigger on pendingAutoClose flag
  }, [pendingAutoClose]);

  const QUOTE_COLLAPSE_THRESHOLD = 120;
  const isQuoteLong = (quote?.text.length ?? 0) > QUOTE_COLLAPSE_THRESHOLD;

  const mentionSuggestions = useMemo<MentionSuggestion[]>(
    () =>
      members.map((m) => ({
        id: m.name,
        name: m.name,
        subtitle: formatAgentRole(m.role) ?? formatAgentRole(m.agentType) ?? undefined,
        color: colorMap.get(m.name),
      })),
    [members, colorMap]
  );

  const { suggestions: teamMentionSuggestions } = useTeamSuggestions(teamName);

  const attachmentsBlocked = attachments.length > 0 && !supportsAttachments;

  const trimmedText = textDraft.value.trim();
  const serialized = serializeChipsWithText(trimmedText, chipDraft.chips);
  const finalText = quote ? buildReplyBlock(quote.from, quote.text, serialized) : serialized;
  const remaining = MAX_TEXT_LENGTH - finalText.length;

  const canSend =
    member.trim().length > 0 &&
    finalText.length > 0 &&
    finalText.length <= MAX_TEXT_LENGTH &&
    !sending &&
    !attachmentsBlocked;

  const handleChipRemove = (chipId: string): void => {
    const chip = chipDraft.chips.find((c) => c.id === chipId);
    if (chip) {
      textDraft.setValue(removeChipTokenFromText(textDraft.value, chip));
    }
    chipDraft.setChips(chipDraft.chips.filter((c) => c.id !== chipId));
  };

  const handleSubmit = (): void => {
    if (!canSend) return;
    onSend(
      member.trim(),
      finalText,
      trimmedText,
      attachments.length > 0 ? attachments : undefined,
      actionMode
    );
    textDraft.clearDraft();
    chipDraft.clearChipDraft();
    clearAttachments();
  };

  const handleOpenChange = (nextOpen: boolean): void => {
    if (!nextOpen) {
      onClose();
    }
  };

  const handleFileInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const input = e.target;
      if (input.files?.length) {
        void addFiles(input.files);
      }
      input.value = '';
    },
    [addFiles]
  );

  const showImageRestrictionError = useCallback(() => {
    setImageRestrictionError('Images can only be sent to the team lead');
    window.clearTimeout(imageRestrictionTimerRef.current);
    imageRestrictionTimerRef.current = window.setTimeout(() => {
      setImageRestrictionError(null);
    }, 4000);
  }, []);

  // Cleanup restriction error timer on unmount
  useEffect(() => {
    const ref = imageRestrictionTimerRef;
    return () => window.clearTimeout(ref.current);
  }, []);

  const handleDragEnter = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    dragCounterRef.current += 1;
    if (dragCounterRef.current === 1) setIsDragOver(true);
  }, []);

  const handleDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    dragCounterRef.current -= 1;
    if (dragCounterRef.current <= 0) {
      dragCounterRef.current = 0;
      setIsDragOver(false);
    }
  }, []);

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
  }, []);

  const handleDropWrapper = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      dragCounterRef.current = 0;
      setIsDragOver(false);
      if (!isLeadRecipient) {
        const files = e.dataTransfer?.files;
        if (files?.length) {
          const hasImages = Array.from(files).some((f) => f.type.startsWith('image/'));
          if (hasImages) {
            showImageRestrictionError();
          }
        }
        return;
      }
      if (canAttach) handleDrop(e);
    },
    [isLeadRecipient, canAttach, handleDrop, showImageRestrictionError]
  );

  const handlePasteWrapper = useCallback(
    (e: React.ClipboardEvent) => {
      if (!isLeadRecipient) {
        const hasImages = Array.from(e.clipboardData.items).some((item) =>
          item.type.startsWith('image/')
        );
        if (hasImages) {
          e.preventDefault();
          showImageRestrictionError();
        }
        return;
      }
      if (canAttach) handlePaste(e);
    },
    [isLeadRecipient, canAttach, handlePaste, showImageRestrictionError]
  );

  return (
    <Dialog open={open} onOpenChange={handleOpenChange}>
      <DialogContent
        className="min-w-0 max-w-3xl"
        onDragEnter={handleDragEnter}
        onDragLeave={handleDragLeave}
        onDragOver={handleDragOver}
        onDrop={handleDropWrapper}
        onPaste={handlePasteWrapper}
      >
        <DropZoneOverlay active={isDragOver} rejected={!isLeadRecipient} />

        <DialogHeader>
          <DialogTitle>Send Message</DialogTitle>
          <DialogDescription>Send a direct message to a team member.</DialogDescription>
        </DialogHeader>

        <div className="grid gap-4 py-2">
          <div className="grid gap-2">
            <Label htmlFor="smd-recipient">Recipient</Label>
            <MemberSelect
              members={members}
              value={member || null}
              onChange={(v) => setMember(v ?? '')}
              placeholder="Select member..."
              size="sm"
            />
          </div>

          <div className="grid gap-2">
            <div className="flex items-center gap-2">
              <Label htmlFor="smd-message">Message</Label>
              {isLeadRecipient ? (
                <>
                  <input
                    ref={fileInputRef}
                    type="file"
                    accept="image/png,image/jpeg,image/gif,image/webp"
                    multiple
                    className="hidden"
                    onChange={handleFileInputChange}
                  />
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        type="button"
                        className={`inline-flex items-center gap-1 rounded p-1 transition-colors ${
                          canAttach
                            ? 'text-[var(--color-text-secondary)] hover:text-[var(--color-text)]'
                            : 'text-[var(--color-text-muted)] opacity-40'
                        }`}
                        disabled={!canAttach}
                        onClick={() => fileInputRef.current?.click()}
                      >
                        <ImagePlus size={14} />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent side="top">
                      {!isTeamAlive
                        ? 'Team must be online to attach images'
                        : !canAddMore
                          ? 'Maximum attachments reached'
                          : 'Attach images (paste or drag & drop)'}
                    </TooltipContent>
                  </Tooltip>
                </>
              ) : null}
            </div>

            <AttachmentPreviewList
              attachments={attachments}
              onRemove={removeAttachment}
              error={attachmentError ?? imageRestrictionError}
              disabled={attachmentsBlocked}
              disabledHint="Image attachments are only supported when sending to the team lead while the team is online. Remove attachments or switch recipient."
            />

            <div className={quote ? 'flex flex-col' : 'contents'}>
              {quote ? (
                <div className="relative overflow-hidden rounded-t-md border border-b-0 border-blue-400/30 bg-blue-100/80 py-2 pl-3 pr-2 dark:border-blue-500/20 dark:bg-blue-950/20">
                  {/* Decorative quotation mark */}
                  <span className="pointer-events-none absolute -right-1 top-1/2 -translate-y-1/2 select-none font-serif text-[64px] leading-none text-blue-500/[0.08] dark:text-blue-400/[0.08]">
                    &ldquo;
                  </span>

                  <Tooltip>
                    <TooltipTrigger asChild>
                      <button
                        type="button"
                        className="absolute right-1.5 top-1.5 z-10 rounded p-0.5 text-blue-400/60 hover:text-blue-600 dark:text-blue-300/40 dark:hover:text-blue-200"
                        onClick={() => setQuote(undefined)}
                      >
                        <X size={12} />
                      </button>
                    </TooltipTrigger>
                    <TooltipContent side="left">Remove quote</TooltipContent>
                  </Tooltip>

                  <div className="mb-1 flex items-center gap-1.5">
                    <span className="text-[10px] text-blue-600/70 dark:text-blue-300/60">
                      Replying to
                    </span>
                    <MemberBadge name={quote.from} color={colorMap.get(quote.from)} size="sm" />
                  </div>
                  <div
                    className={`pr-5 opacity-60 dark:opacity-50 ${quoteExpanded ? '' : 'max-h-[3.75rem] overflow-hidden'}`}
                  >
                    <MarkdownViewer
                      content={quote.text}
                      bare
                      maxHeight={quoteExpanded ? 'max-h-48' : 'max-h-[3.75rem]'}
                    />
                  </div>
                  {isQuoteLong ? (
                    <button
                      type="button"
                      className="mt-0.5 text-[10px] text-blue-500 hover:text-blue-700 dark:text-blue-400/60 dark:hover:text-blue-300"
                      onClick={() => setQuoteExpanded((v) => !v)}
                    >
                      {quoteExpanded ? 'less' : 'more'}
                    </button>
                  ) : null}
                </div>
              ) : null}
              <MentionableTextarea
                id="smd-message"
                className={quote ? 'rounded-t-none' : undefined}
                placeholder="Write your message... (Enter to send)"
                value={textDraft.value}
                onValueChange={textDraft.setValue}
                suggestions={mentionSuggestions}
                teamSuggestions={teamMentionSuggestions}
                chips={chipDraft.chips}
                onChipRemove={handleChipRemove}
                projectPath={projectPath}
                onFileChipInsert={(chip) => chipDraft.setChips([...chipDraft.chips, chip])}
                onModEnter={handleSubmit}
                minRows={4}
                maxRows={12}
                maxLength={MAX_TEXT_LENGTH}
                disabled={sending}
                cornerActionLeft={
                  <ActionModeSelector
                    value={actionMode}
                    onChange={setActionMode}
                    showDelegate={canDelegate}
                  />
                }
                cornerAction={
                  <button
                    type="button"
                    className="inline-flex shrink-0 items-center gap-1 rounded-full bg-blue-600 px-3 py-1.5 text-[11px] font-medium text-white shadow-sm transition-colors hover:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50"
                    disabled={!canSend}
                    onClick={handleSubmit}
                  >
                    <Send size={12} />
                    {sending ? 'Sending...' : 'Send'}
                  </button>
                }
                footerRight={
                  <div className="flex items-center gap-2">
                    {sendError ? (
                      <span className="inline-flex items-center gap-1 rounded bg-red-500/10 px-1.5 py-0.5 text-[10px] text-red-400">
                        <AlertCircle size={10} className="shrink-0" />
                        {sendError}
                      </span>
                    ) : null}
                    {remaining < 200 ? (
                      <span
                        className={`text-[10px] ${remaining < 100 ? 'text-yellow-400' : 'text-[var(--color-text-muted)]'}`}
                      >
                        {remaining} chars left
                      </span>
                    ) : null}
                    {textDraft.isSaved ? (
                      <span className="text-[10px] text-[var(--color-text-muted)]">
                        Draft saved
                      </span>
                    ) : null}
                  </div>
                }
              />
            </div>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
};
