
"use client";

import { Message, URLPreviewData, Group, ChatRecipient, ActionPayload, UserProfile } from "@/lib/types";
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Button } from "./ui/button";
import { SmilePlus, Clock, Trash2, Ban, CornerUpLeft, Play, Pause, Loader2, Info, Check, CheckCheck, ArrowDownUp, ExternalLink, Pencil, X, Languages, Mic, AlertTriangle, Download, File as FileIcon, Copy, Lock, MoreHorizontal, UserCheck, Eye, EyeOff } from "lucide-react";
import { useSettings } from "@/contexts/settings-context";
import { useAuth } from "@/contexts/auth-context";
import { useAppContext } from "@/contexts/app-context";
import { useFirebase } from "@/contexts/firebase-context";
import { useMemo, useState, useRef, useEffect } from "react";
import { cn } from "@/lib/utils";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
import { Textarea } from "./ui/textarea";
import { cryptoService } from "@/lib/crypto-service";
import { storageService } from "@/lib/storage-service";
import Image from 'next/image';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./ui/dropdown-menu";
import { Capacitor } from "@capacitor/core";


const emojiToCodePoint = (emoji: string): string => {
    const codePoints = [];
    for (let i = 0; i < emoji.length; i++) {
        const code = emoji.codePointAt(i);
        if (code) {
            codePoints.push(code.toString(16));
            if (code > 0xffff) {
                i++; 
            }
        }
    }
    return codePoints.join('-');
};

const renderTextWithEmojisAndHighlight = (text: string | null | undefined, searchQuery?: string, isHighlighted?: boolean): string => {
    if (!text) return '';

    let content = text;

    content = content.replace(/</g, "&lt;").replace(/>/g, "&gt;");

    if (searchQuery) {
        const regex = new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
        content = content.replace(regex, (match, group1) => {
            const highlightClass = isHighlighted ? 'bg-orange-400 dark:bg-orange-600' : 'bg-yellow-300 dark:bg-yellow-500';
            return `<mark class="search-highlight ${highlightClass} text-black rounded-sm px-0.5">${group1}</mark>`;
        });
    }
    
    // This regex finds unicode characters but they are rendered as images via the `img.emoji` CSS selector logic
    const emojiRegex = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])(\ufe0f)?/g;

    return content.replace(emojiRegex, (emoji) => {
        try {
            const unified = emojiToCodePoint(emoji);
            if (!unified) return emoji;
            const imageUrl = `https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/${unified}.png`;
            return `<img src="${imageUrl}" alt="${emoji}" class="emoji" />`;
        } catch (error) {
            console.error(`Could not convert emoji: ${emoji}`, error);
            return emoji; 
        }
    });
};



interface MessageItemProps {
    message: Message;
    isSender: boolean;
    isDeleting: boolean;
    onDelete: () => void;
    onToggleReaction: (emoji: string) => void;
    onPreviewImage: (imageUrl: string, isViewOnce?: boolean, onViewed?: () => void) => void;
    onEditMessage: (messageId: string, newText: string) => void;
    onTranslate: (message: Message) => void;
    onPrivateReply: (user: UserProfile) => void;
    translatedText?: string;
    isTranslating?: boolean;
    currentGroup?: Group;
    searchQuery?: string;
    isHighlighted?: boolean;
    isViewed: boolean;
    onMarkAsViewed: (messageId: string) => void;
}

const QuotedMessage = ({ message, isSender, t }: { message: Message, isSender: boolean, t: (key: any, options?: any) => string }) => (
    <a href={`#message-${message.replyTo?.id}`} className="block mb-2">
        <div className={cn(
            "p-2 rounded-lg bg-opacity-50 border-l-4",
            isSender 
                ? "bg-black/20 border-white/50" 
                : "bg-black/5 dark:bg-white/10 border-primary"
        )}>
            <p className="font-bold text-xs">{message.replyTo?.displayName}</p>
            <p className="text-sm truncate opacity-80">{message.replyTo?.text || t(message.replyTo?.imageKey ? 'image' : message.replyTo?.videoKey ? 'video' : 'voiceMessage')}</p>
        </div>
    </a>
);

