"use client"

import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react"
import { Reply, ReplyAll, Forward } from "lucide-react"
import { cn } from "@/lib/utils"
import {
  avatarColor,
  cleanSenderName,
  senderInitial,
} from "@/lib/sender-display"
import type { ApiMessageSummary, ApiMessageFull } from "@/lib/api/types"
import { useListMessageAttachments } from "@/lib/api/hooks/use-list-message-attachments"
import { useRecoverMissingMessageAttachments } from "@/lib/api/hooks/use-recover-missing-message-attachments"
import { resolvePreviewAttachments } from "@/lib/attachment-display"
import type { Email, EmailAttachment } from "@/lib/email-data"
import {
  mailFlagIsRead,
  mailFlagIsStarred,
  mailFlagIsImportant,
  messageIsSpam,
  messageHasFlag,
  messageHasLabel,
} from "@/lib/mail-flags"
import { repairSnippet } from "@/lib/mail-mime-body"
import type { FolderTreeNode, LabelRowItem } from "@/lib/sidebar-nav-data"
import { useMessage, useThread, unwrapThreadMessages } from "@/lib/api/hooks/use-mail-queries"
import {
  useToggleStar,
  useUpdateFlags,
  useUpdateLabels,
} from "@/lib/api/hooks/use-mail-mutations"
import {
  useComposeActions,
  useComposeDrafts,
  useComposeWindows,
  type ThreadComposeKind,
  type ComposeOpenPreset,
  savedThreadDraftToComposePreset,
} from "@/lib/compose-context"
import { resolveComposeIdentity } from "@/lib/compose/resolve-compose-identity"
import { useSelfMailEmails } from "@/lib/hooks/use-self-mail-emails"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { resolveMessageFrom } from "@/lib/mail-message-participants"
import { buildMessageHeaderDetails } from "@/lib/mail-message-header-details"
import { splitThreadAroundOpenMessage } from "@/lib/mail-thread"
import {
  buildThreadComposePreset,
  withTouchFullscreenComposePreset,
} from "@/lib/thread-compose-preset"
import { openConversationPrint } from "@/lib/print-conversation"
import { resolveParsedCalendarInvitation } from "@/lib/resolve-email-calendar-invitation"
import { ComposeWindow } from "@/components/gmail/compose-modal"
import { CalendarInvitationPreview } from "@/components/gmail/calendar-invitation-preview"
import { EmailViewSubjectHeader } from "./email-view/email-view-header"
import {
  MAIL_PREVIEW_SCROLL_CLASS,
  MAIL_REPLY_BAR_CLASS,
  MAIL_REPLY_BUTTON_CLASS,
} from "@/lib/mail-chrome-classes"
import {
  ExpandedMessage,
  SpamWhyBanner,
  ThreadPriorMessage,
  formatApiMessageBody,
  plainTextBodyFallback,
} from "@/components/gmail/email-view/email-view-messages"
import {
  ConversationAttachmentsSection,
  type ConversationAttachmentEntry,
} from "@/components/gmail/email-view/message-attachments"

function apiToLegacyEmail(
  msg: ApiMessageSummary,
  full?: ApiMessageFull | null,
  thread?: ApiMessageFull[] | null
): Email {
  const senderName = msg.from[0]?.name ?? ""
  return {
    id: msg.id,
    sender: senderName,
    senderEmail: msg.from[0]?.address,
    subject: msg.subject,
    preview: repairSnippet(msg.snippet) ?? msg.snippet,
    body: full?.body_html ?? full?.body_text,
    date: msg.date,
    read: mailFlagIsRead(msg.flags),
    starred: mailFlagIsStarred(msg.flags),
    important: mailFlagIsImportant(msg.flags, msg.labels),
    spam: messageIsSpam(msg.flags, msg.labels),
    labels: msg.labels,
    hasAttachment: msg.has_attachments,
    conversation: thread
      ?.filter((m) => m.id !== msg.id)
      .map((m) => ({
        id: m.id,
        sender: m.from[0]?.name ?? "",
        senderEmail: m.from[0]?.address ?? "",
        date: m.date,
        body: m.body_html ?? m.body_text ?? "",
        preview: repairSnippet(m.snippet) ?? m.snippet,
      })),
  }
}

interface EmailViewProps {
  email: ApiMessageSummary
  onNavigateToLabel?: (label: string) => void
  showLabelChip?: (label: string) => boolean
  labelBgByText?: Map<string, string>
  emailLabelToSidebarFolderId?: Record<string, string>
  getNavItemPrefs?: (id: string) => { messages: string }
  folderTree?: FolderTreeNode[]
  labelRows?: readonly LabelRowItem[]
  currentFolderId?: string
  isSingleMessageView?: boolean
}

