'use client';

import { ImageCompareView, resolveCompareTargetIndex } from '@/components/image-compare-view';
import { Button } from '@/components/ui/button';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle
} from '@/components/ui/dialog';
import { useI18n } from '@/lib/i18n';
import { getActivityLogLevelLabel } from '@/lib/image-display-labels';
import { buildLogScopeDiagnostics, filterLogsByScope, resolveLogClientRequestIds } from '@/lib/log-filter';
import { cn } from '@/lib/utils';
import {
    Copy,
    Download,
    GitCompare,
    Grid,
    Loader2,
    RefreshCcw,
    Send,
    Share2,
    Activity,
    Trash2,
    ImageIcon
} from 'lucide-react';
import Image from 'next/image';
import * as React from 'react';

type ImageInfo = {
    path: string;
    filename: string;
    clientRequestId?: string;
    storageMode?: 'fs' | 'indexeddb';
};

type ImageActionTarget = {
    filename: string;
    storageMode?: ImageInfo['storageMode'];
};

type ImageOutputProps = {
    imageBatch: ImageInfo[] | null;
    viewMode: 'grid' | number;
    onViewChange: (view: 'grid' | number) => void;
    altText?: string;
    isLoading: boolean;
    onSendToEdit: (filename: string, storageMode?: ImageInfo['storageMode']) => void;
    onDownloadImage: (filename: string, storageMode?: ImageInfo['storageMode']) => void;
    onShareImage: (filename: string, storageMode?: ImageInfo['storageMode']) => void;
    onCreateVariant: () => void;
    onReusePrompt: () => void;
    failureMessage?: string | null;
    onRetry?: () => void;
    compareImage?: ImageInfo | null;
    compareImageLabel?: string;
    canCreateVariant: boolean;
    canReusePrompt: boolean;
    currentMode: 'generate' | 'edit';
    baseImagePreviewUrl: string | null;
    streamingPreviewImages?: Map<number, string>;
    isStreamingRequest?: boolean;
    clientPasswordHash: string | null;
    canOpenLogs: boolean;
    openLogsSignal?: number;
    logClientRequestIds?: string[];
    logFilenames?: string[];
};

type LogEntry = {
    id: number;
    at: string;
    level: 'debug' | 'info' | 'warn' | 'error';
    message: string;
    context?: string;
    clientRequestId?: string;
    filenames?: string[];
};

type ImageDimensions = {
    width: number;
    height: number;
};

export function buildImageActionTarget(image: ImageInfo | null): ImageActionTarget | null {
    if (!image) return null;
    return {
        filename: image.filename,
        ...(image.storageMode ? { storageMode: image.storageMode } : {})
    };
}

export function formatLogTime(value: string, locale: string): string {
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return value;
    return date.toLocaleTimeString(locale);
}

function formatLogScopeValues(values: string[]): string {
    return values.length > 0 ? values.join(', ') : '-';
}

function readImageDimensionsFromSource(source: string): Promise<ImageDimensions | null> {
    return new Promise((resolve) => {
        const image = new window.Image();
        image.onload = () => {
            if (!image.naturalWidth || !image.naturalHeight) {
                resolve(null);
                return;
            }
            resolve({
                width: image.naturalWidth,
                height: image.naturalHeight
            });
        };
        image.onerror = () => resolve(null);
        image.src = source;
    });
}

const getGridColsClass = (count: number): string => {
    if (count <= 1) return 'grid-cols-1';
    if (count <= 4) return 'grid-cols-2';
    if (count <= 9) return 'grid-cols-3';
    return 'grid-cols-3';
};

type ResultActionButtonProps = {
    icon: React.ReactNode;
    label: string;
    onClick?: () => void;
    disabled?: boolean;
    active?: boolean;
    emphasized?: boolean;
    iconOnly?: boolean;
};

function ResultActionButton({
    icon,
    label,
    onClick,
    disabled = false,
    active = false,
    emphasized = false,
    iconOnly = false
}: ResultActionButtonProps) {
    return (
        <Button
            variant='ghost'
            size='sm'
            onClick={onClick}
            disabled={disabled}
            className={cn(
                'touch-target text-muted-foreground hover:text-foreground shrink-0 rounded-md px-3 text-xs transition-[background-color,color,box-shadow,transform] enabled:active:scale-[0.96] disabled:opacity-50 lg:px-2',
                active && 'bg-accent text-accent-foreground hover:text-accent-foreground',
                emphasized && 'border-border/70 bg-card/80 border'
            )}
            aria-label={iconOnly ? label : undefined}>
            {icon}
            {!iconOnly && <span>{label}</span>}
        </Button>
    );
}