const AudioPlayer = ({ message, isSender, currentGroup }: { message: Message, isSender: boolean, currentGroup?: Group }) => {
    const { t } = useSettings();
    const { currentUser } = useAuth();
    const { db } = useFirebase();
    const [secureUrl, setSecureUrl] = useState<string | null>(message.status === 'pending' ? message.audioKey || null : null);
    const [isLoading, setIsLoading] = useState(message.status !== 'pending');
    const [error, setError] = useState<string | null>(null);
    const [isPlaying, setIsPlaying] = useState(false);
    const audioRef = useRef<HTMLAudioElement | null>(null);
    const canvasRef = useRef<HTMLCanvasElement | null>(null);

    const audioContextRef = useRef<AudioContext | null>(null);
    const analyserRef = useRef<AnalyserNode | null>(null);
    const sourceRef = useRef<MediaElementAudioSourceNode | null>(null);
    const animationFrameRef = useRef<number | null>(null);
    const volumeRef = useRef(0);
    const timeRef = useRef(0);


    useEffect(() => {
        const fetchAndDecryptAudio = async () => {
            if (!message.audioKey || !currentUser || message.status === 'pending') return;
            
            if (secureUrl && secureUrl.startsWith('blob:')) {
                setIsLoading(false);
                return;
            }

            setIsLoading(true);
            setError(null);
            try {
                const cachedBlob = await storageService.getCachedMedia(message.audioKey);
                if (cachedBlob) {
                    setSecureUrl(URL.createObjectURL(cachedBlob));
                    return;
                }
                const isNative = Capacitor.isNativePlatform();
                const baseUrl = isNative ? process.env.NEXT_PUBLIC_API_BASE_URL : '';
                const apiUrl = `${baseUrl}/api/media?fileKey=${encodeURIComponent(message.audioKey)}`;
                const response = await fetch(apiUrl);
                if (!response.ok) {
                    const errorData = await response.json();
                    throw new Error(errorData.error || 'Failed to get media file');
                }
                
                const originalBlob = await response.blob();

                let blobToCache = originalBlob;

                if (message.encryptionKey) {
                    blobToCache = await cryptoService.decryptFile(originalBlob, message.encryptionKey, message.keyVersion, currentUser, db, currentGroup, message.fileType);
                } 
                
                await storageService.cacheMedia(message.audioKey, blobToCache);
                setSecureUrl(URL.createObjectURL(blobToCache));

            } catch (error: any) {
                console.error("Error fetching or decrypting audio:", error);
                setError(error.message);
            } finally {
                setIsLoading(false);
            }
        };
        fetchAndDecryptAudio();
    }, [message.audioKey, message.encryptionKey, message.keyVersion, currentUser, db, currentGroup, message.status, secureUrl, message.fileType]);

    const drawWave = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, amplitude: number, lineWidth: number, alphaShift: number) => {
        let centerY = canvas.height / 2;
        ctx.beginPath();
        ctx.moveTo(0, centerY);

        const normalizedVolume = volumeRef.current / 128;
        const waveAmplitude = amplitude * normalizedVolume;

        for (let x = 0; x <= canvas.width; x++) {
            let y = centerY + Math.sin(x * 0.05 + timeRef.current * 0.08) * waveAmplitude;
            ctx.lineTo(x, y);
        }

        let gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
        gradient.addColorStop(0, `rgba(0,255,255,${0.9 - alphaShift})`);
        gradient.addColorStop(0.5, `rgba(0,128,255,${0.6 - alphaShift})`);
        gradient.addColorStop(1, `rgba(0,0,80,${0.2 - alphaShift})`);

        ctx.strokeStyle = gradient;
        ctx.lineWidth = lineWidth;
        ctx.shadowBlur = 15;
        ctx.shadowColor = "#00eaff";
        ctx.stroke();
    };


    const animate = () => {
        const canvas = canvasRef.current;
        if (!canvas) return;
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
        
        if (analyserRef.current) {
            const dataArray = new Uint8Array(analyserRef.current.frequencyBinCount);
            analyserRef.current.getByteFrequencyData(dataArray);
            let sum = 0;
            for (let i = 0; i < dataArray.length; i++) {
                sum += dataArray[i];
            }
            volumeRef.current = sum / dataArray.length;
        } else {
             volumeRef.current = 0;
        }

        if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
            canvas.width = canvas.clientWidth;
            canvas.height = canvas.clientHeight;
        }

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawWave(ctx, canvas, 15, 3, 0);
        drawWave(ctx, canvas, 20, 2, 0.3);
        
        timeRef.current++;
        animationFrameRef.current = requestAnimationFrame(animate);
    };

    const setupAudioAnalysis = () => {
        if (typeof window === 'undefined' || !audioRef.current || audioContextRef.current) return;
        
        const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
        const audioCtx = new AudioContext();
        audioContextRef.current = audioCtx;
        
        sourceRef.current = audioCtx.createMediaElementSource(audioRef.current);
        analyserRef.current = audioCtx.createAnalyser();
        analyserRef.current.fftSize = 256;

        sourceRef.current.connect(analyserRef.current);
        analyserRef.current.connect(audioCtx.destination);
    };

    const stopAnimation = () => {
        if (animationFrameRef.current) {
            cancelAnimationFrame(animationFrameRef.current);
            animationFrameRef.current = null;
        }
        
        const canvas = canvasRef.current;
        const ctx = canvas?.getContext('2d');
        if (ctx && canvas) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            volumeRef.current = 0;
            drawWave(ctx, canvas, 15, 3, 0);
            drawWave(ctx, canvas, 20, 2, 0.3);
        }
    };

    useEffect(() => {
        const audio = audioRef.current;
        if (!audio || !secureUrl) return;

        const handleEnded = () => {
            setIsPlaying(false);
            stopAnimation();
        };

        audio.addEventListener('ended', handleEnded);
        audio.addEventListener('pause', handleEnded);
        
        const canvas = canvasRef.current;
        const ctx = canvas?.getContext('2d');
        if (ctx && canvas) {
            drawWave(ctx, canvas, 15, 3, 0);
            drawWave(ctx, canvas, 20, 2, 0.3);
        }

        return () => {
            audio.removeEventListener('ended', handleEnded);
            audio.removeEventListener('pause', handleEnded);
            stopAnimation();
            if (sourceRef.current) sourceRef.current.disconnect();
            if (analyserRef.current) analyserRef.current.disconnect();
            if (audioContextRef.current && audioContextRef.current.state !== 'closed') {
                audioContextRef.current.close().catch(console.error);
                audioContextRef.current = null;
            }
        };
    }, [secureUrl]);

    const togglePlay = () => {
        const audio = audioRef.current;
        if (!audio || !secureUrl) return;
        
        setupAudioAnalysis();
        
        if (isPlaying) {
            audio.pause();
        } else {
            audio.play().then(() => {
                if (audioContextRef.current?.state === 'suspended') {
                    audioContextRef.current.resume();
                }
                animate();
            });
        }
        setIsPlaying(!isPlaying);
    };

    const formatTime = (seconds: number) => {
        const floorSeconds = Math.floor(seconds);
        const min = Math.floor(floorSeconds / 60);
        const sec = floorSeconds % 60;
        return `${min}:${sec < 10 ? '0' : ''}${sec}`;
    };

    if (isLoading) {
        return <div className="flex items-center gap-2 p-2"><Loader2 className="w-4 h-4 animate-spin" /> <span className="text-xs">{t('loading')}</span></div>;
    }
    
    if (error) {
        return <div className="text-xs text-destructive p-2 flex items-center gap-2"><AlertTriangle className="h-4 w-4"/> {t('loadMediaError')}</div>;
    }

    return (
        <div className={cn("w-full max-w-[250px]", isSender ? 'voice-bubble-sent' : 'voice-bubble-received')}>
             {secureUrl && <audio ref={audioRef} src={secureUrl} preload="metadata" crossOrigin="anonymous" />}
             <button onClick={togglePlay} className="play-btn-style" disabled={!secureUrl}>
                {isPlaying ? <Pause className="h-4 w-4"/> : <Play className="h-4 w-4"/>}
             </button>
             <canvas ref={canvasRef}></canvas>
             {message.audioDuration && <span className={cn("duration-style", isSender ? "text-primary-foreground/80" : "text-muted-foreground")}>{formatTime(message.audioDuration)}</span>}
        </div>
    );
};

