import {
    ChevronLeft,
    ChevronRight,
    Download,
    ExternalLink,
} from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogTitle,
} from '@/components/ui/dialog';
import { cn } from '@/lib/utils';
import type { TicketAttachment } from '@/types';

/** The least a viewer needs: the ticket's own files and a comment's file look alike. */
export type ViewableAttachment = {
    id: number;
    name: string;
    url: string;
    download_url: string;
    is_image: boolean;
    mime_type?: string | null;
    human_size?: string;
};

/**
 * Image and PDF previews, opened over the page rather than in a new tab.
 *
 * A fault code is read while typing a reply, so the picture has to stay next to
 * the ticket instead of replacing it. Arrow keys and the on-screen buttons walk
 * through everything attached to the ticket, Escape closes, and the download
 * link is always one click away.
 *
 * Only formats the browser renders itself are shown this way — a photo, a
 * screenshot or a PDF. Anything else (a HEIC off an iPhone, say) is offered as
 * a download, because a blank preview would be worse than no preview.
 */
export function AttachmentViewer({
    attachments,
    startIndex = 0,
    open,
    onOpenChange,
}: {
    attachments: ViewableAttachment[];
    startIndex?: number;
    open: boolean;
    onOpenChange: (open: boolean) => void;
}) {
    const [index, setIndex] = useState(startIndex);

    // Opening from a different thumbnail starts at that one.
    useEffect(() => {
        if (open) {
            setIndex(startIndex);
        }
    }, [open, startIndex]);

    const count = attachments.length;
    const current = attachments[index] ?? null;

    const step = useCallback(
        (delta: number) => {
            if (count === 0) {
                return;
            }

            setIndex((position) => (position + delta + count) % count);
        },
        [count],
    );

    /**
     * Arrow keys walk the photos and Escape closes. The handler sits on the
     * dialog itself, which is where focus lands when it opens, so it works
     * whether the viewer was opened with a click or from the keyboard.
     */
    const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
        if (event.key === 'ArrowRight') {
            event.preventDefault();
            step(1);

            return;
        }

        if (event.key === 'ArrowLeft') {
            event.preventDefault();
            step(-1);

            return;
        }

        if (event.key === 'Escape') {
            event.preventDefault();
            onOpenChange(false);
        }
    };

    if (current === null) {
        return null;
    }

    const isPdf =
        current.mime_type === 'application/pdf' ||
        current.name.toLowerCase().endsWith('.pdf');

    return (
        <Dialog open={open} onOpenChange={onOpenChange}>
            <DialogContent
                className="flex max-h-[92vh] w-full flex-col gap-0 overflow-hidden bg-background/95 p-0 backdrop-blur sm:max-w-4xl"
                data-test="attachment-viewer"
                onKeyDown={onKeyDown}
            >
                {/* Right padding leaves room for the dialog's own close
                    button, which sits in the corner. */}
                <div className="flex items-center gap-3 border-b border-hairline py-3 pr-12 pl-4">
                    <div className="min-w-0 flex-1">
                        <DialogTitle className="truncate text-sm font-medium">
                            {current.name}
                        </DialogTitle>
                        <DialogDescription className="truncate text-xs text-muted-foreground">
                            {[
                                current.human_size,
                                count > 1 ? `${index + 1} of ${count}` : null,
                            ]
                                .filter(Boolean)
                                .join(' · ')}
                        </DialogDescription>
                    </div>

                    <a
                        href={current.url}
                        target="_blank"
                        rel="noreferrer"
                        className="flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
                        aria-label="Open in a new tab"
                        title="Open in a new tab"
                    >
                        <ExternalLink className="size-4" aria-hidden />
                    </a>

                    <a
                        href={current.download_url}
                        className="flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
                        aria-label={`Download ${current.name}`}
                        title="Download"
                    >
                        <Download className="size-4" aria-hidden />
                    </a>
                </div>

                <div className="relative flex min-h-0 flex-1 items-center justify-center bg-black/5 dark:bg-black/40">
                    {count > 1 ? (
                        <button
                            type="button"
                            onClick={() => step(-1)}
                            aria-label="Previous attachment"
                            className="absolute left-2 z-10 flex size-9 items-center justify-center rounded-full bg-background/90 shadow-sm ring-1 ring-hairline hover:bg-accent"
                        >
                            <ChevronLeft className="size-4" aria-hidden />
                        </button>
                    ) : null}

                    {isPdf ? (
                        // The browser's own PDF viewer, in the modal: a claim
                        // form is read here rather than downloaded and opened.
                        <iframe
                            src={current.url}
                            title={current.name}
                            className="h-[70vh] w-full"
                        />
                    ) : current.is_image ? (
                        <img
                            src={current.url}
                            alt={current.name}
                            className="max-h-[70vh] w-auto max-w-full object-contain"
                        />
                    ) : (
                        <div className="flex flex-col items-center gap-3 px-6 py-16 text-center">
                            <p className="text-sm font-medium">
                                This file cannot be previewed here.
                            </p>
                            <p className="max-w-sm text-xs text-muted-foreground">
                                HEIC photos and other formats a browser does not
                                render are kept exactly as they arrived — use
                                the download button above.
                            </p>
                        </div>
                    )}

                    {count > 1 ? (
                        <button
                            type="button"
                            onClick={() => step(1)}
                            aria-label="Next attachment"
                            className="absolute right-2 z-10 flex size-9 items-center justify-center rounded-full bg-background/90 shadow-sm ring-1 ring-hairline hover:bg-accent"
                        >
                            <ChevronRight className="size-4" aria-hidden />
                        </button>
                    ) : null}
                </div>

                {count > 1 ? (
                    <div className="flex gap-2 overflow-x-auto border-t border-hairline px-4 py-3">
                        {attachments.map((attachment, position) => (
                            <button
                                key={attachment.id}
                                type="button"
                                onClick={() => setIndex(position)}
                                aria-label={attachment.name}
                                aria-current={position === index}
                                className={cn(
                                    'size-12 shrink-0 overflow-hidden rounded-md border bg-muted',
                                    position === index
                                        ? 'border-solar ring-2 ring-solar/40'
                                        : 'border-hairline hover:border-ring/60',
                                )}
                            >
                                {attachment.is_image ? (
                                    <img
                                        src={attachment.url}
                                        alt=""
                                        className="size-full object-cover"
                                    />
                                ) : (
                                    <span className="flex size-full items-center justify-center text-[10px] font-medium text-muted-foreground">
                                        PDF
                                    </span>
                                )}
                            </button>
                        ))}
                    </div>
                ) : null}
            </DialogContent>
        </Dialog>
    );
}

/**
 * The state a gallery needs: which attachment a click opened, if any.
 *
 * Kept here so the ticket page and any other list of files share one way of
 * opening the viewer.
 */
export function useAttachmentViewer(attachments: TicketAttachment[]) {
    const [openIndex, setOpenIndex] = useState<number | null>(null);

    return {
        viewer: (
            <AttachmentViewer
                attachments={attachments}
                startIndex={openIndex ?? 0}
                open={openIndex !== null}
                onOpenChange={(next) =>
                    setOpenIndex(next ? (openIndex ?? 0) : null)
                }
            />
        ),
        open: (index: number) => setOpenIndex(index),
    };
}
