const formatBytes = (b) => {
    if (!b) return '';
    if (b < 1024)        return `${b} B`;
    if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
    return `${(b / (1024 * 1024)).toFixed(1)} MB`;
};

const fileIcon = (mime) => {
    if (!mime) return { cls: 'file', icon: 'fa-file' };
    if (mime.startsWith('image/'))                       return { cls: 'img',  icon: 'fa-file-image' };
    if (mime === 'application/pdf')                      return { cls: 'pdf',  icon: 'fa-file-pdf' };
    if (mime.includes('word') || mime.includes('odt'))   return { cls: 'doc',  icon: 'fa-file-word' };
    if (mime.includes('excel') || mime.includes('sheet'))return { cls: 'xls',  icon: 'fa-file-excel' };
    if (mime.includes('zip') || mime.includes('rar'))    return { cls: 'zip',  icon: 'fa-file-archive' };
    return { cls: 'file', icon: 'fa-file' };
};

// NOTE: every button in this panel is type="button" ON PURPOSE. The panel
// renders inside host <form>s (e.g. SPEC's property modal), where a bare
// <button> defaults to type="submit" — clicking Delete would ALSO submit the
// host form and close the modal. Keep the attribute on any button added here.
// A file list long enough to blow up the height of whatever modal/view hosts
// the panel starts collapsed. Short lists stay open — collapsing two files
// costs a click and buys nothing.
const ATTACHMENTS_COLLAPSE_OVER = 4;