function formatBytes(bytes: number, decimals = 2) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

const UploadProgress = ({ progress }: { progress: number }) => {
    const radius = 20;
    const circumference = 2 * Math.PI * radius;
    const [offset, setOffset] = useState(circumference);
    const [isFadingOut, setIsFadingOut] = useState(false);

    useEffect(() => {
        const newOffset = circumference - (progress / 100) * circumference;
        setOffset(newOffset);
        if (progress >= 100) {
            const timer = setTimeout(() => setIsFadingOut(true), 300);
            return () => clearTimeout(timer);
        } else {
            setIsFadingOut(false);
        }
    }, [progress, circumference]);

    return (
        <div className={cn(
            "absolute inset-0 bg-black/60 flex flex-col items-center justify-center text-white z-10 transition-opacity duration-300",
            isFadingOut && 'upload-progress-fade-out'
        )}>
            {!isFadingOut && (
                 <>
                    <svg className="w-16 h-16 transform -rotate-90">
                        <circle
                            className="text-white/20"
                            strokeWidth="4"
                            stroke="currentColor"
                            fill="transparent"
                            r={radius}
                            cx="32"
                            cy="32"
                        />
                        <circle
                            className="text-white"
                            strokeWidth="4"
                            strokeDasharray={circumference}
                            strokeDashoffset={offset}
                            strokeLinecap="round"
                            stroke="currentColor"
                            fill="transparent"
                            r={radius}
                            cx="32"
                            cy="32"
                            style={{ transition: 'stroke-dashoffset 0.3s ease-out' }}
                        />
                    </svg>
                    <span className="mt-2 text-sm font-bold">{progress.toFixed(0)}%</span>
                </>
            )}
        </div>
    );
};