export function EmailView({
  email,
  onNavigateToLabel,
  showLabelChip,
  labelBgByText,
  emailLabelToSidebarFolderId = {},
  getNavItemPrefs = () => ({ messages: "show" }),
  folderTree,
  labelRows,
  currentFolderId,
  isSingleMessageView = false,
}: EmailViewProps) {
  const { data: fullMessage, isPending: fullMessagePending } = useMessage(email.id)
  const { data: threadMessages } = useThread(email.thread_id ?? null)
  const selfEmails = useSelfMailEmails()
  const chromeIdentity = useChromeIdentity()
  const selfDisplayName = chromeIdentity?.name

  const toggleStar = useToggleStar()
  const updateFlags = useUpdateFlags()
  const updateLabels = useUpdateLabels()

  const flags = fullMessage?.flags ?? email.flags
  const isStarred = mailFlagIsStarred(flags)
  const isSpam = messageIsSpam(flags, fullMessage?.labels ?? email.labels)

  const body = useMemo(
    () =>
      formatApiMessageBody(
        fullMessage,
        email.snippet,
        fullMessagePending && !fullMessage
      ),
    [fullMessage, fullMessagePending, email.snippet]
  )
  const plainTextFallback = useMemo(
    () => plainTextBodyFallback(fullMessage),
    [fullMessage]
  )

  const [showFullThread, setShowFullThread] = useState(false)
  const [mainDetailsOpen, setMainDetailsOpen] = useState(false)

  const threadOrdered = useMemo(
    () => unwrapThreadMessages(threadMessages),
    [threadMessages]
  )

  const { before: threadBefore, after: threadAfter } = useMemo(
    () => splitThreadAroundOpenMessage(threadOrdered, email.id),
    [threadOrdered, email.id]
  )

  const otherThreadCount = threadBefore.length + threadAfter.length
  const showRepliesCta =
    isSingleMessageView && !showFullThread && otherThreadCount > 0

  const showFullThreadList = !isSingleMessageView || showFullThread
  const messagesBefore = showFullThreadList ? threadBefore : []
  const messagesAfter = showFullThreadList ? threadAfter : []

  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
  const isThreadMessageExpanded = useCallback(
    (msgId: string) => expandedIds.has(msgId),
    [expandedIds]
  )
  const toggleExpanded = (msgId: string) => {
    setExpandedIds((prev) => {
      const next = new Set(prev)
      if (next.has(msgId)) next.delete(msgId)
      else next.add(msgId)
      return next
    })
  }

  const mainFrom = useMemo(
    () =>
      resolveMessageFrom(fullMessage?.from ?? email.from, {
        selfEmails,
        selfDisplayName,
      }),
    [fullMessage?.from, email.from, selfEmails, selfDisplayName]
  )
  const mainHeaderDetails = useMemo(
    () =>
      buildMessageHeaderDetails(
        {
          ...email,
          ...fullMessage,
          from: fullMessage?.from ?? email.from,
          to: fullMessage?.to ?? email.to,
          cc: fullMessage?.cc,
          subject: email.subject,
        },
        { selfEmails, selfDisplayName, subject: email.subject }
      ),
    [email, fullMessage, selfEmails, selfDisplayName]
  )

  const legacyEmail = useMemo(
    () => apiToLegacyEmail(email, fullMessage, unwrapThreadMessages(threadMessages)),
    [email, fullMessage, threadMessages]
  )

  const allThreadMessages = useMemo((): ApiMessageFull[] => {
    const main: ApiMessageFull = fullMessage
      ? { ...email, ...fullMessage }
      : { ...email }
    return [...threadBefore, main, ...threadAfter]
  }, [threadBefore, threadAfter, email, fullMessage])

  // Thread API used to omit has_attachments; treat undefined as unknown (still fetch).
  const attachmentMessageIds = useMemo(
    () =>
      allThreadMessages
        .filter((m) => m.has_attachments !== false)
        .map((m) => m.id),
    [allThreadMessages]
  )

  const { byId: attachmentsByMessageId, stateById: attachmentFetchStateById } =
    useListMessageAttachments(attachmentMessageIds)

  useRecoverMissingMessageAttachments(
    allThreadMessages,
    attachmentsByMessageId,
    attachmentFetchStateById
  )

  const resolveMessageAttachments = useCallback(
    (msg: Pick<ApiMessageSummary, "id" | "has_attachments">): EmailAttachment[] =>
      resolvePreviewAttachments(
        { hasAttachment: msg.has_attachments },
        attachmentsByMessageId.get(msg.id),
        attachmentFetchStateById.get(msg.id) ?? "idle"
      ),
    [attachmentsByMessageId, attachmentFetchStateById]
  )

  const mainMessageAttachments = useMemo(
    () => resolveMessageAttachments(email),
    [resolveMessageAttachments, email]
  )

  const conversationAttachmentEntries = useMemo((): ConversationAttachmentEntry[] => {
    return allThreadMessages
      .map((msg) => {
        const attachments = resolveMessageAttachments(msg)
        if (attachments.length === 0) return null
        const from = resolveMessageFrom(msg.from, {
          selfEmails,
          selfDisplayName,
        })
        return {
          messageId: msg.id,
          senderName: from.name,
          attachments,
        }
      })
      .filter((entry): entry is ConversationAttachmentEntry => entry !== null)
  }, [
    allThreadMessages,
    resolveMessageAttachments,
    selfEmails,
    selfDisplayName,
  ])

  const { composeWindows } = useComposeWindows()
  const { savedThreadReplyDrafts } = useComposeDrafts()
  const { openComposeWithInitial } = useComposeActions()

  const inlineCompose = useMemo(
    () =>
      composeWindows.find(
        (c) => c.placement === "inline" && c.threadEmailId === email.id
      ),
    [composeWindows, email.id]
  )

  const hasInlineForThread = Boolean(inlineCompose)
  const showReplyForwardBar = !inlineCompose

  const previewScrollRef = useRef<HTMLDivElement>(null)
  const threadComposeAnchorRef = useRef<HTMLDivElement>(null)

  const scrollThreadComposeIntoView = useCallback(() => {
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        threadComposeAnchorRef.current?.scrollIntoView({
          behavior: "smooth",
          block: "end",
          inline: "nearest",
        })
      })
    })
  }, [])

  const openThreadCompose = useCallback(
    (preset: ComposeOpenPreset) => {
      const resolved = withTouchFullscreenComposePreset(preset)
      openComposeWithInitial(resolved)
      if (resolved.placement === "inline") {
        scrollThreadComposeIntoView()
      }
    },
    [openComposeWithInitial, scrollThreadComposeIntoView]
  )

  const savedThreadDraft = savedThreadReplyDrafts[email.id]

  useEffect(() => {
    if (!savedThreadDraft || hasInlineForThread) return
    openThreadCompose(savedThreadDraftToComposePreset(savedThreadDraft))
  }, [email.id, savedThreadDraft, hasInlineForThread, openThreadCompose])

  const startThreadCompose = useCallback(
    (kind: ThreadComposeKind) => {
      openThreadCompose(buildThreadComposePreset(legacyEmail, kind))
    },
    [legacyEmail, openThreadCompose]
  )

  const onToolbarReply = useCallback(
    () => startThreadCompose("reply"),
    [startThreadCompose]
  )
  const onToolbarForward = useCallback(
    () => startThreadCompose("forward"),
    [startThreadCompose]
  )

  const selfIdentity = resolveComposeIdentity()
  const selfName = cleanSenderName(selfIdentity.name)

  const calendarInvitation = useMemo(
    () => resolveParsedCalendarInvitation(legacyEmail),
    [legacyEmail]
  )

  const handleToggleStar = useCallback(() => {
    toggleStar.mutate({ id: email.id, flags, starred: isStarred })
  }, [email.id, flags, isStarred, toggleStar])

  const handleNotSpam = useCallback(() => {
    if (messageHasFlag(flags, "spam")) {
      updateFlags.mutate({
        id: email.id,
        flags: flags.filter((f) => f.toLowerCase() !== "spam"),
      })
    }
    if (messageHasLabel(email.labels, "spam")) {
      updateLabels.mutate({
        id: email.id,
        labels: (email.labels ?? []).filter((l) => l.toLowerCase() !== "spam"),
      })
    }
  }, [email.id, flags, email.labels, updateFlags, updateLabels])

  const handlePrint = useCallback(() => {
    openConversationPrint(legacyEmail)
  }, [legacyEmail])

  return (
    <div className="flex min-h-0 min-w-0 flex-1 flex-col">
        <div ref={previewScrollRef} className={MAIL_PREVIEW_SCROLL_CLASS}>
          <div
            className="h-[52px] shrink-0 bg-mail-surface sm:hidden"
            aria-hidden
          />
          <EmailViewSubjectHeader
            email={email}
            isSpamMessage={isSpam}
            onNotSpam={isSpam ? handleNotSpam : undefined}
            onPrint={handlePrint}
            onNavigateToLabel={onNavigateToLabel}
            showLabelChip={showLabelChip}
            labelBgByText={labelBgByText}
            emailLabelToSidebarFolderId={emailLabelToSidebarFolderId}
            getNavItemPrefs={getNavItemPrefs}
            folderTree={folderTree}
            labelRows={labelRows}
            currentFolderId={currentFolderId}
          />

          {calendarInvitation ? (
            <CalendarInvitationPreview invitation={calendarInvitation} />
          ) : null}

          {isSpam && <SpamWhyBanner onNotSpam={handleNotSpam} />}

          {showRepliesCta ? (
            <div className="border-b border-border px-6 py-3 max-sm:px-4">
              <button
                type="button"
                onClick={() => setShowFullThread(true)}
                className="text-sm font-medium text-primary hover:underline"
              >
                {otherThreadCount === 1
                  ? "Afficher l'autre message du fil"
                  : `Afficher les ${otherThreadCount} autres messages du fil`}
              </button>
            </div>
          ) : null}

          {messagesBefore.map((msg) => (
            <div key={msg.id} className="border-b border-[#eceff1]">
              <ThreadPriorMessage
                message={msg}
                isExpanded={isThreadMessageExpanded(msg.id)}
                onToggle={() => toggleExpanded(msg.id)}
                onPrintConversation={handlePrint}
                onReply={onToolbarReply}
                onForward={onToolbarForward}
                selfEmails={selfEmails}
                selfDisplayName={selfDisplayName}
                collapseQuotedReplies={otherThreadCount > 0}
                attachments={resolveMessageAttachments(msg)}
              />
            </div>
          ))}

          <ExpandedMessage
            sender={mainFrom.name}
            senderEmail={mainFrom.email}
            headerDetails={mainHeaderDetails}
            dateIso={email.date}
            body={body}
            isSpam={isSpam}
            isLast={messagesAfter.length === 0}
            starred={isStarred}
            attachments={mainMessageAttachments}
            onToggleStar={handleToggleStar}
            onPrintConversation={handlePrint}
            onReply={onToolbarReply}
            onForward={onToolbarForward}
            detailsOpen={mainDetailsOpen}
            onDetailsOpenChange={setMainDetailsOpen}
            collapseQuotedReplies={otherThreadCount > 0}
            messageId={email.id}
            plainTextFallback={plainTextFallback}
          />

          {messagesAfter.map((msg) => (
            <div key={msg.id} className="border-b border-[#eceff1]">
              <ThreadPriorMessage
                message={msg}
                isExpanded={isThreadMessageExpanded(msg.id)}
                onToggle={() => toggleExpanded(msg.id)}
                onPrintConversation={handlePrint}
                onReply={onToolbarReply}
                onForward={onToolbarForward}
                selfEmails={selfEmails}
                selfDisplayName={selfDisplayName}
                collapseQuotedReplies={otherThreadCount > 0}
                attachments={resolveMessageAttachments(msg)}
              />
            </div>
          ))}

          {otherThreadCount > 0 && conversationAttachmentEntries.length > 0 ? (
            <ConversationAttachmentsSection entries={conversationAttachmentEntries} />
          ) : null}

          {showReplyForwardBar ? (
            <div
              className={cn(
                "z-10 mt-4 hidden flex-wrap items-center gap-x-3 gap-y-2 px-4 pb-6 pl-[68px] sm:flex",
                "max-sm:static sm:sticky sm:bottom-0",
                MAIL_REPLY_BAR_CLASS
              )}
            >
              <button
                type="button"
                onClick={() => startThreadCompose("reply")}
                className={MAIL_REPLY_BUTTON_CLASS}
              >
                <Reply
                  className="h-[18px] w-[18px] shrink-0 text-muted-foreground"
                  strokeWidth={1.5}
                />
                Répondre
              </button>
              <button
                type="button"
                onClick={() => startThreadCompose("replyAll")}
                className={MAIL_REPLY_BUTTON_CLASS}
              >
                <ReplyAll
                  className="h-[18px] w-[18px] shrink-0 text-muted-foreground"
                  strokeWidth={1.5}
                />
                Répondre à tous
              </button>
              <button
                type="button"
                onClick={() => startThreadCompose("forward")}
                className={MAIL_REPLY_BUTTON_CLASS}
              >
                <Forward
                  className="h-[18px] w-[18px] shrink-0 text-muted-foreground"
                  strokeWidth={1.5}
                />
                Transférer
              </button>
            </div>
          ) : null}

          {inlineCompose ? (
            <div
              ref={threadComposeAnchorRef}
              className="mt-6 px-4 pb-6 max-sm:px-4"
            >
              <div className="flex items-start gap-3">
                <div
                  className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full text-sm font-bold text-white"
                  style={{ backgroundColor: avatarColor(selfName) }}
                  aria-hidden
                >
                  {senderInitial(selfName)}
                </div>
                <div className="min-w-0 flex-1">
                  <ComposeWindow
                    key={inlineCompose.id}
                    compose={inlineCompose}
                    threadSourceEmail={legacyEmail}
                  />
                </div>
              </div>
            </div>
          ) : null}
        </div>
      </div>
  )
}