export function ImageOutput({
    imageBatch,
    viewMode,
    onViewChange,
    altText,
    isLoading,
    onSendToEdit,
    onDownloadImage,
    onShareImage,
    onCreateVariant,
    onReusePrompt,
    failureMessage = null,
    onRetry,
    compareImage = null,
    compareImageLabel,
    canCreateVariant,
    canReusePrompt,
    currentMode,
    baseImagePreviewUrl,
    streamingPreviewImages,
    isStreamingRequest = false,
    clientPasswordHash,
    canOpenLogs,
    openLogsSignal,
    logClientRequestIds = [],
    logFilenames = []
}: ImageOutputProps) {
    const { locale, t } = useI18n();
    const resolvedAltText = altText ?? t('output.alt');
    const [isLogDialogOpen, setIsLogDialogOpen] = React.useState(false);
    const [logs, setLogs] = React.useState<LogEntry[]>([]);
    const [logConnectionState, setLogConnectionState] = React.useState<'idle' | 'connected' | 'error'>('idle');
    const [copiedLogScopeText, setCopiedLogScopeText] = React.useState<string | null>(null);
    const [compareSelection, setCompareSelection] = React.useState<{
        imageBatch: ImageInfo[];
        selectedImageIndex: number;
        compareTargetFilename: string;
    } | null>(null);
    const [imageDimensions, setImageDimensions] = React.useState<Record<string, ImageDimensions>>({});
    const logEndRef = React.useRef<HTMLDivElement | null>(null);
    const resolvedLogClientRequestIds = React.useMemo(
        () => resolveLogClientRequestIds({ logs, clientRequestIds: logClientRequestIds, filenames: logFilenames }),
        [logClientRequestIds, logFilenames, logs]
    );
    const filteredLogs = React.useMemo(
        () => filterLogsByScope({ logs, clientRequestIds: resolvedLogClientRequestIds, filenames: [] }) as LogEntry[],
        [logs, resolvedLogClientRequestIds]
    );
    const logScopeDiagnostics = React.useMemo(
        () =>
            buildLogScopeDiagnostics({
                clientRequestIds: logClientRequestIds,
                filenames: logFilenames,
                resolvedClientRequestIds: resolvedLogClientRequestIds
            }),
        [logClientRequestIds, logFilenames, resolvedLogClientRequestIds]
    );
    const hasSelectedImageBatch = !!imageBatch && imageBatch.length > 0;
    const hasLogScope = resolvedLogClientRequestIds.length > 0;
    const hasScopeCandidate = logClientRequestIds.length > 0 || logFilenames.length > 0;
    const visibleLogs = React.useMemo(() => (hasLogScope ? filteredLogs : []), [filteredLogs, hasLogScope]);
    const showCarousel = Boolean(imageBatch && imageBatch.length > 1);
    const selectedImageIndex =
        imageBatch && imageBatch.length > 0
            ? typeof viewMode === 'number' && imageBatch[viewMode]
                ? viewMode
                : showCarousel
                  ? null
                  : 0
            : null;
    const selectedImage = selectedImageIndex === null ? null : imageBatch?.[selectedImageIndex] || null;
    const sameBatchCompareTargetIndex = imageBatch
        ? resolveCompareTargetIndex(imageBatch.length, selectedImageIndex)
        : null;
    const sameBatchCompareTargetImage =
        sameBatchCompareTargetIndex === null ? null : imageBatch?.[sameBatchCompareTargetIndex] || null;
    const compareTargetImage = sameBatchCompareTargetImage || compareImage || null;
    const selectedImageDimensions = selectedImage ? imageDimensions[selectedImage.filename] : null;
    const hasFailure = !isLoading && !hasSelectedImageBatch && Boolean(failureMessage);
    const hasEditReferencePreview = currentMode === 'edit' && Boolean(baseImagePreviewUrl);
    const isGenerateEmptyState = !isLoading && !hasFailure && !hasSelectedImageBatch && currentMode === 'generate';
    const previewStateLabel = isLoading
        ? t('output.progressDeveloping')
        : hasFailure
          ? t('output.failedTitle')
          : hasEditReferencePreview
            ? t('output.editReferenceReady')
            : currentMode === 'edit'
              ? t('output.editReferenceNeeded')
              : imageBatch && imageBatch.length > 0
                ? t('output.previewReady')
                : t('output.emptyTitle');
    const previewMetaItems = [
        !isLoading && imageBatch && imageBatch.length > 0
            ? selectedImageIndex === null
                ? t('output.selectImageForActions')
                : t('output.selectedImageMeta', {
                      index: selectedImageIndex + 1,
                      count: imageBatch.length
                  })
            : null,
        !isLoading && selectedImageDimensions
            ? t('output.imageDimensions', {
                  width: selectedImageDimensions.width,
                  height: selectedImageDimensions.height
              })
            : null,
        isLoading ? (isStreamingRequest ? t('output.streaming') : t('output.progressGenerating')) : null
    ].filter((item): item is string => Boolean(item));
    const canUseSelectedImageActions = !isLoading && !!selectedImage;
    const canCompareImages = !isLoading && !!selectedImage && !!compareTargetImage;
    const isCompareView =
        !!compareSelection &&
        compareSelection.imageBatch === imageBatch &&
        compareSelection.selectedImageIndex === selectedImageIndex &&
        compareSelection.compareTargetFilename === compareTargetImage?.filename;
    const compareReferenceLabel = sameBatchCompareTargetImage
        ? selectedImageIndex === 0
            ? t('output.compareOther')
            : t('output.compareReference')
        : (compareImageLabel ?? t('output.compareReference'));

    const handleSendClick = () => {
        const target = buildImageActionTarget(selectedImage);
        if (!target) return;
        onSendToEdit(target.filename, target.storageMode);
    };

    const handleDownloadClick = () => {
        const target = buildImageActionTarget(selectedImage);
        if (!target) return;
        onDownloadImage(target.filename, target.storageMode);
    };

    const handleShareClick = () => {
        const target = buildImageActionTarget(selectedImage);
        if (!target) return;
        onShareImage(target.filename, target.storageMode);
    };

    const handleCompareClick = () => {
        if (canCompareImages && imageBatch && selectedImageIndex !== null && compareTargetImage) {
            setCompareSelection({ imageBatch, selectedImageIndex, compareTargetFilename: compareTargetImage.filename });
        }
    };

    const handleImageLoad = React.useCallback((filename: string, event: React.SyntheticEvent<HTMLImageElement>) => {
        const source = event.currentTarget.currentSrc || event.currentTarget.src;
        if (!source) return;

        void readImageDimensionsFromSource(source).then((nextDimensions) => {
            if (!nextDimensions) return;
            setImageDimensions((current) => {
                const currentDimensions = current[filename];
                if (
                    currentDimensions?.width === nextDimensions.width &&
                    currentDimensions.height === nextDimensions.height
                ) {
                    return current;
                }
                return { ...current, [filename]: nextDimensions };
            });
        });
    }, []);

    const handleLogDialogOpenChange = (open: boolean) => {
        setIsLogDialogOpen(open);
        if (!open) {
            setLogConnectionState('idle');
            setCopiedLogScopeText(null);
        }
    };

    const handleCopyLogScope = async () => {
        try {
            if (!navigator.clipboard?.writeText) {
                throw new Error('Clipboard API unavailable.');
            }
            await navigator.clipboard.writeText(logScopeDiagnostics.copyText);
            setCopiedLogScopeText(logScopeDiagnostics.copyText);
            window.setTimeout(() => {
                setCopiedLogScopeText((current) => (current === logScopeDiagnostics.copyText ? null : current));
            }, 1500);
        } catch (error) {
            console.error('复制日志诊断范围失败。', error);
        }
    };

    React.useEffect(() => {
        if (!isLogDialogOpen || !canOpenLogs) return;

        const abortController = new AbortController();
        const readLogs = async () => {
            try {
                const response = await fetch('/api/logs', {
                    headers: clientPasswordHash ? { Authorization: `Bearer ${clientPasswordHash}` } : {},
                    signal: abortController.signal
                });
                if (!response.ok || !response.body) {
                    setLogConnectionState('error');
                    return;
                }

                setLogConnectionState('connected');
                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let buffer = '';

                const processEvent = (rawEvent: string) => {
                    const dataLines = rawEvent
                        .split(/\r?\n/)
                        .filter((line) => line.startsWith('data: '))
                        .map((line) => line.slice(6));
                    if (dataLines.length === 0) return;

                    let entry: LogEntry;
                    try {
                        entry = JSON.parse(dataLines.join('\n')) as LogEntry;
                    } catch (error) {
                        console.error('解析日志事件失败。', error);
                        return;
                    }

                    setLogs((prevLogs) => {
                        const nextLogs = prevLogs.some((log) => log.id === entry.id)
                            ? prevLogs
                            : [...prevLogs, entry].slice(-300);
                        return nextLogs;
                    });
                };

                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;

                    buffer += decoder.decode(value, { stream: true });
                    const events = buffer.split(/\n\n|\r\n\r\n/);
                    buffer = events.pop() || '';
                    events.forEach(processEvent);
                }

                const remainingEvent = buffer.trim();
                if (remainingEvent) {
                    processEvent(remainingEvent);
                }
                setLogConnectionState('idle');
            } catch (error) {
                if (abortController.signal.aborted) return;
                console.error('读取日志流失败。', error);
                setLogConnectionState('error');
            }
        };

        void readLogs();

        return () => {
            abortController.abort();
        };
    }, [canOpenLogs, clientPasswordHash, isLogDialogOpen]);

    React.useEffect(() => {
        if (!isLogDialogOpen) return;
        logEndRef.current?.scrollIntoView({ block: 'end' });
    }, [isLogDialogOpen, visibleLogs]);

    React.useEffect(() => {
        if (!openLogsSignal || !canOpenLogs) return;
        queueMicrotask(() => setIsLogDialogOpen(true));
    }, [canOpenLogs, openLogsSignal]);

    return (
        <div
            className={cn(
                'workbench-panel text-card-foreground border-border flex min-h-[300px] w-full flex-col overflow-hidden rounded-lg border',
                isGenerateEmptyState ? 'h-fit xl:h-full' : 'h-full'
            )}>
            <div className='border-border/70 flex shrink-0 flex-wrap items-center justify-between gap-3 border-b px-4 py-3'>
                <h2 className='editorial-title text-xl font-semibold'>{t('output.previewTitle')}</h2>
                <div className='text-muted-foreground ui-stat flex flex-wrap items-center gap-3 text-xs'>
                    <span className='inline-flex items-center gap-1.5'>
                        {isLoading ? (
                            <Loader2 className='text-primary h-3.5 w-3.5 animate-spin' />
                        ) : (
                            <span
                                className={cn(
                                    'h-2 w-2 rounded-full',
                                    imageBatch && imageBatch.length > 0 ? 'bg-emerald-500' : 'bg-muted-foreground/45'
                                )}
                            />
                        )}
                        {previewStateLabel}
                    </span>
                    {previewMetaItems.map((item) => (
                        <span key={item}>{item}</span>
                    ))}
                </div>
            </div>
            <div
                className={cn(
                    'preview-gallery-board relative flex items-center justify-center overflow-hidden px-3 py-5 sm:px-6',
                    isGenerateEmptyState
                        ? 'min-h-[220px] shrink-0 sm:min-h-[248px] lg:min-h-[272px] xl:min-h-0 xl:flex-1'
                        : 'min-h-[300px] flex-1 sm:min-h-[420px] lg:min-h-[520px]'
                )}>
                {isLoading ? (
                    streamingPreviewImages && streamingPreviewImages.size > 0 ? (
                        // 展示流式预览图，单图时和最终视图一样居中。
                        <div className='photo-paper relative flex aspect-[4/3] w-full max-w-[780px] items-center justify-center p-3 2xl:max-w-[860px]'>
                            {/* 展示最新的预览图，也就是最大索引图片。 */}
                            {(() => {
                                const entries = Array.from(streamingPreviewImages.entries());
                                const latestEntry = entries[entries.length - 1];
                                if (!latestEntry) return null;
                                const [, dataUrl] = latestEntry;
                                return (
                                    <Image
                                        src={dataUrl}
                                        alt={t('output.streaming')}
                                        width={512}
                                        height={512}
                                        className='image-edge h-full w-full rounded-md object-contain'
                                        unoptimized
                                    />
                                );
                            })()}
                            {/* 在底部居中叠加加载状态。 */}
                            <div className='bg-foreground/80 text-background absolute bottom-4 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full px-3 py-1.5'>
                                <Loader2 className='h-4 w-4 animate-spin' />
                                <p className='text-sm'>{t('output.streaming')}</p>
                            </div>
                        </div>
                    ) : currentMode === 'edit' && baseImagePreviewUrl ? (
                        <div className='photo-paper relative flex aspect-[4/3] w-full max-w-[780px] items-center justify-center p-3 2xl:max-w-[860px]'>
                            <Image
                                src={baseImagePreviewUrl}
                                alt={t('output.editing')}
                                fill
                                style={{ objectFit: 'contain' }}
                                className='image-edge rounded-md blur-md filter'
                                unoptimized
                            />
                            <div className='absolute inset-0 flex flex-col items-center justify-center bg-black/50 text-white/80'>
                                <Loader2 className='mb-2 h-8 w-8 animate-spin' />
                                <p>{t('output.editing')}</p>
                            </div>
                        </div>
                    ) : (
                        <div className='text-muted-foreground flex flex-col items-center justify-center'>
                            <Loader2 className='mb-2 h-8 w-8 animate-spin' />
                            <p>{isStreamingRequest ? t('output.keepalive') : t('output.generating')}</p>
                        </div>
                    )
                ) : hasFailure ? (
                    <div className='photo-paper relative flex aspect-[4/3] w-full max-w-[780px] flex-col justify-between p-5 sm:p-6 2xl:max-w-[860px]'>
                        <div className='space-y-3'>
                            <div className='text-muted-foreground text-xs'>{t('output.failedKicker')}</div>
                            <div className='space-y-2'>
                                <h3 className='editorial-title text-2xl font-semibold'>{t('output.failedTitle')}</h3>
                                <p className='text-muted-foreground max-w-[46rem] text-sm leading-6'>
                                    {failureMessage}
                                </p>
                            </div>
                        </div>
                        <div className='flex flex-wrap items-center gap-2'>
                            {onRetry ? (
                                <Button type='button' onClick={onRetry} className='min-h-11'>
                                    <RefreshCcw className='mr-2 h-4 w-4' />
                                    {t('output.retry')}
                                </Button>
                            ) : null}
                            {canOpenLogs ? (
                                <Button
                                    type='button'
                                    variant='outline'
                                    onClick={() => setIsLogDialogOpen(true)}
                                    className='bg-background/76 min-h-11'>
                                    <Activity className='mr-2 h-4 w-4' />
                                    {t('logs.open')}
                                </Button>
                            ) : null}
                        </div>
                    </div>
                ) : isCompareView && selectedImage && compareTargetImage ? (
                    <ImageCompareView
                        leftImage={compareTargetImage}
                        leftLabel={compareReferenceLabel}
                        rightImage={selectedImage}
                        rightLabel={t('output.compareCurrent')}
                    />
                ) : imageBatch && imageBatch.length > 0 ? (
                    viewMode === 'grid' ? (
                        <div
                            className={`grid ${getGridColsClass(imageBatch.length)} w-full max-w-[780px] gap-2 2xl:max-w-[860px]`}>
                            {imageBatch.map((img, index) => (
                                <button
                                    type='button'
                                    key={img.filename}
                                    onClick={() => onViewChange(index)}
                                    className={cn(
                                        'photo-paper relative aspect-square overflow-hidden p-2 text-left transition-[box-shadow,transform] enabled:motion-safe:hover:-translate-y-0.5',
                                        selectedImageIndex === index
                                            ? 'ring-ring ring-offset-background ring-2 ring-offset-2'
                                            : 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2'
                                    )}
                                    aria-label={t('output.selectImage', { index: index + 1 })}>
                                    <Image
                                        src={img.path}
                                        alt={t('output.generatedImage', { index: index + 1 })}
                                        fill
                                        style={{ objectFit: 'contain' }}
                                        className='image-edge rounded-md'
                                        sizes='(max-width: 768px) 50vw, (max-width: 1024px) 33vw, 25vw'
                                        onLoad={(event) => handleImageLoad(img.filename, event)}
                                        unoptimized
                                    />
                                </button>
                            ))}
                        </div>
                    ) : imageBatch[viewMode] ? (
                        <div className='photo-paper relative aspect-[4/3] w-full max-w-[780px] p-3 2xl:max-w-[860px]'>
                            <Image
                                src={imageBatch[viewMode].path}
                                alt={resolvedAltText}
                                fill
                                sizes='(max-width: 768px) 92vw, (max-width: 1536px) 44vw, 860px'
                                className='image-edge rounded-md object-contain'
                                onLoad={(event) => handleImageLoad(imageBatch[viewMode].filename, event)}
                                unoptimized
                            />
                        </div>
                    ) : (
                        <div className='text-muted-foreground text-center'>
                            <p>{t('output.error')}</p>
                        </div>
                    )
                ) : currentMode === 'edit' ? (
                    baseImagePreviewUrl ? (
                        <div className='photo-paper relative flex aspect-[4/3] w-full max-w-[780px] items-center justify-center p-3 2xl:max-w-[860px]'>
                            <Image
                                src={baseImagePreviewUrl}
                                alt={t('output.editReferenceAlt')}
                                fill
                                sizes='(max-width: 768px) 92vw, (max-width: 1536px) 44vw, 860px'
                                className='image-edge rounded-md object-contain p-3'
                                unoptimized
                            />
                            <div className='border-border/70 bg-background/86 text-muted-foreground absolute bottom-5 left-6 rounded-md border px-3 py-1 text-xs shadow-sm'>
                                {t('output.editReferenceReadyLabel')}
                            </div>
                        </div>
                    ) : (
                        <div className='relative mx-auto flex w-full max-w-[30rem] flex-col items-center justify-center gap-3 px-6 py-7 text-center'>
                            <div className='border-border/70 bg-background/70 text-muted-foreground flex h-11 w-11 items-center justify-center rounded-md border shadow-sm'>
                                <ImageIcon className='h-5 w-5' />
                            </div>
                            <div className='space-y-2'>
                                <h3 className='editorial-title text-lg font-semibold'>
                                    {t('output.editReferenceNeeded')}
                                </h3>
                                <p className='text-muted-foreground max-w-[36rem] text-sm leading-6'>
                                    {t('output.editReferenceDescription')}
                                </p>
                            </div>
                            <span className='border-border bg-card text-muted-foreground rounded-md border px-3 py-1 text-xs'>
                                {t('output.editReferenceWaitingLabel')}
                            </span>
                        </div>
                    )
                ) : (
                    <div className='relative mx-auto flex w-full max-w-[30rem] flex-col items-center justify-center gap-3 px-6 py-7 text-center'>
                        <div className='border-border/70 bg-background/70 text-muted-foreground flex h-11 w-11 items-center justify-center rounded-md border shadow-sm'>
                            <ImageIcon className='h-5 w-5' />
                        </div>
                        <div className='space-y-2'>
                            <h3 className='editorial-title text-lg font-semibold'>{t('output.emptyTitle')}</h3>
                            <p className='text-muted-foreground text-sm leading-6'>{t('output.emptyDescription')}</p>
                        </div>
                    </div>
                )}
            </div>

            <Dialog open={isLogDialogOpen} onOpenChange={handleLogDialogOpenChange}>
                <DialogContent className='sm:max-w-[760px]'>
                    <DialogHeader>
                        <DialogTitle>{t('logs.title')}</DialogTitle>
                        <DialogDescription>{t('logs.description')}</DialogDescription>
                    </DialogHeader>
                    <div className='text-muted-foreground bg-muted/40 border-border flex items-center justify-between rounded-md border px-3 py-2 text-xs'>
                        <span>{t(`logs.status.${logConnectionState}`)}</span>
                        <span>{t('logs.count', { count: visibleLogs.length })}</span>
                    </div>
                    {hasLogScope ? (
                        <div className='text-muted-foreground border-border bg-muted/20 rounded-md border px-3 py-2 text-xs'>
                            {t('logs.scopeSelected')}
                        </div>
                    ) : hasSelectedImageBatch ? (
                        <div className='text-muted-foreground border-border rounded-md border border-dashed px-3 py-2 text-xs'>
                            {t('logs.scopeMissing')}
                        </div>
                    ) : (
                        <div className='text-muted-foreground border-border rounded-md border border-dashed px-3 py-2 text-xs'>
                            {t('logs.scopeNone')}
                        </div>
                    )}
                    <div className='border-border bg-card/70 flex flex-col gap-2 rounded-md border px-3 py-2 text-xs sm:flex-row sm:items-start sm:justify-between'>
                        <div className='text-muted-foreground min-w-0 space-y-1'>
                            <p className='break-all'>
                                {t('logs.scopeRequestIds', {
                                    value: formatLogScopeValues(logScopeDiagnostics.requestIds)
                                })}
                            </p>
                            <p className='break-all'>
                                {t('logs.scopeFilenames', {
                                    value: formatLogScopeValues(logScopeDiagnostics.filenames)
                                })}
                            </p>
                            <p className='break-all'>
                                {t('logs.scopeResolvedRequestIds', {
                                    value: formatLogScopeValues(logScopeDiagnostics.filenameMatchedRequestIds)
                                })}
                            </p>
                        </div>
                        <Button
                            type='button'
                            variant='outline'
                            size='sm'
                            className='shrink-0'
                            onClick={handleCopyLogScope}>
                            <Copy className='mr-2 h-4 w-4' />
                            {copiedLogScopeText === logScopeDiagnostics.copyText
                                ? t('common.copied')
                                : t('logs.copyScope')}
                        </Button>
                    </div>
                    <div className='literary-scrollbar bg-muted/30 border-border text-foreground/80 h-[420px] overflow-y-auto rounded-md border p-3 font-mono text-xs leading-5'>
                        {visibleLogs.length === 0 ? (
                            <p className='text-muted-foreground'>
                                {hasLogScope
                                    ? t('logs.emptyForSelection')
                                    : hasSelectedImageBatch && !hasScopeCandidate
                                      ? t('logs.historyWithoutScope')
                                      : t('logs.selectImage')}
                            </p>
                        ) : (
                            visibleLogs.map((entry) => (
                                <div key={entry.id} className='border-border/50 border-b py-2 last:border-b-0'>
                                    <div className='flex flex-wrap items-center gap-2'>
                                        <span className='text-muted-foreground'>{formatLogTime(entry.at, locale)}</span>
                                        <span
                                            className={cn(
                                                'rounded border px-1.5 py-0.5 uppercase',
                                                entry.level === 'error' &&
                                                    'border-red-500/40 text-red-700 dark:border-red-400/40 dark:text-red-200',
                                                entry.level === 'warn' &&
                                                    'border-yellow-500/40 text-yellow-700 dark:border-yellow-400/40 dark:text-yellow-200',
                                                entry.level === 'info' &&
                                                    'border-blue-500/40 text-blue-700 dark:border-blue-300/30 dark:text-blue-100',
                                                entry.level === 'debug' && 'border-border text-muted-foreground'
                                            )}>
                                            {getActivityLogLevelLabel(entry.level, t)}
                                        </span>
                                        <span className='text-foreground break-all'>{entry.message}</span>
                                    </div>
                                    {entry.context ? (
                                        <pre className='text-muted-foreground mt-1 break-words whitespace-pre-wrap'>
                                            {entry.context}
                                        </pre>
                                    ) : null}
                                </div>
                            ))
                        )}
                        <div ref={logEndRef} />
                    </div>
                    <DialogFooter>
                        <Button type='button' variant='outline' size='sm' onClick={() => setLogs([])}>
                            <Trash2 className='mr-2 h-4 w-4' />
                            {t('logs.clear')}
                        </Button>
                    </DialogFooter>
                </DialogContent>
            </Dialog>

            <div
                className={cn(
                    'border-border/40 flex w-full shrink-0 flex-wrap items-center justify-center gap-1.5 border-t bg-transparent px-3 py-2',
                    !hasSelectedImageBatch && 'bg-background/36'
                )}>
                {hasSelectedImageBatch ? (
                    <>
                        {showCarousel && (
                            <div className='bg-card/80 border-border literary-scrollbar flex max-w-full items-center gap-1.5 overflow-x-auto rounded-md border p-1'>
                                <Button
                                    variant='ghost'
                                    size='icon'
                                    className={cn(
                                        'touch-target rounded p-1',
                                        viewMode === 'grid'
                                            ? 'bg-accent text-accent-foreground'
                                            : 'text-muted-foreground'
                                    )}
                                    onClick={() => onViewChange('grid')}
                                    aria-label={t('output.showGrid')}>
                                    <Grid className='h-4 w-4' />
                                </Button>
                                {imageBatch.map((img, index) => (
                                    <Button
                                        key={img.filename}
                                        variant='ghost'
                                        size='icon'
                                        className={cn(
                                            'touch-target overflow-hidden rounded p-0.5',
                                            viewMode === index
                                                ? 'ring-ring ring-offset-background ring-2 ring-offset-1'
                                                : 'opacity-60 hover:opacity-100'
                                        )}
                                        onClick={() => onViewChange(index)}
                                        aria-label={t('output.selectImage', { index: index + 1 })}>
                                        <Image
                                            src={img.path}
                                            alt={t('output.thumbnail', { index: index + 1 })}
                                            width={28}
                                            height={28}
                                            className='image-edge h-full w-full rounded object-cover'
                                            unoptimized
                                        />
                                    </Button>
                                ))}
                            </div>
                        )}

                        {canOpenLogs && (
                            <ResultActionButton
                                icon={<Activity className='mr-2 h-4 w-4' />}
                                label={t('logs.open')}
                                onClick={() => setIsLogDialogOpen(true)}
                            />
                        )}

                        <ResultActionButton
                            icon={<Download className='mr-2 h-4 w-4' />}
                            label={t('output.download')}
                            onClick={handleDownloadClick}
                            disabled={!canUseSelectedImageActions}
                            emphasized={showCarousel && viewMode === 'grid'}
                        />
                        <ResultActionButton
                            icon={<Send className='mr-2 h-4 w-4' />}
                            label={t('output.continueEdit')}
                            onClick={handleSendClick}
                            disabled={!canUseSelectedImageActions}
                            emphasized={showCarousel && viewMode === 'grid'}
                        />
                        <ResultActionButton
                            icon={<RefreshCcw className='mr-2 h-4 w-4' />}
                            label={t('output.createVariant')}
                            onClick={onCreateVariant}
                            disabled={isLoading || !canCreateVariant}
                        />
                        <ResultActionButton
                            icon={<Copy className='mr-2 h-4 w-4' />}
                            label={t('output.reusePrompt')}
                            onClick={onReusePrompt}
                            disabled={isLoading || !canReusePrompt}
                        />
                        <ResultActionButton
                            icon={<GitCompare className='mr-2 h-4 w-4' />}
                            label={t('output.compare')}
                            onClick={handleCompareClick}
                            disabled={!canCompareImages}
                            active={isCompareView}
                        />
                        <ResultActionButton
                            icon={<Share2 className='mr-2 h-4 w-4' />}
                            label={t('output.share')}
                            onClick={handleShareClick}
                            disabled={!canUseSelectedImageActions}
                            emphasized={showCarousel && viewMode === 'grid'}
                        />
                    </>
                ) : (
                    <>
                        <ResultActionButton
                            icon={<Download className='mr-2 h-4 w-4' />}
                            label={t('output.download')}
                            disabled
                        />
                        <ResultActionButton
                            icon={<Send className='mr-2 h-4 w-4' />}
                            label={t('output.continueEdit')}
                            disabled
                        />
                        <ResultActionButton
                            icon={<RefreshCcw className='mr-2 h-4 w-4' />}
                            label={t('output.createVariant')}
                            disabled
                        />
                        <ResultActionButton
                            icon={<Copy className='mr-2 h-4 w-4' />}
                            label={t('output.reusePrompt')}
                            disabled
                        />
                        <ResultActionButton
                            icon={<GitCompare className='mr-2 h-4 w-4' />}
                            label={t('output.compare')}
                            disabled
                        />
                    </>
                )}
            </div>
        </div>
    );
}
