import { useTranslation } from "react-i18next"
import { Link, useParams } from "@tanstack/react-router"
import { useEffect, useMemo, useRef, useState } from "react"
import { createPortal } from "react-dom"
import clsx from "clsx"
import { DateHelper } from "@/features/utils/date-helper"
import { Thread } from "@/features/api/gen/models"
import { ThreadItemSenders } from "./thread-item-senders"
import { Badge } from "@/features/ui/components/badge"
import { ThreadDragPreview } from "./thread-drag-preview"
import { PORTALS } from "@/features/config/constants"
import { Checkbox, Tooltip } from "@gouvfr-lasuite/cunningham-react"
import { Icon, IconSize, IconType } from "@gouvfr-lasuite/ui-kit"
import { AssigneesAvatarGroup } from "@/features/ui/components/assignees-avatar-group"
import { LabelBadge } from "@/features/ui/components/label-badge"
import { useLayoutDragContext } from "@/features/layouts/components/layout-context"
import ViewHelper from "@/features/utils/view-helper"
import useCanEditThreads from "@/features/message/use-can-edit-threads"
import { ThreadListboxItemProps } from "../../hooks/use-thread-listbox"

type ThreadItemProps = {
    thread: Thread
    isSelected: boolean
    onToggle: (threadId: string) => void
    onSelectRange: (threadId: string) => void
    selectedThreadIds: Set<string>
    isSelectionMode: boolean
} & ThreadListboxItemProps