// `title` lets the host name the collection in its own vocabulary ("Photos &
// Documents" on a property) — the panel owns the ONE header either way, so the
// host must not print a second one above it (one-title rule).
const AttachmentsPanel = ({ entityType, entityId, title = 'Files & Attachments' }) => {
    const [attachments, setAttachments] = React.useState([]);
    const [loading, setLoading]         = React.useState(true);
    const [uploading, setUploading]     = React.useState(false);
    const [dragOver, setDragOver]       = React.useState(false);
    // Batch delete: select mode turns rows into click-to-select targets —
    // highlighted rows + one "Delete (n)" action, not a checkbox column.
    const [selectMode, setSelectMode]   = React.useState(false);
    const [selected, setSelected]       = React.useState(() => new Set());
    const [deleting, setDeleting]       = React.useState(false);
    // Errors render inline in the panel, never through alert(): a browser
    // dialog is suppressible (see ConfirmDialog.jsx) and can't be styled.
    const [error, setError]             = React.useState(null);
    // Collapsed = header row only (list AND drop zone hidden). The host is
    // usually a form modal, so an unbounded list pushed its buttons off screen.
    const [open, setOpen]               = React.useState(true);
    const autoDecided = React.useRef(false);
    const fileInputRef = React.useRef(null);

    const load = () => {
        api.getAttachments(entityType, entityId)
            .then(list => {
                setAttachments(list);
                // First load picks the default; after that the user's choice wins.
                if (!autoDecided.current) {
                    autoDecided.current = true;
                    setOpen(list.length <= ATTACHMENTS_COLLAPSE_OVER);
                }
            })
            .catch(err => setError('Could not load files: ' + err.message))
            .finally(() => setLoading(false));
    };

    React.useEffect(() => { autoDecided.current = false; load(); }, [entityType, entityId]);

    const handleFiles = async (files) => {
        if (!files?.length) return;
        setUploading(true);
        setError(null);
        try {
            for (const file of Array.from(files)) {
                await api.uploadAttachment(entityType, entityId, file);
            }
            load();
        } catch (err) {
            setError('Upload failed: ' + err.message);
        } finally {
            setUploading(false);
        }
    };

    const handleDelete = async (att) => {
        if (!await confirmAction({
            title: 'Delete file',
            message: `"${att.original_name}" will be removed from this record and from disk. This cannot be undone.`,
            confirmLabel: 'Delete file',
        })) return;
        setError(null);
        try { await api.deleteAttachment(att.id); load(); }
        catch (err) { setError(err.message); }
    };

    const exitSelectMode = () => { setSelectMode(false); setSelected(new Set()); };

    const toggleSelected = (id) => {
        setSelected(prev => {
            const next = new Set(prev);
            next.has(id) ? next.delete(id) : next.add(id);
            return next;
        });
    };

    const handleBatchDelete = async () => {
        if (!selected.size) return;
        const n = selected.size;
        if (!await confirmAction({
            title: `Delete ${n} file${n > 1 ? 's' : ''}`,
            message: `${n} selected file${n > 1 ? 's' : ''} will be removed from this record and from disk. This cannot be undone.`,
            confirmLabel: `Delete ${n} file${n > 1 ? 's' : ''}`,
        })) return;
        setDeleting(true);
        setError(null);
        // One failure must not abandon the rest of the batch — each delete is
        // independent server-side, so keep going and report what didn't go.
        const failures = [];
        for (const id of selected) {
            try { await api.deleteAttachment(id); }
            catch (err) { failures.push(err.message); }
        }
        setDeleting(false);
        exitSelectMode();
        load();
        if (failures.length) {
            setError(`${failures.length} of ${n} file${n > 1 ? 's' : ''} could not be deleted: ${failures[0]}`);
        }
    };

    return (
        <div className="attachments-section">
            <h4>
                {/* The title IS the toggle — no separate chevron button to hunt for. */}
                <span onClick={() => setOpen(o => !o)} style={{ cursor: 'pointer', userSelect: 'none' }}
                      title={open ? 'Hide the file list' : 'Show the file list'}>
                    <i className={`fas ${open ? 'fa-chevron-down' : 'fa-chevron-right'}`}
                       style={{ color: '#6b7280', marginRight: '0.4rem', fontSize: '0.75rem' }}></i>
                    <i className="fas fa-paperclip" style={{ color: '#6b7280' }}></i> {title} {attachments.length > 0 && `(${attachments.length})`}
                </span>
                <span style={{ display: 'inline-flex', gap: '0.375rem' }}>
                    {attachments.length > 1 && !selectMode && (
                        <button type="button" className="btn btn-secondary btn-small" onClick={() => { setOpen(true); setSelectMode(true); }}>
                            <i className="fas fa-check-square"></i> Select
                        </button>
                    )}
                    {selectMode && (
                        <>
                            <button type="button" className="btn btn-danger btn-small" onClick={handleBatchDelete}
                                disabled={!selected.size || deleting}>
                                {deleting ? <><i className="fas fa-spinner fa-spin"></i> Deleting…</> : <><i className="fas fa-trash"></i> Delete ({selected.size})</>}
                            </button>
                            <button type="button" className="btn btn-secondary btn-small" onClick={exitSelectMode} disabled={deleting}>
                                Cancel
                            </button>
                        </>
                    )}
                    {!selectMode && (
                        <button type="button" className="btn btn-secondary btn-small" onClick={() => { setOpen(true); fileInputRef.current?.click(); }} disabled={uploading}>
                            {uploading ? <><i className="fas fa-spinner fa-spin"></i> Uploading…</> : <><i className="fas fa-upload"></i> Upload</>}
                        </button>
                    )}
                </span>
            </h4>

            <input ref={fileInputRef} type="file" multiple style={{ display: 'none' }}
                   onChange={e => handleFiles(e.target.files)} />

            {open && !selectMode && (
                <div
                    className={`upload-zone ${dragOver ? 'drag-over' : ''}`}
                    style={{ marginBottom: '0.75rem', padding: '0.875rem' }}
                    onClick={() => fileInputRef.current?.click()}
                    onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
                    onDragLeave={() => setDragOver(false)}
                    onDrop={(e) => { e.preventDefault(); setDragOver(false); handleFiles(e.dataTransfer.files); }}
                >
                    <i className="fas fa-cloud-upload-alt" style={{ marginRight: '0.5rem' }}></i>
                    Drop files here or click to browse &mdash; tax forms, photos, floorplans, docs (25 MB max)
                </div>
            )}
            {open && selectMode && (
                <p style={{ color: '#9ca3af', fontSize: '0.8125rem', marginBottom: '0.5rem' }}>
                    Click files to select them, then Delete.
                </p>
            )}

            {error && (
                <p style={{ color: 'var(--danger, #ef4444)', fontSize: '0.8125rem', marginBottom: '0.5rem' }}>
                    <i className="fas fa-triangle-exclamation" style={{ marginRight: '0.35rem' }}></i>{error}
                </p>
            )}

            {/* Bounded height: even a 400-photo record can't grow the host modal
                past this — the list scrolls inside itself instead. */}
            {open && <div style={{ maxHeight: '22rem', overflowY: 'auto' }}>
            {loading ? (
                <div style={{ color: '#6b7280', fontSize: '0.875rem' }}><i className="fas fa-spinner fa-spin"></i> Loading…</div>
            ) : attachments.length === 0 ? (
                <p style={{ color: '#9ca3af', fontSize: '0.8125rem' }}>No files attached yet.</p>
            ) : attachments.map(att => {
                const { cls, icon } = fileIcon(att.mime_type);
                const isSel = selected.has(att.id);
                return (
                    <div key={att.id} className="attachment-item"
                        onClick={selectMode ? () => toggleSelected(att.id) : undefined}
                        style={selectMode ? {
                            cursor: 'pointer',
                            outline: isSel ? '2px solid var(--accent, #3b82f6)' : '2px solid transparent',
                            borderRadius: '6px',
                            opacity: isSel ? 1 : 0.75,
                        } : undefined}
                    >
                        <div className={`attachment-icon ${cls}`}><i className={`fas ${icon}`}></i></div>
                        <div className="attachment-info">
                            <div className="attachment-name">{att.original_name}</div>
                            <div className="attachment-meta">{formatBytes(att.size_bytes)} · {formatDate(att.created_at)}{att.uploaded_by_name ? ` · ${att.uploaded_by_name}` : ''}</div>
                        </div>
                        {selectMode ? (
                            <i className={`fas ${isSel ? 'fa-check-circle' : 'fa-circle'}`}
                               style={{ color: isSel ? 'var(--accent, #3b82f6)' : '#4b5563' }}></i>
                        ) : (
                            <>
                                {/* a plain <a href> sends no Authorization header and 401s — downloads must go through the blob fetch */}
                                <button type="button" className="btn btn-secondary btn-small" title="Download"
                                    onClick={() => api.downloadExport(`/api/attachments/${att.id}/download`, att.original_name).catch(err => setError('Download failed: ' + err.message))}>
                                    <i className="fas fa-download"></i>
                                </button>
                                <button type="button" className="btn-icon-sm danger" onClick={() => handleDelete(att)} title="Delete">
                                    <i className="fas fa-trash"></i>
                                </button>
                            </>
                        )}
                    </div>
                );
            })}
            </div>}
        </div>
    );
};
