/**
 * FileUploadModal — compose UI that opens after dropping file(s)
 * onto the chat area (unless Shift was held, which triggers an
 * instant upload instead). Shows a preview of each file, a single
 * shared caption input, and a Send button.
 *
 * Behaviour:
 *   - Image files (mime starts with `image/`): show as thumbnails.
 *   - Other files: show filename + extension badge + size.
 *   - Caption is sent attached to the FIRST file only (matches
 *     Discord where a single comment lives above a group of files).
 *   - Enter in the caption input sends; Shift+Enter inserts a newline.
 *   - Cancel discards everything and closes.
 */
import { useEffect, useMemo, useState } from 'react';
import { File as FileIcon, X, PaperPlaneTilt } from '@phosphor-icons/react';
import { Modal, Button } from '@brycord/ui';
import styles from './FileUploadModal.module.css';

interface FileUploadModalProps {
	isOpen: boolean;
	onClose: () => void;
	channelName: string;
	files: File[];
	onSend: (caption: string) => Promise<void> | void;
}

function formatSize(bytes: number): string {
	if (bytes < 1024) return `${bytes} B`;
	if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
	return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function FilePreview({ file, onRemove }: { file: File; onRemove?: () => void }) {
	// Create a local object URL for image previews. Revoke on unmount
	// so we don't leak. For non-image files we skip the URL entirely.
	const objectUrl = useMemo(() => {
		if (file.type.startsWith('image/')) return URL.createObjectURL(file);
		return null;
	}, [file]);

	useEffect(() => {
		return () => {
			if (objectUrl) URL.revokeObjectURL(objectUrl);
		};
	}, [objectUrl]);

	return (
		<div className={styles.preview}>
			{onRemove && (
				<button
					type="button"
					className={styles.removeButton}
					onClick={onRemove}
					aria-label={`Remove ${file.name}`}
				>
					<X size={14} weight="bold" />
				</button>
			)}
			{objectUrl ? (
				<img src={objectUrl} alt={file.name} className={styles.previewImage} />
			) : (
				<div className={styles.previewIcon}>
					<FileIcon size={40} weight="duotone" />
				</div>
			)}
			<div className={styles.previewMeta}>
				<div className={styles.previewName} title={file.name}>
					{file.name}
				</div>
				<div className={styles.previewSize}>{formatSize(file.size)}</div>
			</div>
		</div>
	);
}

export function FileUploadModal({
	isOpen,
	onClose,
	channelName,
	files,
	onSend,
}: FileUploadModalProps) {
	const [caption, setCaption] = useState('');
	const [sending, setSending] = useState(false);
	const [workingFiles, setWorkingFiles] = useState<File[]>(files);
	const [error, setError] = useState<string | null>(null);

	// Reset local state whenever the modal opens with a fresh file list.
	useEffect(() => {
		if (isOpen) {
			setWorkingFiles(files);
			setCaption('');
			setSending(false);
			setError(null);
		}
	}, [isOpen, files]);

	const handleRemove = (index: number) => {
		setWorkingFiles((prev) => {
			const next = prev.filter((_, i) => i !== index);
			// If the user removed the last file, close the modal.
			if (next.length === 0) onClose();
			return next;
		});
	};

	const handleSend = async () => {
		if (workingFiles.length === 0 || sending) return;
		setSending(true);
		setError(null);
		try {
			await onSend(caption);
			onClose();
		} catch (err: any) {
			console.error('Failed to upload file(s):', err);
			// Surface the error inline so the user knows why their
			// upload failed. Common causes: 413 (too large — shouldn't
			// happen anymore since the drop-zone pre-checks), 520/502
			// (homeserver's media CDN is misbehaving), auth token
			// expired, network drop. The modal stays open so they can
			// hit Upload again to retry, or Cancel to bail out.
			const code = err?.httpStatus || err?.status;
			const matrixCode = err?.errcode || err?.data?.errcode;
			const detail = err?.data?.error || err?.message;
			let message = 'Upload failed';
			if (code) message += ` (HTTP ${code})`;
			if (matrixCode) message += ` — ${matrixCode}`;
			if (detail) message += `: ${detail}`;
			setError(message);
			setSending(false);
		}
	};

	const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
		if (e.key === 'Enter' && !e.shiftKey) {
			e.preventDefault();
			void handleSend();
		}
	};

	if (!isOpen) return null;

	const title =
		workingFiles.length === 1
			? `Upload ${workingFiles[0].name}`
			: `Upload ${workingFiles.length} files`;

	return (
		<Modal.Root isOpen={isOpen} onClose={onClose} size="medium">
			<Modal.Header title={title} onClose={onClose} />
			<Modal.Content className={styles.content}>
				<div className={styles.destination}>
					Sending to <strong>#{channelName}</strong>
				</div>
				<div className={styles.previewGrid}>
					{workingFiles.map((file, i) => (
						<FilePreview
							key={`${file.name}-${i}`}
							file={file}
							onRemove={workingFiles.length > 1 ? () => handleRemove(i) : undefined}
						/>
					))}
				</div>
				<label className={styles.captionLabel}>
					<span className={styles.captionLabelText}>Add a comment</span>
					<input
						type="text"
						className={styles.captionInput}
						placeholder="Optional comment…"
						value={caption}
						onChange={(e) => setCaption(e.target.value)}
						onKeyDown={handleKeyDown}
						autoFocus
					/>
				</label>
				{error && <div className={styles.errorBanner}>{error}</div>}
			</Modal.Content>
			<Modal.Footer>
				<Button variant="secondary" onClick={onClose} disabled={sending}>
					Cancel
				</Button>
				<Button
					variant="primary"
					onClick={handleSend}
					disabled={workingFiles.length === 0 || sending}
					icon={<PaperPlaneTilt size={16} weight="fill" />}
				>
					{sending ? 'Uploading…' : 'Upload'}
				</Button>
			</Modal.Footer>
		</Modal.Root>
	);
}