const SecureMedia = ({ message, type, alt, onPreview, currentGroup, onMarkAsViewed, isViewed, isSender }: { message: Message, type: 'image' | 'video' | 'file', alt?: string, onPreview?: (url: string, isViewOnce?: boolean, onViewed?: () => void) => void, currentGroup?: Group, onMarkAsViewed?: (messageId: string) => void; isViewed?: boolean; isSender: boolean; }) => {
    const { currentUser } = useAuth();
    const { db } = useFirebase();
    const { t } = useSettings();
    const [secureUrl, setSecureUrl] = useState<string | null>(message.status === 'pending' ? (message.imageKey || message.videoKey || null) : null);
    const [isLoading, setIsLoading] = useState(message.status !== 'pending');
    const [error, setError] = useState<string | null>(null);

    const mediaKey = type === 'image' ? message.imageKey : type === 'video' ? message.videoKey : message.fileKey;

    // This effect will dispatch an event when the image has loaded
    const handleImageLoad = () => {
        window.dispatchEvent(new CustomEvent('imageLoaded'));
    };

    useEffect(() => {
        const fetchAndDecryptMedia = async () => {
            if (!mediaKey || !currentUser || message.status === 'pending') return;
            
            if (mediaKey.startsWith('blob:')) {
                setIsLoading(false);
                setSecureUrl(mediaKey);
                return;
            }

            if (secureUrl && secureUrl.startsWith('blob:')) {
                setIsLoading(false);
                return;
            }

            setIsLoading(true);
            setError(null);
            try {
                const cachedBlob = await storageService.getCachedMedia(mediaKey);
                if (cachedBlob) {
                    setSecureUrl(URL.createObjectURL(cachedBlob));
                    return;
                }
                const isNative = Capacitor.isNativePlatform();
                const baseUrl = isNative ? process.env.NEXT_PUBLIC_API_BASE_URL : '';
                const apiUrl = `${baseUrl}/api/media?fileKey=${encodeURIComponent(mediaKey)}`;
                const response = await fetch(apiUrl);
                 if (!response.ok) {
                    const errorData = await response.json();
                    throw new Error(errorData.error || 'Failed to get media file');
                }
                const originalBlob = await response.blob();
                
                let blobToCache = originalBlob;
                if (message.encryptionKey) {
                    blobToCache = await cryptoService.decryptFile(originalBlob, message.encryptionKey, message.keyVersion, currentUser, db, currentGroup, message.fileType);
                } 
                
                await storageService.cacheMedia(mediaKey, blobToCache);
                setSecureUrl(URL.createObjectURL(blobToCache));

            } catch (err: any) {
                console.error("Error fetching or decrypting media:", err);
                setError(err.message || "Could not load media.");
            } finally {
                setIsLoading(false);
            }
        };

        fetchAndDecryptMedia();
    }, [mediaKey, message.status, message.encryptionKey, message.keyVersion, type, currentUser, db, currentGroup, secureUrl, message.fileType]);
    
    const handleDownload = () => {
        if (!secureUrl || !message.fileName) return;
        const a = document.createElement('a');
        a.href = secureUrl;
        a.download = message.fileName;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    };

    const handleViewOnceClick = () => {
        if (!secureUrl || !onPreview || !message.id || isViewed) return;
    
        if (onMarkAsViewed) {
            onMarkAsViewed(message.id);
        }
    
        onPreview(secureUrl, true);
    };
    
    
    const mediaContainerClasses = "relative max-w-xs rounded-lg mt-1 overflow-hidden";

    if (isLoading) {
        return <div className="flex items-center justify-center bg-muted rounded-lg max-w-xs h-32"><Loader2 className="w-6 h-6 animate-spin" /></div>;
    }
    
    if (error || !secureUrl) {
        return <div className="text-xs text-destructive p-2 flex items-center gap-2"><AlertTriangle className="h-4 w-4"/> {t('loadMediaError')}</div>;
    }

    if (message.deleteAfterDelivery && !isSender) {
        if (isViewed) {
            return (
                <div className={cn(mediaContainerClasses, "p-4 bg-muted/50 border border-dashed flex flex-col items-center justify-center gap-2")}>
                    <EyeOff className="w-8 h-8 text-muted-foreground"/>
                    <span className="font-bold text-muted-foreground">Viewed</span>
                </div>
            );
        }
        return (
            <button onClick={handleViewOnceClick} disabled={isViewed} className={cn(mediaContainerClasses, "p-4 bg-muted/50 border border-dashed flex flex-col items-center justify-center gap-2 cursor-pointer hover:bg-muted/70 disabled:cursor-not-allowed disabled:opacity-60")}>
                <Eye className="w-8 h-8"/>
                <span className="font-bold">Tap to view</span>
                <span className="text-xs text-muted-foreground">{type === 'image' ? 'Photo' : 'Video'}</span>
            </button>
        );
    }

    if (type === 'image') {
        return (
            <div className={mediaContainerClasses}>
                {message.status === 'pending' && typeof message.uploadProgress === 'number' && <UploadProgress progress={message.uploadProgress} />}
                <Image src={secureUrl} alt={alt || 'sent image'} className="w-full h-auto cursor-pointer" onClick={() => message.status !== 'pending' && onPreview?.(secureUrl, false, undefined)} onLoad={handleImageLoad} width={300} height={300} />
            </div>
        );
    }

    if (type === 'video') {
         return (
            <div className={mediaContainerClasses}>
                {message.status === 'pending' && typeof message.uploadProgress === 'number' && <UploadProgress progress={message.uploadProgress} />}
                <video src={secureUrl} controls className="w-full h-auto" onLoadedData={handleImageLoad} />
            </div>
        );
    }

    if (type === 'file') {
        return (
            <div className="p-3 rounded-lg border bg-muted/50 flex items-center gap-3 max-w-xs mt-1">
                <FileIcon className="h-8 w-8 text-muted-foreground flex-shrink-0" />
                <div className="flex-1 overflow-hidden">
                    <p className="font-medium truncate">{message.fileName}</p>
                    <p className="text-xs text-muted-foreground">{message.fileSize ? formatBytes(message.fileSize) : ''}</p>
                </div>
                 <Button variant="ghost" size="icon" onClick={handleDownload} title="Download file">
                    <Download className="h-5 w-5" />
                </Button>
            </div>
        )
    }

    return null;
}

const MessageText = ({ text, searchQuery, isHighlighted }: { text: string; searchQuery?: string; isHighlighted?: boolean; }) => {
    const [isClient, setIsClient] = useState(false);
    useEffect(() => {
        setIsClient(true);
    }, []);

    const safeText = text || '';

    if (!isClient) {
        return <p className="whitespace-pre-wrap break-words">{safeText}</p>;
    }

    return (
        <p
            className="whitespace-pre-wrap break-words"
            dangerouslySetInnerHTML={{ __html: renderTextWithEmojisAndHighlight(safeText, searchQuery, isHighlighted) }}
        />
    );
};