export const ThreadItem = ({ thread, isSelected, onToggle, onSelectRange, selectedThreadIds, isSelectionMode, tabIndex, itemRef, onFocusItem }: ThreadItemProps) => {
    const { t, i18n } = useTranslation();
    const params = useParams({ strict: false }) as { mailboxId?: string; threadId?: string }
    const [isDragging, setIsDragging] = useState(false)
    const [isExiting, setIsExiting] = useState(false)
    const { setIsDragging: setGlobalDragging, setDragAction } = useLayoutDragContext();
    const dragPreviewContainer = useRef(document.getElementById(PORTALS.DRAG_PREVIEW));
    const threadDate = useMemo(() => {
        if (ViewHelper.isInboxView() && thread.active_messaged_at) {
            return thread.active_messaged_at;
        }
        if (ViewHelper.isArchivedView() && thread.archived_messaged_at) {
            return thread.archived_messaged_at;
        }
        if (ViewHelper.isDraftsView() && thread.draft_messaged_at) {
            return thread.draft_messaged_at
        }
        if ((ViewHelper.isOutboxView() || ViewHelper.isSentView()) && thread.sender_messaged_at) {
            return thread.sender_messaged_at;
        }
        if (ViewHelper.isTrashedView() && thread.trashed_messaged_at) {
            return thread.trashed_messaged_at;
        }

        // Draft-only threads have messaged_at=null, fall back to draft_messaged_at
        return thread.messaged_at || thread.draft_messaged_at;
    }, [thread])

    const hasUnread = useMemo(() => {
        const access = thread.accesses.find((a) => a.mailbox.id === params?.mailboxId)
        const compareDate = thread.messaged_at;
        if (!access || !compareDate) return false
        if (!access.read_at) return true
        return new Date(compareDate) > new Date(access.read_at)
    }, [thread, params?.mailboxId])

    const hasSelection = isSelectionMode || selectedThreadIds.size > 0;
    const showCheckbox = hasSelection;

    // Used by drop zones (folders, label auto-archive) to decide whether
    // the dragged threads can be mutated. Expressed as a dataTransfer
    // type so drop zones can read it on dragover — JSON payloads are only
    // readable on drop per the browser security model.
    const dragThreadIds = useMemo(
        () => isSelectionMode ? Array.from(selectedThreadIds) : [thread.id],
        [isSelectionMode, selectedThreadIds, thread.id]
    );
    const hasEditableInDrag = useCanEditThreads(dragThreadIds);

    const handleItemClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
        onFocusItem();
        // Keyboard activation (Enter on the focused option) fires a click
        // with detail === 0: let it navigate even while a selection is
        // active, it is the only pointer-free way to open a thread.
        if (e.detail === 0) return;
        if (e.shiftKey) {
            e.preventDefault();
            onSelectRange(thread.id);
        } else if (e.ctrlKey || e.metaKey || hasSelection) {
            e.preventDefault();
            onToggle(thread.id);
        }
        // Otherwise, let the Link handle navigation normally
    };

    const handleDragStart = (e: React.DragEvent<HTMLAnchorElement>) => {
        setIsDragging(true)
        setGlobalDragging(true)

        e.dataTransfer.setData('application/json', JSON.stringify({
            type: 'thread',
            threadIds: dragThreadIds,
            labels: isSelectionMode ? [] : thread.labels.map((label) => label.id),
            hasEditable: hasEditableInDrag,
        }));
        e.dataTransfer.setData('text/thread-drag', '');
        // Advertised on dragover so folder drop zones can refuse drops
        // when the dragged selection has no editable thread (archive,
        // spam, trash and restore-to-inbox all require edit rights).
        if (hasEditableInDrag) {
            e.dataTransfer.setData('text/thread-editable', '');
        }

        // Hide native drag image by using an offscreen empty element as drag image
        // (Safari-compatible — `new Image()` with an inline data URL is unreliable
        // because Safari requires the image to be loaded/in the DOM).
        // The ThreadDragPreview portal follows the cursor instead.
        const ghost = document.createElement('div');
        ghost.style.cssText = 'position:absolute;top:-1000px;left:-1000px;width:1px;height:1px;';
        document.body.appendChild(ghost);
        e.dataTransfer.setDragImage(ghost, 0, 0);
        setTimeout(() => document.body.removeChild(ghost), 0);
    }
    const handleDragEnd = () => {
        setIsDragging(false);
        setGlobalDragging(false);
        setIsExiting(true);
    };

    const handleExitEnd = () => {
        setIsExiting(false);
        setDragAction(null);
    };

    const dragCount = selectedThreadIds.size > 0 ? selectedThreadIds.size : 1;

    // Clear any pending drag action if the item unmounts before the
    // exit animation completes (e.g. archived after label assignment),
    // otherwise the stale action would leak into the next drag preview.
    useEffect(() => () => setDragAction(null), [setDragAction]);


    return (
        <>
            <Link
                to="/mailbox/$mailboxId/thread/$threadId"
                params={{ mailboxId: params?.mailboxId ?? '', threadId: thread.id }}
                search={true}
                className={clsx(
                    'thread-item',
                    {
                        'thread-item--active': thread.id === params?.threadId,
                        'thread-item--dragging': isDragging,
                        'thread-item--selected': isSelected,
                    },
                )}
                data-unread={hasUnread}
                draggable
                onDragStart={handleDragStart}
                onDragEnd={handleDragEnd}
                onClick={handleItemClick}
                onFocus={onFocusItem}
                tabIndex={tabIndex}
                ref={itemRef}
                role="option"
                aria-selected={isSelected}
            >
                <div>
                    {showCheckbox && (
                        // Mouse-only affordance, purely presentational: the option
                        // itself carries the selection state (aria-selected) and a
                        // focusable checkbox would add a second tab stop, hence
                        // aria-hidden + tabIndex=-1. pointer-events:none (see SCSS)
                        // lets the click fall through to the Link, where
                        // handleItemClick owns the selection logic; the checkbox
                        // only reflects `checked`. Driving it from its own click
                        // handler would fight handleItemClick's preventDefault and
                        // leave it visually out of sync.
                        <span aria-hidden="true" className="thread-item__checkbox-wrapper">
                            <Checkbox
                                checked={isSelected}
                                tabIndex={-1}
                                className="thread-item__checkbox"
                            />
                        </span>
                    )}
                    <div className="thread-item__read-indicator" />
                </div>
                <div>
                    <div className="thread-item__row">
                        <div className="thread-item__column">
                            {thread.sender_names && thread.sender_names.length > 0 && (
                                <ThreadItemSenders senders={thread.sender_names} />
                            )}
                        </div>
                        <div className="thread-item__column thread-item__column--metadata">
                            {(threadDate || thread.messaged_at) && (
                                <span className="thread-item__date">
                                    {DateHelper.formatDate((threadDate || thread.messaged_at)!, i18n.resolvedLanguage)}
                                </span>
                            )}
                        </div>
                    </div>
                    <div className="thread-item__row thread-item__row--subject">
                        <div className="thread-item__column">
                            <p className="thread-item__subject">{thread.subject || t('No subject')}</p>
                        </div>
                        <div className="thread-item__column thread-item__column--badges">
                            {thread.has_draft && (
                                <Badge aria-label={t('Draft')} title={t('Draft')} color="neutral" variant="tertiary" compact>
                                    <Icon
                                        type={IconType.FILLED}
                                        name="mode_edit"
                                        className="icon--size-sm"
                                        aria-hidden="true"
                                    />
                                </Badge>
                            )}
                            {thread.has_attachments ? (
                                <Badge
                                    aria-label={t('Attachments')}
                                    title={t('Attachments')}
                                    color="neutral"
                                    variant="tertiary"
                                    compact>
                                    <Icon
                                        name="attachment"
                                        size={IconSize.SMALL}
                                        aria-hidden="true"
                                    />
                                </Badge>
                            ) : null}
                            {thread.has_starred && (
                                <Badge
                                    aria-label={t('Starred')}
                                    title={t('Starred')}
                                    color="yellow"
                                    variant="tertiary"
                                    compact>
                                    <Icon
                                        name="star"
                                        size={IconSize.SMALL}
                                        aria-hidden="true"
                                    />
                                </Badge>
                            )}
                            {thread.has_unread_mention && (
                                <Badge
                                    aria-label={t('Unread mention')}
                                    title={t('Unread mention')}
                                    color="warning"
                                    variant="tertiary"
                                    compact>
                                    <Icon
                                        type={IconType.OUTLINED}
                                        name="alternate_email"
                                        aria-hidden="true"
                                        className="icon--size-sm"
                                    />
                                </Badge>
                            )}
                            {thread.has_delivery_failed && (
                                <Badge
                                    aria-label={t('Delivery failed')}
                                    title={t('Some recipients have not received this message!')}
                                    color="error"
                                    variant="tertiary"
                                    compact>
                                    <Icon
                                        name="error"
                                        type={IconType.OUTLINED}
                                        size={IconSize.SMALL}
                                        aria-hidden="true"
                                    />
                                </Badge>
                            )}
                            {!thread.has_delivery_failed && thread.has_delivery_pending && (
                                <Badge
                                    aria-label={t('Delivering')}
                                    title={t('This message has not yet been delivered to all recipients.')}
                                    color="warning"
                                    variant="tertiary"
                                    compact>
                                    <Icon
                                        name="update"
                                        type={IconType.OUTLINED}
                                        size={IconSize.SMALL}
                                        aria-hidden="true"
                                    />
                                </Badge>
                            )}
                            {thread.assigned_users.length > 0 && (
                                <Tooltip
                                    content={t('Assigned to {{names}}', {
                                        names: thread.assigned_users.map((u) => u.name).join(', '),
                                    })}
                                >
                                    <span
                                        aria-label={t('Assigned to {{names}}', {
                                            names: thread.assigned_users.map((u) => u.name).join(', '),
                                        })}
                                    >
                                        <AssigneesAvatarGroup
                                            users={thread.assigned_users}
                                            maxAvatars={2}
                                            overflowMode="replace-last"
                                        />
                                    </span>
                                </Tooltip>
                            )}
                        </div>
                    </div>
                    <div className="thread-item__row">
                     {thread.labels.length > 0 && (
                         <div className="thread-item__labels">
                             {thread.labels.map((label) => (
                                 <LabelBadge key={label.id} label={label} compact />
                             ))}
                         </div>
                     )}
                 </div>
                </div>
            </Link>
            {(isDragging || isExiting) && dragPreviewContainer.current && createPortal(
                <ThreadDragPreview
                    count={dragCount}
                    exiting={isExiting}
                    onExitEnd={handleExitEnd}
                />,
                dragPreviewContainer.current
            )}
        </>
    )
}
