"use client"

import {
  useState,
  useRef,
  useEffect,
  useLayoutEffect,
  useCallback,
  useMemo,
} from "react"
import { useIsXs } from "@/hooks/use-xs"
import { readCoarsePointerMatches } from "@/hooks/use-touch-nav"
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"
import { useEditor, EditorContent } from "@tiptap/react"
import { type Extensions } from "@tiptap/core"
import StarterKit from "@tiptap/starter-kit"
import Underline from "@tiptap/extension-underline"
import Link from "@tiptap/extension-link"
import TextAlign from "@tiptap/extension-text-align"
import { TextStyle, FontFamily, FontSize, BackgroundColor } from "@tiptap/extension-text-style"
import Color from "@tiptap/extension-color"
import {
  Reply,
  ReplyAll,
  Forward,
  Maximize2,
  Minimize2,
  X,
} from "lucide-react"
import {
  type ComposeState,
  cloneComposeForPendingSend,
  DEFAULT_IDENTITIES,
  useComposeActions,
  useComposeWindows,
} from "@/lib/compose-context"
import { useScheduledMail } from "@/lib/scheduled-mail-context"
import type { ScheduleSendPayload } from "@/lib/api/scheduled-mail"
import type { Email } from "@/lib/email-data"
import {
  buildThreadComposePreset,
  collectThreadParticipants,
} from "@/lib/thread-compose-preset"
import { toast } from "sonner"
import { showPendingSendToast } from "@/lib/pending-send-toast"
import { cn } from "@/lib/utils"
import {
  MAIL_COMPOSE_TITLEBAR_CLASS,
  MAIL_ICON_BTN,
} from "@/lib/mail-chrome-classes"
import { ComposeRecipientFields } from "@/components/gmail/compose/compose-recipients"
import {
  ComposeBottomToolbar,
  FormattingToolbar,
} from "@/components/gmail/compose/compose-toolbar"
import {
  ComposeAttachmentsList,
  ComposeDockTitleBar,
  ComposeDropOverlay,
  ComposeInlineRecipientHeader,
  ComposeXsSheetHeader,
} from "@/components/gmail/compose/compose-editor-chrome"
import { SignatureBlock, stripSignature, insertSignatureHtml } from "@/components/gmail/compose/compose-shared"