const DecryptedContent = ({ message, currentGroup, t, searchQuery, isHighlighted }: { message: Message; currentGroup?: Group, t: (key: any) => string, searchQuery?: string, isHighlighted?: boolean }) => {
    const { currentUser } = useAuth();
    const { db } = useFirebase();
    const [decryptedText, setDecryptedText] = useState<string | null>(message.text || null);
    const [isDecrypting, setIsDecrypting] = useState(false);
    const [error, setError] = useState<string | null>(null);

    useEffect(() => {
        const decrypt = async () => {
            // If text is already available (e.g., sender's own message, or already decrypted), just use it.
            if (message.text) {
                setDecryptedText(message.text);
                return;
            }
            
            if (message.encryptedText && typeof message.encryptedText === 'object' && !message.isSystemMessage && currentUser) {
                setIsDecrypting(true);
                setError(null);
                try {
                    let text;
                    if (currentGroup) {
                        const encryptedPayload = message.encryptedText as EncryptedMessage;
                        // This logic needs to be robust for both old and new group encryption formats.
                        if (typeof encryptedPayload === 'string') { // Old format
                             if (!message.keyVersion) {
                                throw new Error("Cannot decrypt old message: Key version is missing.");
                            }
                            const groupKey = await cryptoService.getGroupKey(currentGroup.id, message.keyVersion, currentUser.uid, db);
                            if (!groupKey) throw new Error("Could not retrieve group key for decryption. Your access might be revoked.");
                            text = await cryptoService.decryptGroupMessage(encryptedPayload, groupKey);
                        } else { // New Hybrid format
                            text = await cryptoService.decryptMessage(encryptedPayload, currentUser.uid);
                        }
                    } else {
                         text = await cryptoService.decryptMessage(message.encryptedText as EncryptedMessage, currentUser.uid);
                    }
                    setDecryptedText(text);
                } catch (e: any) {
                    console.warn(`Decryption Error for message: ${message.id}`, e);
                    setError(e.message || "Decryption failed.");
                    setDecryptedText(null);
                } finally {
                    setIsDecrypting(false);
                }
            } else {
                setDecryptedText(message.text || null);
            }
        };

        decrypt();
    }, [message, currentUser, currentGroup, db]);

    if (error) {
        return <p className="whitespace-pre-wrap break-words text-destructive">{error}</p>;
    }
    
    return <MessageText text={isDecrypting ? 'Decrypting...' : decryptedText || ''} searchQuery={searchQuery} isHighlighted={isHighlighted} />;
};



const UrlPreview = ({ data, isSender }: { data: URLPreviewData, isSender: boolean }) => (
    <a href={data.url} target="_blank" rel="noopener noreferrer" className={cn(
        "mt-2 block rounded-lg overflow-hidden border",
        isSender ? "border-white/20 bg-black/10" : "border-border bg-muted/50",
        "hover:bg-opacity-50 transition-colors"
    )}>
        {data.imageUrl && (
            <Image src={data.imageUrl} alt={data.title} className="w-full h-32 object-cover" width={300} height={128} />
        )}
        <div className="p-2">
            <p className={cn("text-xs font-bold truncate", isSender ? "text-primary-foreground/80" : "text-muted-foreground")}>{data.domain}</p>
            <p className={cn("text-sm font-bold truncate", isSender ? "text-primary-foreground" : "text-foreground")}>{data.title}</p>
            {data.description && <p className={cn("text-xs truncate", isSender ? "text-primary-foreground/80" : "text-muted-foreground")}>{data.description}</p>}
        </div>
    </a>
);


const usePrevious = <T,>(value: T) => {
    const ref = useRef<T>();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
};


export function MessageItem({ message, isSender, isDeleting, onDelete, onToggleReaction, onPreviewImage, onEditMessage, onTranslate, onPrivateReply, translatedText, isTranslating, currentGroup, searchQuery, isHighlighted, isViewed, onMarkAsViewed }: MessageItemProps) {
    const { currentUser } = useAuth();
    const { setReplyTo, setPrivateReplyTo } = useAppContext();
    const { playSound, t, formatDistanceToNow, language, addToast } = useSettings();
    const [isEditing, setIsEditing] = useState(false);
    const [editText, setEditText] = useState(message.text || "");
    const textareaRef = useRef<HTMLTextAreaElement>(null);
    const [pulsingReactions, setPulsingReactions] = useState<string[]>([]);
    const [isMenuOpen, setIsMenuOpen] = useState(false);
    const longPressTimer = useRef<NodeJS.Timeout>();

    const reactionEmojis = ['❤️', '😂', '👍', '😢', '😡'];

    const reactions = useMemo(() => {
        if (!message.reactions) return [];
        return Object.entries(message.reactions)
            .map(([emoji, users]) => ({ emoji, count: Object.keys(users).length, users: Object.values(users) }))
            .filter(r => r.count > 0)
            .sort((a,b) => b.count - a.count);
    }, [message.reactions]);
    
    const prevReactions = usePrevious(reactions);
    const prevStatus = usePrevious(message.status);
    const justConfirmed = prevStatus === 'pending' && message.status === 'sent';

    useEffect(() => {
        if (prevReactions && reactions.length >= prevReactions.length) {
            const newPulsing: string[] = [];
            reactions.forEach(currentReaction => {
                const prevReaction = prevReactions.find(pr => pr.emoji === currentReaction.emoji);
                if (prevReaction && currentReaction.count > prevReaction.count) {
                    newPulsing.push(currentReaction.emoji);
                }
            });
            if (newPulsing.length > 0) {
                setPulsingReactions(newPulsing);
                const timer = setTimeout(() => setPulsingReactions([]), 300); 
                return () => clearTimeout(timer);
            }
        }
    }, [reactions, prevReactions]);


    const formattedTimestamp = useMemo(() => {
        if (message.status === 'pending') return t('pending');
        if (message.status === 'failed') return 'Failed';
        if (!message.timestamp || typeof message.timestamp !== 'number') return 'sending...';
        return formatDistanceToNow(new Date(message.timestamp), { addSuffix: true });
    }, [message.timestamp, message.status, t, formatDistanceToNow]);

    useEffect(() => {
        if (isEditing && textareaRef.current) {
            textareaRef.current.focus();
            textareaRef.current.style.height = 'auto';
            textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
        }
    }, [isEditing]);
    
     const MessageStatusIcon = () => {
        if (!isSender || message.isSystemMessage) return null;
        if (message.status === 'pending') return <Clock className="h-4 w-4 text-muted-foreground" />;
        if (message.status === 'failed') return <AlertTriangle className="h-4 w-4 text-destructive" />;
        
        const isReadBySomeone = message.readBy && Object.keys(message.readBy).length > 0;
        if (isReadBySomeone) return <CheckCheck className="h-4 w-4 text-sky-400" />;
        
        const isDeliveredToSomeone = message.deliveredTo && Object.keys(message.deliveredTo).length > 1; 
        if (isDeliveredToSomeone) return <CheckCheck className="h-4 w-4 text-muted-foreground" />;

        return <Check className="h-4 w-4 text-muted-foreground" />;
    };


    const canInteract = !message.isDeleted && message.status !== 'pending' && message.status !== 'failed' && !message.isSystemMessage;

    const handleReply = () => {
        if(!message.id) return;
        playSound('touch');
        setReplyTo({
            id: message.id,
            text: message.text || (message.imageKey ? t('image') : message.videoKey ? t('video') : message.audioKey ? t('voiceMessage') : ""),
            displayName: isSender ? t('you') : message.senderDisplayName,
            imageKey: message.imageKey,
            videoKey: message.videoKey,
            audioKey: message.audioKey,
        });
        setIsMenuOpen(false);
    };

    const handlePrivateReply = () => {
        if (!currentGroup) return;
        const senderAsProfile: UserProfile = {
            uid: message.sender,
            displayName: message.senderDisplayName,
            photoURL: message.senderPhotoUrl,
            publicId: '' // Not needed for this action
        };
        setPrivateReplyTo(senderAsProfile);
        setIsMenuOpen(false);
    };

    const handleCopy = () => {
        const textToCopy = message.text || '';
        navigator.clipboard.writeText(textToCopy).then(() => {
            addToast('Message copied!');
        }, (err) => {
            console.error('Could not copy text: ', err);
            addToast('Failed to copy message.', { variant: 'destructive' });
        });
        setIsMenuOpen(false);
    }

    const handleSaveEdit = () => {
        if (message.id && editText.trim() && editText.trim() !== message.text) {
            onEditMessage(message.id, editText.trim());
        }
        setIsEditing(false);
    };

    const handleCancelEdit = () => {
        setEditText(message.text || "");
        setIsEditing(false);
    };

    const handleEditKeydown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            handleSaveEdit();
        }
        if (e.key === 'Escape') {
            e.preventDefault();
            handleCancelEdit();
        }
    }
    
    const handleReactionSelect = (emoji: string) => {
        onToggleReaction(emoji);
        setIsMenuOpen(false);
    };
    
    const isPrivate = !!message.privateFor;
    const canViewPrivateMessage = isPrivate && (currentUser?.uid === message.sender || currentUser?.uid === message.privateFor.uid);
    
    const handleLongPress = () => {
        longPressTimer.current = setTimeout(() => {
            setIsMenuOpen(true);
        }, 300);
    };

    const handlePressEnd = () => {
        clearTimeout(longPressTimer.current);
    };

    const handleMenuItemSelect = (action: () => void) => {
        action();
        setIsMenuOpen(false);
    }

    const renderMessageContent = () => {
        if (message.isDeleted) {
            return (
                <div className="flex items-center gap-2 italic text-muted-foreground">
                    <Ban className="h-4 w-4" />
                    <span>{t('deletedMessage')}</span>
                </div>
            );
        }
        
        if (isPrivate && !canViewPrivateMessage) {
            return (
                <div className="flex items-center gap-2 italic text-muted-foreground">
                    <Lock className="h-4 w-4" />
                    <span>Private message to {message.privateFor?.displayName}</span>
                </div>
            );
        }

        if (isEditing) {
            return (
                <div className="space-y-2">
                    <Textarea 
                        ref={textareaRef}
                        value={editText}
                        onChange={(e) => setEditText(e.target.value)}
                        onKeyDown={handleEditKeydown}
                        className="bg-background text-foreground"
                        rows={1}
                    />
                    <div className="flex justify-end gap-2">
                        <Button variant="ghost" size="sm" onClick={handleCancelEdit}>{t('cancel')}</Button>
                        <Button size="sm" onClick={handleSaveEdit}>{t('save')}</Button>
                    </div>
                </div>
            )
        }
        
        return (
             <>
                {message.replyTo && <QuotedMessage message={message} isSender={isSender} t={t} />}
                {message.imageKey && (
                    <SecureMedia
                        message={{ ...message, chatId: message.chatId || '' }}
                        type="image"
                        alt="sent image"
                        onPreview={onPreviewImage}
                        currentGroup={currentGroup}
                        onMarkAsViewed={onMarkAsViewed}
                        isViewed={isViewed}
                        isSender={isSender}
                    />
                )}
                {message.videoKey && (
                    <SecureMedia message={{ ...message, chatId: message.chatId || '' }} type="video" currentGroup={currentGroup} isSender={isSender}/>
                )}
                 {message.audioKey && (
                    <div className="relative">
                        {message.status === 'pending' && typeof message.uploadProgress === 'number' && <UploadProgress progress={message.uploadProgress} />}
                        <AudioPlayer message={message} isSender={isSender} currentGroup={currentGroup} />
                    </div>
                )}
                {message.fileKey && (
                    <SecureMedia message={{ ...message, chatId: message.chatId || '' }} type="file" currentGroup={currentGroup} isSender={isSender}/>
                )}
                
                {isPrivate && (
                    <div className="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
                        <Lock className="h-3 w-3" />
                        Private message to {message.privateFor?.displayName}
                    </div>
                )}
                
                {message.encryptedText ? <DecryptedContent message={message} currentGroup={currentGroup} t={t} searchQuery={searchQuery} isHighlighted={isHighlighted}/> : message.text && <MessageText text={message.text} searchQuery={searchQuery} isHighlighted={isHighlighted} />}
                
                {translatedText && (
                     <div className="border-t mt-2 pt-2">
                        <MessageText text={translatedText} searchQuery={searchQuery} isHighlighted={isHighlighted} />
                    </div>
                )}

                {message.urlPreviewData && <UrlPreview data={message.urlPreviewData} isSender={isSender} />}
                
                {message.status === 'pending' && !message.uploadProgress && !message.audioKey && !message.imageKey && !message.videoKey && !message.fileKey && (
                     <div className="flex items-center justify-center p-2">
                        <Loader2 className="w-4 h-4 animate-spin" />
                    </div>
                )}
            </>
        )
    };
    
    if (message.isSystemMessage) {
        return (
            <div className="flex justify-center items-center my-4">
                <div className="text-xs text-muted-foreground bg-muted px-3 py-1 rounded-full flex items-center gap-2">
                    <MessageText text={message.text || ''} searchQuery={searchQuery} isHighlighted={isHighlighted} />
                </div>
            </div>
        );
    }
    
    const hasMedia = message.imageKey || message.videoKey || message.fileKey;
    const hasAudio = !!message.audioKey;
    
    const wasCompressed = message.originalSize && message.compressedSize && isSender;
    const compressionSavings = wasCompressed ? 100 - (message.compressedSize! / message.originalSize!) * 100 : 0;
    const compressionTooltip = wasCompressed 
        ? `Compressed: ${message.originalSize}b -> ${message.compressedSize}b (${compressionSavings.toFixed(0)}% saved)`
        : "";
        
    const needsTranslation = message.detectedLang && message.detectedLang !== language;
    const canTranslate = !isSender && message.text && needsTranslation && !translatedText;

    const reactionsTooltipContent = reactions
        .map(r => {
            const codePoint = emojiToCodePoint(r.emoji);
            return `<div class='flex items-center gap-1.5 p-1'>
                     <img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/${codePoint}.png" alt="${r.emoji}" class="w-4 h-4" />
                     <span class='font-medium'>${r.users.map(u => u.displayName === currentUser?.displayName ? t('you') : u.displayName).join(', ')}</span>
                   </div>`;
        })
        .join('');


    return (
        <div 
            id={`message-${message.id}`}
            className={cn(
                "flex items-end gap-2 my-2 group",
                !justConfirmed && "message-anim",
                isDeleting && 'disintegrating'
            )}
            style={{ '--transform-origin': isSender ? 'bottom right' : 'bottom left' } as React.CSSProperties}
        >
             <Avatar className="h-8 w-8 self-end shadow-sm">
                <AvatarImage src={isSender ? currentUser?.photoURL : message.senderPhotoUrl} />
                <AvatarFallback>{message.senderDisplayName ? message.senderDisplayName.charAt(0) : '?'}</AvatarFallback>
            </Avatar>
            <div className={cn(
                "flex flex-col gap-1 w-fit items-start",
                isSender ? 'items-end' : 'items-start',
                "max-w-xs md:max-w-md"
            )}>
                
                    <Popover open={isMenuOpen} onOpenChange={setIsMenuOpen}>
                        <PopoverTrigger asChild>
                             <div
                                onMouseDown={handleLongPress}
                                onMouseUp={handlePressEnd}
                                onTouchStart={handleLongPress}
                                onTouchEnd={handlePressEnd}
                                onContextMenu={(e) => e.preventDefault()}
                                className={cn(
                                    "relative cursor-pointer",
                                    hasAudio ? "bg-transparent p-0" :
                                    isSender && !message.isDeleted && "bubble-sent",
                                    !isSender && !message.isDeleted && "bubble-received",
                                    message.isDeleted && 'bg-transparent text-muted-foreground',
                                    !hasMedia && !hasAudio && 'bubble-content'
                                )}
                            >
                                <div className={cn(
                                    "text-left relative",
                                    (hasMedia || hasAudio) && !message.text && !message.compressedText && !isEditing ? 'p-0' : '',
                                    !hasMedia && !hasAudio && 'p-0',
                                )}>
                                    {!isSender && !message.isDeleted && !hasAudio && <p className="text-xs font-bold mb-1 text-primary px-3 pt-2">{message.senderDisplayName}</p>}
                                    <div className={cn(!hasAudio && !hasMedia && "px-3 pb-2", hasAudio && "p-0", !isSender && "pt-0")}>
                                        {renderMessageContent()}
                                    </div>
                                </div>
                            </div>
                        </PopoverTrigger>
                        {canInteract && (
                            <PopoverContent className="w-auto p-0 bg-transparent border-0 shadow-none -translate-y-2">
                               <div className="reaction-picker flex items-center gap-1 p-1">
                                    {reactionEmojis.map(emoji => (
                                        <Button key={emoji} variant="ghost" size="icon" className="rounded-full w-9 h-9 transition-transform hover:scale-125" onClick={() => handleReactionSelect(emoji)}>
                                            <Image src={`https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/${emojiToCodePoint(emoji)}.png`} alt={emoji} width={24} height={24} />
                                        </Button>
                                    ))}
                                    <Button variant="ghost" size="icon" className="rounded-full w-9 h-9 transition-transform hover:scale-125" onClick={handleReply}><CornerUpLeft /></Button>
                                    
                                     <DropdownMenu>
                                        <DropdownMenuTrigger asChild>
                                            <Button variant="ghost" size="icon" className="rounded-full w-9 h-9 transition-transform hover:scale-125"><MoreHorizontal /></Button>
                                        </DropdownMenuTrigger>
                                        <DropdownMenuContent>
                                            {currentGroup && !isSender && <DropdownMenuItem onSelect={handlePrivateReply}><Lock className="mr-2 h-4 w-4" /> Reply Privately</DropdownMenuItem>}
                                            {message.text && <DropdownMenuItem onSelect={handleCopy}><Copy className="mr-2 h-4 w-4" /> {t('Copy')}</DropdownMenuItem>}
                                            {canTranslate && <DropdownMenuItem onSelect={() => onTranslate(message)} disabled={isTranslating}>{isTranslating ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Languages className="mr-2 h-4 w-4"/>} {t('Translate')}</DropdownMenuItem>}
                                            {isSender && !!message.text && <DropdownMenuItem onSelect={() => setIsEditing(true)}><Pencil className="mr-2 h-4 w-4" /> {t('Edit')}</DropdownMenuItem>}
                                            {isSender && <DropdownMenuItem onSelect={onDelete} className="text-destructive"><Trash2 className="mr-2 h-4 w-4" /> {t('Delete')}</DropdownMenuItem>}
                                        </DropdownMenuContent>
                                    </DropdownMenu>
                               </div>
                            </PopoverContent>
                        )}
                    </Popover>
                    {reactions.length > 0 && (
                        <TooltipProvider>
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <div className={cn("reactions-container", isSender ? 'justify-end' : 'justify-start')}>
                                        {reactions.map(({ emoji, count }, index) => (
                                            <div 
                                                key={emoji} 
                                                className={cn(
                                                    "reaction-item",
                                                    pulsingReactions.includes(emoji) && 'reaction-pulse-anim'
                                                )} 
                                                style={{ animationDelay: `${index * 50}ms` }}
                                            >
                                                <Image src={`https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/${emojiToCodePoint(emoji)}.png`} alt={emoji} width={18} height={18} style={{ verticalAlign: 'middle' }}/>
                                                <span className="text-xs font-bold text-foreground/80">{count}</span>
                                            </div>
                                        ))}
                                    </div>
                                </TooltipTrigger>
                                <TooltipContent>
                                <div dangerouslySetInnerHTML={{ __html: reactionsTooltipContent }} />
                                </TooltipContent>
                            </Tooltip>
                        </TooltipProvider>
                    )}
                


                 <div className="flex items-center gap-1.5 px-2">
                    <MessageStatusIcon />
                    <span className="text-xs text-muted-foreground">
                        {formattedTimestamp}
                    </span>
                    {message.editedAt && <span className="text-xs text-muted-foreground">{t('edited')}</span>}
                     {wasCompressed && (
                        <TooltipProvider>
                            <Tooltip>
                                <TooltipTrigger>
                                     <ArrowDownUp className="h-3 w-3 text-muted-foreground hover:text-foreground" />
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{compressionTooltip}</p>
                                </TooltipContent>
                            </Tooltip>
                        </TooltipProvider>
                    )}
                </div>
            </div>
        </div>
    );
}