export function ComposeWindow({
  compose,
  threadSourceEmail = null,
  isXsSheet = false,
  bindXsSheetClose,
}: {
  compose: ComposeState
  /** Fil courant : nécessaire pour le menu Répondre / Transférer en inline */
  threadSourceEmail?: Email | null
  /** Plein écran dans une bottom sheet (xs) — pas de file ni réduction */
  isXsSheet?: boolean
  bindXsSheetClose?: (fn: (() => void) | null) => void
}) {
  const {
    closeCompose,
    updateCompose,
    applyComposePreset,
    toggleMinimize,
    toggleMaximize,
    restoreComposeFromSnapshot,
  } = useComposeActions()
  const { scheduleSend, requestUpdateScheduledSend, requestSendScheduledNow } =
    useScheduledMail()
  const isInline = compose.placement === "inline"
  const isEditingScheduled = compose.editingScheduledId != null
  const [showFormatting, setShowFormatting] = useState(false)
  const [recipientsFocused, setRecipientsFocused] = useState(false)
  const [sendMenuOpen, setSendMenuOpen] = useState(false)
  const [isDragOver, setIsDragOver] = useState(false)
  const fieldsRef = useRef<HTMLDivElement>(null)
  const inlineRecipientShellRef = useRef<HTMLDivElement>(null)
  const subjectInputRef = useRef<HTMLInputElement>(null)
  const fileInputRef = useRef<HTMLInputElement>(null)
  const imageInputRef = useRef<HTMLInputElement>(null)

  const snapBodyCaretToStartOnHeaderTab =
    !isInline &&
    !compose.threadEmailId &&
    !compose.threadKind &&
    !compose.editingScheduledId

  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit,
      Underline,
      Link.configure({ openOnClick: false }),
      TextStyle,
      Color,
      BackgroundColor,
      FontFamily,
      FontSize,
      TextAlign.configure({ types: ["heading", "paragraph"], alignments: ["left", "center", "right", "justify"] }),
      SignatureBlock,
    ] as Extensions,
    content: compose.bodyHtml,
    onUpdate: ({ editor: ed }) => {
      updateCompose(compose.id, { bodyHtml: ed.getHTML() })
    },
    onFocus: ({ editor: ed, event }) => {
      if (!snapBodyCaretToStartOnHeaderTab) return
      const rt = event.relatedTarget as Node | null
      if (!rt || !fieldsRef.current?.contains(rt)) return
      window.requestAnimationFrame(() => {
        if (!ed.view.hasFocus()) return
        try {
          ed.chain().setTextSelection(1).run()
        } catch {
          /* empty doc edge */
        }
      })
    },
    editorProps: {
      attributes: {
        class: cn(
          "prose prose-sm max-w-none px-3 py-2 text-sm text-foreground outline-none focus:outline-none",
          isInline
            ? "min-h-[200px]"
            : isXsSheet
              ? "min-h-[min(36vh,280px)]"
              : "min-h-[150px]"
        ),
      },
    },
  })

  const titleText = compose.subject || "Nouveau message"
  const bodyWithoutSig = stripSignature(compose.bodyHtml)
    .replace(/<p><\/p>/g, "")
    .trim()
  const hasContent =
    compose.subject.trim() !== "" ||
    compose.to.length > 0 ||
    compose.cc.length > 0 ||
    compose.bcc.length > 0 ||
    compose.attachments.length > 0 ||
    bodyWithoutSig !== ""

  const handleIdentityChange = useCallback(
    (identity: (typeof DEFAULT_IDENTITIES)[number]) => {
      if (compose.autoInsertSignature && editor) {
        const sigId = identity.defaultSignatureId
        const newHtml = insertSignatureHtml(editor.getHTML(), sigId)
        editor.commands.setContent(newHtml)
        updateCompose(compose.id, { from: identity, bodyHtml: newHtml, signatureId: sigId })
      } else {
        updateCompose(compose.id, { from: identity })
      }
    },
    [compose.id, compose.autoInsertSignature, editor, updateCompose]
  )

  const handleClose = () => {
    const threadInlineDiscard =
      isInline && compose.threadEmailId
        ? ({ discardThreadReplyDraft: true } as const)
        : undefined
    if (!hasContent) {
      closeCompose(compose.id, threadInlineDiscard)
    } else {
      updateCompose(compose.id, { savedAt: Date.now() })
      closeCompose(compose.id, threadInlineDiscard)
    }
  }

  const handleCloseRef = useRef(handleClose)
  handleCloseRef.current = handleClose

  useLayoutEffect(() => {
    if (!isXsSheet || !bindXsSheetClose) return
    bindXsSheetClose(() => {
      handleCloseRef.current()
    })
    return () => bindXsSheetClose(null)
  }, [isXsSheet, bindXsSheetClose, compose.id])

  const htmlToPreviewText = useCallback((html: string) => {
    return html
      .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, " ")
      .replace(/<[^>]+>/g, " ")
      .replace(/\s+/g, " ")
      .trim()
  }, [])

  const handleSend = useCallback(() => {
    if (compose.to.length === 0) return
    const bodyHtml = editor?.getHTML() ?? compose.bodyHtml
    const snapshot = cloneComposeForPendingSend({ ...compose, bodyHtml })
    closeCompose(compose.id, { sent: true })
    showPendingSendToast({
      onCommit: async () => {},
      onCancel: () => restoreComposeFromSnapshot(snapshot),
    })
  }, [
    closeCompose,
    compose,
    editor,
    restoreComposeFromSnapshot,
  ])

  const submitScheduledSendAt = useCallback(
    async (sendAt: Date) => {
      if (isEditingScheduled) return
      if (compose.to.length === 0) return
      setSendMenuOpen(false)
      const bodyHtml = editor?.getHTML() ?? compose.bodyHtml
      await scheduleSend({
        sendAtIso: sendAt.toISOString(),
        to: compose.to.map((c) => ({ name: c.name, email: c.email })),
        subject: compose.subject,
        previewText: htmlToPreviewText(bodyHtml).slice(0, 500),
        bodyHtml,
      })
      const whenLabel = sendAt.toLocaleString("fr-FR", {
        dateStyle: "medium",
        timeStyle: "short",
      })
      toast.message(`Ce mail sera envoyé le ${whenLabel}`)
      closeCompose(compose.id, { sent: true })
    },
    [
      isEditingScheduled,
      compose.bodyHtml,
      compose.id,
      compose.subject,
      compose.to,
      closeCompose,
      editor,
      htmlToPreviewText,
      scheduleSend,
    ]
  )

  const buildSchedulePayload = useCallback(
    (sendAtIso: string): ScheduleSendPayload | null => {
      if (compose.to.length === 0) return null
      const bodyHtml = editor?.getHTML() ?? compose.bodyHtml
      return {
        sendAtIso,
        to: compose.to.map((c) => ({ name: c.name, email: c.email })),
        subject: compose.subject,
        previewText: htmlToPreviewText(bodyHtml).slice(0, 500),
        bodyHtml,
      }
    },
    [compose.to, compose.subject, compose.bodyHtml, editor, htmlToPreviewText]
  )

  const saveScheduledEdit = useCallback(async () => {
    const id = compose.editingScheduledId
    if (!id) return
    const iso =
      compose.scheduledSendAtIso ?? new Date().toISOString()
    const payload = buildSchedulePayload(iso)
    if (!payload) return
    await requestUpdateScheduledSend(id, payload)
    toast.message("Modifications enregistrées")
    closeCompose(compose.id)
  }, [
    buildSchedulePayload,
    closeCompose,
    compose.editingScheduledId,
    compose.id,
    compose.scheduledSendAtIso,
    requestUpdateScheduledSend,
  ])

  const sendScheduledFromEditNow = useCallback(async () => {
    const id = compose.editingScheduledId
    if (!id) return
    setSendMenuOpen(false)
    const bodyHtml = editor?.getHTML() ?? compose.bodyHtml
    const snapshot = cloneComposeForPendingSend({ ...compose, bodyHtml })
    closeCompose(compose.id, { sent: true })
    showPendingSendToast({
      onCommit: async () => {
        const schedId = snapshot.editingScheduledId
        if (!schedId || snapshot.to.length === 0) return
        const iso = snapshot.scheduledSendAtIso ?? new Date().toISOString()
        const body = snapshot.bodyHtml
        const payload = {
          sendAtIso: iso,
          to: snapshot.to.map((c) => ({ name: c.name, email: c.email })),
          subject: snapshot.subject,
          previewText: htmlToPreviewText(body).slice(0, 500),
          bodyHtml: body,
        }
        await requestUpdateScheduledSend(schedId, payload)
        await requestSendScheduledNow(schedId)
      },
      onCancel: () => restoreComposeFromSnapshot(snapshot),
    })
  }, [
    closeCompose,
    compose,
    editor,
    htmlToPreviewText,
    requestSendScheduledNow,
    requestUpdateScheduledSend,
    restoreComposeFromSnapshot,
  ])

  const applyScheduledPlanAt = useCallback(
    async (sendAt: Date) => {
      const id = compose.editingScheduledId
      if (!id) return
      setSendMenuOpen(false)
      const iso = sendAt.toISOString()
      const payload = buildSchedulePayload(iso)
      if (!payload) return
      await requestUpdateScheduledSend(id, payload)
      updateCompose(compose.id, { scheduledSendAtIso: iso })
      const whenLabel = sendAt.toLocaleString("fr-FR", {
        dateStyle: "medium",
        timeStyle: "short",
      })
      toast.message(`Envoi planifié le ${whenLabel}`)
    },
    [
      buildSchedulePayload,
      compose.editingScheduledId,
      compose.id,
      requestUpdateScheduledSend,
      updateCompose,
    ]
  )

  const addFiles = useCallback((files: FileList | File[]) => {
    const newAttachments = Array.from(files).map((file) => ({
      id: `att-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      file,
      name: file.name,
      size: file.size,
      type: file.type,
    }))
    updateCompose(compose.id, {
      attachments: [...compose.attachments, ...newAttachments],
    })
  }, [compose.id, compose.attachments, updateCompose])

  const removeAttachment = useCallback((attId: string) => {
    updateCompose(compose.id, {
      attachments: compose.attachments.filter((a) => a.id !== attId),
    })
  }, [compose.id, compose.attachments, updateCompose])

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault()
    e.stopPropagation()
    setIsDragOver(false)
    if (e.dataTransfer.files.length > 0) {
      addFiles(e.dataTransfer.files)
    }
  }, [addFiles])

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

  const handleDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault()
    e.stopPropagation()
    if (e.currentTarget === e.target || !e.currentTarget.contains(e.relatedTarget as Node)) {
      setIsDragOver(false)
    }
  }, [])

  const showFromField = recipientsFocused || isXsSheet

  useLayoutEffect(() => {
    if (!isInline || !compose.focusToOnMount) return
    setRecipientsFocused(true)
  }, [isInline, compose.focusToOnMount])

  useEffect(() => {
    if (!recipientsFocused) return
    const handleClickOutside = (e: Event) => {
      const target = e.target as Node
      const root = isInline ? inlineRecipientShellRef.current : fieldsRef.current
      if (root && !root.contains(target)) {
        const el = e.target as HTMLElement | null
        const portal = el?.closest?.(
          "[data-radix-popper-content-wrapper], [data-radix-dropdown-menu-content], [data-slot='dropdown-menu-content'], [data-slot='popover-content']"
        )
        if (portal) return
        setRecipientsFocused(false)
        if (compose.showCc && compose.cc.length === 0) {
          updateCompose(compose.id, { showCc: false })
        }
        if (compose.showBcc && compose.bcc.length === 0) {
          updateCompose(compose.id, { showBcc: false })
        }
      }
    }
    document.addEventListener("pointerdown", handleClickOutside)
    return () => document.removeEventListener("pointerdown", handleClickOutside)
  }, [
    recipientsFocused,
    isInline,
    compose.showCc,
    compose.showBcc,
    compose.cc.length,
    compose.bcc.length,
    compose.id,
    updateCompose,
  ])

  useEffect(() => {
    if (!editor || editor.isDestroyed) return
    const next = compose.bodyHtml
    if (editor.getHTML() === next) return
    editor.commands.setContent(next, { emitUpdate: false })
  }, [compose.bodyHtml, compose.threadKind, editor])

  useEffect(() => {
    if (!compose.focusSubjectOnMount || isInline) return
    const id = window.requestAnimationFrame(() => {
      subjectInputRef.current?.focus()
      updateCompose(compose.id, { focusSubjectOnMount: false })
    })
    return () => window.cancelAnimationFrame(id)
  }, [compose.focusSubjectOnMount, isInline, compose.id, updateCompose])

  useEffect(() => {
    if (!compose.focusBodyOnMount || !editor || editor.isDestroyed) return
    let cancelled = false
    const outer = window.requestAnimationFrame(() => {
      window.requestAnimationFrame(() => {
        if (cancelled || !editor || editor.isDestroyed) return
        try {
          editor.chain().focus().setTextSelection(1).run()
        } catch {
          editor.chain().focus().run()
        }
        updateCompose(compose.id, { focusBodyOnMount: false })
      })
    })
    return () => {
      cancelled = true
      window.cancelAnimationFrame(outer)
    }
  }, [compose.focusBodyOnMount, compose.id, editor, updateCompose])

  const clearFocusToMount = useCallback(() => {
    updateCompose(compose.id, { focusToOnMount: false })
  }, [compose.id, updateCompose])

  const ThreadKindIcon =
    compose.threadKind === "forward"
      ? Forward
      : compose.threadKind === "replyAll"
        ? ReplyAll
        : Reply

  const recipientSummary =
    compose.to.length === 0
      ? "Destinataires"
      : compose.to.length === 1 && compose.to[0]
        ? compose.to[0].name === compose.to[0].email
          ? compose.to[0].email
          : `${compose.to[0].name} <${compose.to[0].email}>`
        : `${compose.to.length} destinataires`

  const showReplyAllInMenu = useMemo(
    () =>
      Boolean(
        threadSourceEmail &&
          collectThreadParticipants(threadSourceEmail).length > 1
      ),
    [threadSourceEmail]
  )

  const openInlinePreset = useCallback(
    (kind: "reply" | "replyAll" | "forward") => {
      if (!threadSourceEmail) return
      applyComposePreset(
        compose.id,
        buildThreadComposePreset(threadSourceEmail, kind)
      )
    },
    [threadSourceEmail, applyComposePreset, compose.id]
  )

  const openDockFromInline = useCallback(
    (opts?: { focusSubject?: boolean }) => {
      setRecipientsFocused(false)
      updateCompose(compose.id, {
        placement: "dock",
        threadEmailId: null,
        focusToOnMount: false,
        focusBodyOnMount: false,
        minimized: false,
        maximized: false,
        focusSubjectOnMount: Boolean(opts?.focusSubject),
      })
    },
    [compose.id, updateCompose]
  )

  const recipientFieldsProps = {
    compose,
    isInline,
    showFromField,
    updateCompose,
    handleIdentityChange,
    clearFocusToMount,
    subjectInputRef,
    onRecipientsActivate: () => setRecipientsFocused(true),
  }

  const modalContent = (
    <div
      data-compose-window
      className={cn(
        "relative flex flex-col overflow-hidden bg-mail-surface text-foreground",
        isInline
          ? "min-h-[360px] w-full rounded-xl border border-border shadow-none transition-shadow focus-within:shadow-[0_1px_4px_rgba(60,64,67,0.12)]"
          : isXsSheet
            ? "h-full min-h-0 w-full max-w-none flex-1 rounded-none shadow-none"
            : cn(
                "rounded-t-lg shadow-[0_-2px_8px_rgba(0,0,0,0.08),_-4px_0_12px_rgba(0,0,0,0.12),_4px_0_12px_rgba(0,0,0,0.12)]",
                compose.maximized
                  ? readCoarsePointerMatches()
                    ? "fixed inset-0 z-60 rounded-none"
                    : "fixed inset-12 z-60 rounded-lg"
                  : "h-[480px] w-[500px]"
              )
      )}
      onDrop={handleDrop}
      onDragOver={handleDragOver}
      onDragLeave={handleDragLeave}
    >
      {/* Hidden file inputs */}
      <input
        ref={fileInputRef}
        type="file"
        multiple
        className="hidden"
        onChange={(e) => {
          if (e.target.files && e.target.files.length > 0) {
            addFiles(e.target.files)
            e.target.value = ""
          }
        }}
      />
      <input
        ref={imageInputRef}
        type="file"
        multiple
        accept="image/*"
        className="hidden"
        onChange={(e) => {
          if (e.target.files && e.target.files.length > 0) {
            addFiles(e.target.files)
            e.target.value = ""
          }
        }}
      />

      {/* Drop overlay */}
      {isDragOver ? <ComposeDropOverlay /> : null}
      {isInline ? (
        <ComposeInlineRecipientHeader
          compose={compose}
          threadSourceEmail={threadSourceEmail}
          recipientSummary={recipientSummary}
          recipientsFocused={recipientsFocused}
          showReplyAllInMenu={showReplyAllInMenu}
          ThreadKindIcon={ThreadKindIcon}
          onOpenInlinePreset={openInlinePreset}
          onOpenDockFromInline={openDockFromInline}
          onActivateRecipients={() => setRecipientsFocused(true)}
          updateCompose={updateCompose}
          recipientFieldsProps={recipientFieldsProps}
          fieldsRef={fieldsRef}
          inlineRecipientShellRef={inlineRecipientShellRef}
        />
      ) : isXsSheet ? (
        <ComposeXsSheetHeader titleText={titleText} onClose={handleClose} />
      ) : (
        <>
          {/* Title bar */}
          <ComposeDockTitleBar
            titleText={titleText}
            maximized={compose.maximized}
            onMinimize={() => toggleMinimize(compose.id)}
            onMaximize={() => toggleMaximize(compose.id)}
            onClose={handleClose}
          />
        </>
      )}

      {!isInline && (
        <div ref={fieldsRef} className="flex shrink-0 flex-col">
          <ComposeRecipientFields {...recipientFieldsProps} />
        </div>
      )}

      {/* Editor */}
      <div className="min-h-0 flex-1 overflow-y-auto">
        <EditorContent editor={editor} />
      </div>

      <ComposeAttachmentsList
        attachments={compose.attachments}
        onRemove={removeAttachment}
      />

      {showFormatting ? <FormattingToolbar editor={editor} /> : null}

      <ComposeBottomToolbar
        compose={compose}
        editor={editor}
        isEditingScheduled={isEditingScheduled}
        showFormatting={showFormatting}
        sendMenuOpen={sendMenuOpen}
        setShowFormatting={setShowFormatting}
        setSendMenuOpen={setSendMenuOpen}
        handleSend={handleSend}
        saveScheduledEdit={saveScheduledEdit}
        sendScheduledFromEditNow={sendScheduledFromEditNow}
        applyScheduledPlanAt={applyScheduledPlanAt}
        submitScheduledSendAt={submitScheduledSendAt}
        handleClose={handleClose}
        fileInputRef={fileInputRef}
        imageInputRef={imageInputRef}
      />
    </div>
  )

  if (compose.minimized && !isInline && !isXsSheet) {
    return (
      <div
        className={cn(
          MAIL_COMPOSE_TITLEBAR_CLASS,
          "h-9 w-[280px] cursor-pointer shadow-lg transition-shadow hover:shadow-xl"
        )}
        onClick={() => toggleMinimize(compose.id)}
      >
        <span className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
          {titleText}
        </span>
        <div className="flex items-center gap-0.5">
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation()
              toggleMaximize(compose.id)
            }}
            className={cn("flex h-6 w-6 items-center justify-center rounded-full", MAIL_ICON_BTN)}
          >
            <Maximize2 className="h-3.5 w-3.5" />
          </button>
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation()
              handleClose()
            }}
            className={cn("flex h-6 w-6 items-center justify-center rounded-full", MAIL_ICON_BTN)}
          >
            <X className="h-3.5 w-3.5" />
          </button>
        </div>
      </div>
    )
  }

  if (compose.maximized && !isInline && !isXsSheet) {
    return (
      <>
        <div
          className="fixed inset-0 z-55 bg-black/50"
          onClick={() => toggleMaximize(compose.id)}
        />
        {modalContent}
      </>
    )
  }

  return modalContent
}

export function ComposeModalManager() {
  const { composeWindows } = useComposeWindows()
  const isXs = useIsXs()

  const nonMaximized = composeWindows.filter(
    (w) => !w.maximized && w.placement !== "inline"
  )
  const maximized = composeWindows.filter((w) => w.maximized && w.placement !== "inline")

  const xsSheetCloseRef = useRef<(() => void) | null>(null)
  const bindXsSheetClose = useCallback((fn: (() => void) | null) => {
    xsSheetCloseRef.current = fn
  }, [])

  /** Une seule fenêtre dock visible en xs : la plus récente (comportement type pile). */
  const xsActiveDock =
    isXs && nonMaximized.length > 0 ? nonMaximized[nonMaximized.length - 1] : null

  const handleXsSheetOpenChange = useCallback((open: boolean) => {
    if (!open) {
      xsSheetCloseRef.current?.()
    }
  }, [])

  const MODAL_WIDTH = 500
  const MINIMIZED_WIDTH = 280
  const GAP = 12
  const RIGHT_OFFSET = 80

  const positions = useMemo(() => {
    const reversed = [...nonMaximized].reverse()
    const result: { id: string; right: number; hidden: boolean }[] = []
    let cursor = RIGHT_OFFSET
    for (let i = 0; i < reversed.length; i++) {
      const w = reversed[i]
      const width = w.minimized ? MINIMIZED_WIDTH : MODAL_WIDTH
      result.push({
        id: w.id,
        right: cursor,
        hidden: i >= 2 && !w.minimized,
      })
      cursor += width + GAP
    }
    return result
  }, [nonMaximized])

  if (isXs) {
    return (
      <>
        <Sheet open={xsActiveDock != null} onOpenChange={handleXsSheetOpenChange}>
          <SheetContent
            side="bottom"
            hideClose
            overlayClassName="z-[60]"
            className="z-[61] h-[100dvh] max-h-[100dvh] w-full gap-0 rounded-none border-0 p-0 shadow-none duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:slide-in-from-bottom data-[state=closed]:slide-out-to-bottom overflow-hidden pb-[env(safe-area-inset-bottom)]"
          >
            <SheetTitle className="sr-only">
              {(xsActiveDock?.subject ?? "").trim() || "Nouveau message"}
            </SheetTitle>
            {xsActiveDock ? (
              <ComposeWindow
                key={xsActiveDock.id}
                compose={xsActiveDock}
                isXsSheet
                bindXsSheetClose={bindXsSheetClose}
              />
            ) : null}
          </SheetContent>
        </Sheet>

        {maximized.map((compose) => (
          <div key={compose.id} className="pointer-events-auto">
            <ComposeWindow compose={compose} />
          </div>
        ))}
      </>
    )
  }

  return (
    <>
      {nonMaximized.map((compose) => {
        const pos = positions.find((p) => p.id === compose.id)
        if (!pos) return null
        return (
          <div
            key={compose.id}
            className={cn(
              "pointer-events-auto fixed bottom-0 z-50 transition-all duration-300",
              pos.hidden && "invisible"
            )}
            style={{ right: pos.right }}
          >
            <ComposeWindow compose={compose} />
          </div>
        )
      })}

      {maximized.map((compose) => (
        <div key={compose.id} className="pointer-events-auto">
          <ComposeWindow compose={compose} />
        </div>
      ))}
    </>
  )
}
