<script>
    import { createEventDispatcher, onMount } from "svelte";
    import { npubEncode } from "nostr-tools/nip19";
    import { fetchUserProfile } from "./nostr.js";
    import { getApiBase } from "./config.js";

    export let isLoggedIn = false;
    export let userPubkey = "";
    export let userSigner = null;
    export let currentEffectiveRole = "";

    const dispatch = createEventDispatcher();

    let blobs = [];
    let isLoading = false;
    let error = "";

    // Upload state
    let selectedFiles = [];
    let isUploading = false;
    let uploadProgress = "";
    let fileInput;

    // Modal state
    let showModal = false;
    let selectedBlob = null;
    let zoomLevel = 1;
    const MIN_ZOOM = 0.25;
    const MAX_ZOOM = 4;
    const ZOOM_STEP = 0.25;

    // Admin view state
    let isAdminView = false;
    let adminUserStats = [];
    let isLoadingAdmin = false;
    let selectedAdminUser = null;
    let selectedUserBlobs = [];

    $: canAccess = isLoggedIn && userPubkey;
    $: isAdmin = currentEffectiveRole === "admin" || currentEffectiveRole === "owner";

    // Track if we've loaded once to prevent repeated loads
    let hasLoadedOnce = false;

    /**
     * Create Blossom auth header (kind 24242) per BUD-01 spec
     * @param {object} signer - The signer instance
     * @param {string} verb - The action verb (list, get, upload, delete)
     * @param {string} sha256Hex - Optional SHA256 hash for x tag
     * @returns {Promise<string|null>} Base64 encoded auth header or null
     */
    async function createBlossomAuth(signer, verb, sha256Hex = null) {
        if (!signer) {
            console.log("No signer available for Blossom auth");
            return null;
        }

        try {
            const now = Math.floor(Date.now() / 1000);
            const expiration = now + 60; // 60 seconds from now

            const tags = [
                ["t", verb],
                ["expiration", expiration.toString()],
            ];

            // Add x tag for blob-specific operations
            if (sha256Hex) {
                tags.push(["x", sha256Hex]);
            }

            const authEvent = {
                kind: 24242,
                created_at: now,
                tags: tags,
                content: `Blossom ${verb} operation`,
            };

            const signedEvent = await signer.signEvent(authEvent);
            // Use URL-safe base64 encoding (replace + with -, / with _)
            return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_');
        } catch (err) {
            console.error("Error creating Blossom auth:", err);
            return null;
        }
    }

    onMount(() => {
        if (canAccess && !hasLoadedOnce) {
            hasLoadedOnce = true;
            loadBlobs();
        }
    });

    // Load once when canAccess becomes true (for when user logs in after mount)
    $: if (canAccess && !hasLoadedOnce && !isLoading) {
        hasLoadedOnce = true;
        loadBlobs();
    }

    async function loadBlobs() {
        if (!userPubkey) return;

        isLoading = true;
        error = "";

        try {
            const url = `${getApiBase()}/blossom/list/${userPubkey}`;
            const authHeader = await createBlossomAuth(userSigner, "list");
            const response = await fetch(url, {
                headers: authHeader ? { Authorization: `Nostr ${authHeader}` } : {},
            });

            if (!response.ok) {
                throw new Error(`Failed to load blobs: ${response.statusText}`);
            }

            const data = await response.json();
            // API returns 'uploaded' timestamp per BUD-02 spec
            blobs = Array.isArray(data) ? data : [];
            blobs.sort((a, b) => (b.uploaded || 0) - (a.uploaded || 0));
            console.log("Loaded blobs:", blobs);
        } catch (err) {
            console.error("Error loading blobs:", err);
            error = err.message || "Failed to load blobs";
        } finally {
            isLoading = false;
        }
    }

    function formatSize(bytes) {
        if (!bytes) return "0 B";
        const units = ["B", "KB", "MB", "GB"];
        let i = 0;
        let size = bytes;
        while (size >= 1024 && i < units.length - 1) {
            size /= 1024;
            i++;
        }
        return `${size.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
    }

    function formatDate(timestamp) {
        if (!timestamp) return "Unknown";
        return new Date(timestamp * 1000).toLocaleString();
    }

    function truncateHash(hash) {
        if (!hash) return "";
        return `${hash.slice(0, 8)}...${hash.slice(-8)}`;
    }

    function getMimeCategory(mimeType) {
        if (!mimeType) return "unknown";
        if (mimeType.startsWith("image/")) return "image";
        if (mimeType.startsWith("video/")) return "video";
        if (mimeType.startsWith("audio/")) return "audio";
        return "file";
    }

    function getMimeIcon(mimeType) {
        const category = getMimeCategory(mimeType);
        switch (category) {
            case "image": return "🖼️";
            case "video": return "🎬";
            case "audio": return "🎵";
            default: return "📄";
        }
    }

    function openModal(blob) {
        selectedBlob = blob;
        zoomLevel = 1;
        showModal = true;
    }

    function closeModal() {
        showModal = false;
        selectedBlob = null;
        zoomLevel = 1;
    }

    function zoomIn() {
        if (zoomLevel < MAX_ZOOM) {
            zoomLevel = Math.min(MAX_ZOOM, zoomLevel + ZOOM_STEP);
        }
    }

    function zoomOut() {
        if (zoomLevel > MIN_ZOOM) {
            zoomLevel = Math.max(MIN_ZOOM, zoomLevel - ZOOM_STEP);
        }
    }

    function handleKeydown(event) {
        if (!showModal) return;
        if (event.key === "Escape") {
            closeModal();
        } else if (event.key === "+" || event.key === "=") {
            zoomIn();
        } else if (event.key === "-") {
            zoomOut();
        }
    }

    function getBlobUrl(blob) {
        // Prefer the URL from the API response (includes extension for proper MIME handling)
        if (blob.url) {
            // Already an absolute URL - return as-is
            if (blob.url.startsWith("http://") || blob.url.startsWith("https://")) {
                return blob.url;
            }
            // Starts with / - it's a path, prepend API base
            if (blob.url.startsWith("/")) {
                return `${getApiBase()}${blob.url}`;
            }
            // No protocol - looks like host:port/path, add http://
            // This handles cases like "localhost:3334/blossom/..."
            return `http://${blob.url}`;
        }
        // Fallback: construct URL with sha256 only
        return `${getApiBase()}/blossom/${blob.sha256}`;
    }

    function openLoginModal() {
        dispatch("openLoginModal");
    }

    async function deleteBlob(blob) {
        if (!confirm(`Delete blob ${truncateHash(blob.sha256)}?`)) return;

        try {
            const url = `${getApiBase()}/blossom/${blob.sha256}`;
            const authHeader = await createBlossomAuth(userSigner, "delete", blob.sha256);
            const response = await fetch(url, {
                method: "DELETE",
                headers: authHeader ? { Authorization: `Nostr ${authHeader}` } : {},
            });

            if (!response.ok) {
                throw new Error(`Failed to delete: ${response.statusText}`);
            }

            blobs = blobs.filter(b => b.sha256 !== blob.sha256);
            if (selectedBlob?.sha256 === blob.sha256) {
                closeModal();
            }
        } catch (err) {
            console.error("Error deleting blob:", err);
            alert(`Failed to delete blob: ${err.message}`);
        }
    }

    function handleFileSelect(event) {
        selectedFiles = Array.from(event.target.files);
    }

    function triggerFileInput() {
        fileInput?.click();
    }

    async function uploadFiles() {
        if (selectedFiles.length === 0) return;

        isUploading = true;
        error = "";
        const uploaded = [];
        const failed = [];

        for (let i = 0; i < selectedFiles.length; i++) {
            const file = selectedFiles[i];
            uploadProgress = `Uploading ${i + 1}/${selectedFiles.length}: ${file.name}`;

            try {
                const url = `${getApiBase()}/blossom/upload`;
                const authHeader = await createBlossomAuth(userSigner, "upload");

                const response = await fetch(url, {
                    method: "PUT",
                    headers: {
                        "Content-Type": file.type || "application/octet-stream",
                        ...(authHeader ? { Authorization: `Nostr ${authHeader}` } : {}),
                    },
                    body: file,
                });

                if (!response.ok) {
                    const reason = response.headers.get("X-Reason") || response.statusText;
                    throw new Error(reason);
                }

                const descriptor = await response.json();
                console.log("Upload response:", descriptor);
                uploaded.push(descriptor);
            } catch (err) {
                console.error(`Error uploading ${file.name}:`, err);
                failed.push({ name: file.name, error: err.message });
            }
        }

        isUploading = false;
        uploadProgress = "";
        selectedFiles = [];
        if (fileInput) fileInput.value = "";

        if (uploaded.length > 0) {
            await loadBlobs();
        }

        if (failed.length > 0) {
            error = `Failed to upload: ${failed.map(f => f.name).join(", ")}`;
        }
    }

    // Admin functions
    function hexToNpub(pubkeyHex) {
        try {
            return npubEncode(pubkeyHex);
        } catch (e) {
            return truncateHash(pubkeyHex);
        }
    }

    function truncateNpub(npub) {
        if (!npub) return "";
        return `${npub.slice(0, 12)}...${npub.slice(-8)}`;
    }

    async function fetchAdminUserStats() {
        isLoadingAdmin = true;
        error = "";

        try {
            const url = `${getApiBase()}/blossom/admin/users`;
            const authHeader = await createBlossomAuth(userSigner, "admin");
            const response = await fetch(url, {
                headers: authHeader ? { Authorization: `Nostr ${authHeader}` } : {},
            });

            if (!response.ok) {
                throw new Error(`Failed to load user stats: ${response.statusText}`);
            }

            adminUserStats = await response.json();

            // Fetch profiles for each user (non-blocking)
            for (const stat of adminUserStats) {
                fetchUserProfile(stat.pubkey).then(profile => {
                    stat.profile = profile || { name: "", picture: "" };
                    adminUserStats = adminUserStats; // trigger reactivity
                }).catch(() => {
                    stat.profile = { name: "", picture: "" };
                });
            }
        } catch (err) {
            console.error("Error fetching admin user stats:", err);
            error = err.message || "Failed to load user stats";
        } finally {
            isLoadingAdmin = false;
        }
    }

    async function loadUserBlobs(pubkeyHex) {
        isLoading = true;
        error = "";

        try {
            const url = `${getApiBase()}/blossom/list/${pubkeyHex}`;
            const authHeader = await createBlossomAuth(userSigner, "list");
            const response = await fetch(url, {
                headers: authHeader ? { Authorization: `Nostr ${authHeader}` } : {},
            });

            if (!response.ok) {
                throw new Error(`Failed to load user blobs: ${response.statusText}`);
            }

            selectedUserBlobs = await response.json();
            selectedUserBlobs.sort((a, b) => (b.uploaded || 0) - (a.uploaded || 0));
        } catch (err) {
            console.error("Error loading user blobs:", err);
            error = err.message || "Failed to load user blobs";
        } finally {
            isLoading = false;
        }
    }

    function enterAdminView() {
        isAdminView = true;
        fetchAdminUserStats();
    }

    function exitAdminView() {
        isAdminView = false;
        adminUserStats = [];
        selectedAdminUser = null;
        selectedUserBlobs = [];
    }

    async function selectUser(userStat) {
        selectedAdminUser = {
            pubkey: userStat.pubkey,
            profile: userStat.profile
        };
        await loadUserBlobs(userStat.pubkey);
    }

    function exitUserView() {
        selectedAdminUser = null;
        selectedUserBlobs = [];
    }

    function handleRefresh() {
        if (selectedAdminUser) {
            loadUserBlobs(selectedAdminUser.pubkey);
        } else if (isAdminView) {
            fetchAdminUserStats();
        } else {
            loadBlobs();
        }
    }

    function getDisplayBlobs() {
        if (selectedAdminUser) {
            return selectedUserBlobs;
        }
        return blobs;
    }
</script>

<svelte:window on:keydown={handleKeydown} />

{#if canAccess}
    <div class="blossom-view">
        <div class="header-section">
            {#if selectedAdminUser}
                <button class="back-btn" on:click={exitUserView}>
                    &larr; Back
                </button>
                <h3 class="user-header">
                    {#if selectedAdminUser.profile?.picture}
                        <img src={selectedAdminUser.profile.picture} alt="" class="header-avatar" />
                    {/if}
                    {selectedAdminUser.profile?.name || truncateNpub(hexToNpub(selectedAdminUser.pubkey))}
                </h3>
            {:else if isAdminView}
                <button class="back-btn" on:click={exitAdminView}>
                    &larr; Back
                </button>
                <h3>All Users Storage</h3>
            {:else}
                <h3>Blossom Media Storage</h3>
            {/if}

            <div class="header-buttons">
                {#if isAdmin && !isAdminView && !selectedAdminUser}
                    <button class="admin-btn" on:click={enterAdminView} disabled={isLoading}>
                        Admin
                    </button>
                {/if}
                <button class="refresh-btn" on:click={handleRefresh} disabled={isLoading || isLoadingAdmin}>
                    🔄 {isLoading || isLoadingAdmin ? "Loading..." : "Refresh"}
                </button>
            </div>
        </div>

        {#if !isAdminView && !selectedAdminUser}
            <div class="upload-section">
                <span class="upload-label">Upload new files</span>
                <input
                    type="file"
                    multiple
                    bind:this={fileInput}
                    on:change={handleFileSelect}
                    class="file-input-hidden"
                />
                {#if selectedFiles.length > 0}
                    <span class="selected-count">{selectedFiles.length} file(s) selected</span>
                    <button
                        class="upload-btn"
                        on:click={uploadFiles}
                        disabled={isUploading}
                    >
                        {isUploading ? uploadProgress : "Upload"}
                    </button>
                {/if}
                <button class="select-btn" on:click={triggerFileInput} disabled={isUploading}>
                    Select Files
                </button>
            </div>
        {/if}

        {#if error}
            <div class="error-message">
                {error}
            </div>
        {/if}

        {#if isAdminView && !selectedAdminUser}
            <!-- Admin users list view -->
            {#if isLoadingAdmin}
                <div class="loading">Loading user statistics...</div>
            {:else if adminUserStats.length === 0}
                <div class="empty-state">
                    <p>No users have uploaded files yet.</p>
                </div>
            {:else}
                <div class="admin-users-list">
                    {#each adminUserStats as userStat}
                        <div
                            class="user-stat-item"
                            on:click={() => selectUser(userStat)}
                            on:keypress={(e) => e.key === "Enter" && selectUser(userStat)}
                            role="button"
                            tabindex="0"
                        >
                            <div class="user-avatar-container">
                                {#if userStat.profile?.picture}
                                    <img src={userStat.profile.picture} alt="" class="user-avatar" />
                                {:else}
                                    <div class="user-avatar-placeholder"></div>
                                {/if}
                            </div>
                            <div class="user-info">
                                <div class="user-name">
                                    {userStat.profile?.name || truncateNpub(hexToNpub(userStat.pubkey))}
                                </div>
                                <div class="user-npub" title={userStat.pubkey}>
                                    <span class="npub-full">{hexToNpub(userStat.pubkey)}</span>
                                    <span class="npub-truncated">{truncateNpub(hexToNpub(userStat.pubkey))}</span>
                                </div>
                            </div>
                            <div class="user-stats">
                                <span class="blob-count">{userStat.blob_count} files</span>
                                <span class="total-size">{formatSize(userStat.total_size_bytes)}</span>
                            </div>
                        </div>
                    {/each}
                </div>
            {/if}
        {:else}
            <!-- Normal blob list view (own files or selected user's files) -->
            {#if isLoading && getDisplayBlobs().length === 0}
                <div class="loading">Loading blobs...</div>
            {:else if getDisplayBlobs().length === 0}
                <div class="empty-state">
                    <p>{selectedAdminUser ? "No files found for this user." : "No files found in your Blossom storage."}</p>
                </div>
            {:else}
                <div class="blob-list">
                    {#each getDisplayBlobs() as blob}
                        <div
                            class="blob-item"
                            on:click={() => openModal(blob)}
                            on:keypress={(e) => e.key === "Enter" && openModal(blob)}
                            role="button"
                            tabindex="0"
                        >
                            <div class="blob-thumbnail">
                                {#if getMimeCategory(blob.type) === "image"}
                                    <img src={getBlobUrl(blob)} alt="" class="thumbnail-img" />
                                {:else if getMimeCategory(blob.type) === "video"}
                                    <video src={getBlobUrl(blob)} class="thumbnail-video" muted preload="metadata"></video>
                                {:else}
                                    <span class="thumbnail-icon">{getMimeIcon(blob.type)}</span>
                                {/if}
                            </div>
                            <div class="blob-info">
                                <div class="blob-hash" title={blob.sha256}>
                                    <span class="hash-full">{blob.sha256}</span>
                                    <span class="hash-truncated">{truncateHash(blob.sha256)}</span>
                                </div>
                                <div class="blob-meta">
                                    <span class="blob-size">{formatSize(blob.size)}</span>
                                    <span class="blob-type">{blob.type || "unknown"}</span>
                                </div>
                            </div>
                            <div class="blob-date">
                                {formatDate(blob.uploaded)}
                            </div>
                            <button
                                class="delete-btn"
                                on:click|stopPropagation={() => deleteBlob(blob)}
                                title="Delete"
                            >
                                X
                            </button>
                        </div>
                    {/each}
                </div>
            {/if}
        {/if}
    </div>
{:else}
    <div class="login-prompt">
        <p>Please log in to view your Blossom storage.</p>
        <button class="login-btn" on:click={openLoginModal}>Log In</button>
    </div>
{/if}

{#if showModal && selectedBlob}
    <div
        class="modal-overlay"
        on:click={closeModal}
        on:keypress={(e) => e.key === "Enter" && closeModal()}
        role="button"
        tabindex="0"
    >
        <div
            class="modal-content"
            on:click|stopPropagation
            on:keypress|stopPropagation
            role="dialog"
        >
            <div class="modal-header">
                <div class="modal-title">
                    <span class="modal-hash">{truncateHash(selectedBlob.sha256)}</span>
                    <span class="modal-type">{selectedBlob.type || "unknown"}</span>
                </div>
                <div class="modal-controls">
                    {#if getMimeCategory(selectedBlob.type) === "image"}
                        <button class="zoom-btn" on:click={zoomOut} disabled={zoomLevel <= MIN_ZOOM}>-</button>
                        <span class="zoom-level">{Math.round(zoomLevel * 100)}%</span>
                        <button class="zoom-btn" on:click={zoomIn} disabled={zoomLevel >= MAX_ZOOM}>+</button>
                    {/if}
                    <button class="close-btn" on:click={closeModal}>X</button>
                </div>
            </div>
            <div class="modal-body">
                {#if getMimeCategory(selectedBlob.type) === "image"}
                    <div class="media-container" style="transform: scale({zoomLevel});">
                        <img src={getBlobUrl(selectedBlob)} alt="Blob content" />
                    </div>
                {:else if getMimeCategory(selectedBlob.type) === "video"}
                    <div class="media-container">
                        <video controls src={getBlobUrl(selectedBlob)}>
                            <track kind="captions" />
                        </video>
                    </div>
                {:else if getMimeCategory(selectedBlob.type) === "audio"}
                    <div class="media-container audio">
                        <audio controls src={getBlobUrl(selectedBlob)}></audio>
                    </div>
                {:else}
                    <div class="file-preview">
                        <div class="file-icon">{getMimeIcon(selectedBlob.type)}</div>
                        <p>Preview not available for this file type.</p>
                        <a href={getBlobUrl(selectedBlob)} target="_blank" rel="noopener noreferrer" class="download-link">
                            Download File
                        </a>
                    </div>
                {/if}
            </div>
            <div class="modal-footer">
                <div class="blob-details">
                    <span>Size: {formatSize(selectedBlob.size)}</span>
                    <span>Uploaded: {formatDate(selectedBlob.uploaded)}</span>
                </div>
                <div class="blob-url-section">
                    <input
                        type="text"
                        readonly
                        value={getBlobUrl(selectedBlob)}
                        class="blob-url-input"
                        on:click={(e) => e.target.select()}
                    />
                    <button
                        class="copy-btn"
                        on:click={() => {
                            navigator.clipboard.writeText(getBlobUrl(selectedBlob));
                        }}
                    >
                        Copy
                    </button>
                </div>
                <div class="modal-actions">
                    <a href={getBlobUrl(selectedBlob)} target="_blank" rel="noopener noreferrer" class="action-btn">
                        Open in New Tab
                    </a>
                    <button class="action-btn danger" on:click={() => deleteBlob(selectedBlob)}>
                        Delete
                    </button>
                </div>
            </div>
        </div>
    </div>
{/if}

<style>
    .blossom-view {
        padding: 1em;
        box-sizing: border-box;
    }

    .header-section {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1em;
    }

    .header-section h3 {
        margin: 0;
        color: var(--text-color);
        flex: 1;
    }

    .header-buttons {
        display: flex;
        align-items: center;
        gap: 0.5em;
    }

    .back-btn {
        background: transparent;
        border: 1px solid var(--border-color);
        color: var(--text-color);
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
        margin-right: 0.5em;
    }

    .back-btn:hover {
        background-color: var(--sidebar-bg);
    }

    .admin-btn {
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
    }

    .admin-btn:hover:not(:disabled) {
        background-color: var(--accent-hover-color);
    }

    .admin-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .user-header {
        display: flex;
        align-items: center;
        gap: 0.5em;
    }

    .header-avatar {
        width: 28px;
        height: 28px;
        border-radius: 50%;
        object-fit: cover;
    }

    .refresh-btn {
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
    }

    .refresh-btn:hover:not(:disabled) {
        background-color: var(--accent-hover-color);
    }

    .refresh-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .upload-section {
        display: flex;
        align-items: center;
        gap: 0.75em;
        padding: 0.75em 1em;
        background-color: var(--card-bg);
        border-radius: 6px;
        margin-bottom: 1em;
        flex-wrap: wrap;
    }

    .upload-label {
        color: var(--text-color);
        font-size: 0.95em;
        flex: 1;
    }

    .file-input-hidden {
        display: none;
    }

    .select-btn {
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
    }

    .select-btn:hover:not(:disabled) {
        background-color: var(--accent-hover-color);
    }

    .select-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .selected-count {
        color: var(--text-color);
        font-size: 0.9em;
    }

    .upload-btn {
        background-color: var(--success, #28a745);
        color: white;
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
        font-weight: bold;
    }

    .upload-btn:hover:not(:disabled) {
        opacity: 0.9;
    }

    .upload-btn:disabled {
        opacity: 0.7;
        cursor: not-allowed;
    }

    .error-message {
        background-color: var(--warning);
        color: var(--text-color);
        padding: 0.75em 1em;
        border-radius: 4px;
        margin-bottom: 1em;
    }

    .loading, .empty-state {
        text-align: center;
        padding: 2em;
        color: var(--text-color);
        opacity: 0.7;
    }

    .blob-list {
        display: flex;
        flex-direction: column;
        gap: 0.5em;
        width: 100%;
    }

    .blob-item {
        display: flex;
        align-items: center;
        gap: 1em;
        padding: 0.75em 1em;
        background-color: var(--card-bg);
        border-radius: 6px;
        cursor: pointer;
        transition: background-color 0.2s;
    }

    .blob-item:hover {
        background-color: var(--sidebar-bg);
    }

    .blob-thumbnail {
        width: 48px;
        height: 48px;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: var(--bg-color);
        border-radius: 4px;
        overflow: hidden;
    }

    .thumbnail-img,
    .thumbnail-video {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .thumbnail-icon {
        font-size: 1.5em;
    }

    .blob-info {
        flex: 1;
        min-width: 0;
    }

    .blob-hash {
        font-family: monospace;
        font-size: 0.9em;
        color: var(--text-color);
    }

    .hash-full {
        display: inline;
    }

    .hash-truncated {
        display: none;
    }

    .blob-meta {
        display: flex;
        gap: 1em;
        font-size: 0.8em;
        color: var(--text-color);
        opacity: 0.7;
        margin-top: 0.25em;
    }

    .blob-date {
        font-size: 0.85em;
        color: var(--text-color);
        opacity: 0.6;
        white-space: nowrap;
    }

    .delete-btn {
        background: transparent;
        border: 1px solid var(--warning);
        color: var(--warning);
        width: 1.75em;
        height: 1.75em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.85em;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .delete-btn:hover {
        background-color: var(--warning);
        color: var(--text-color);
    }

    /* Admin users list styles */
    .admin-users-list {
        display: flex;
        flex-direction: column;
        gap: 0.5em;
        width: 100%;
    }

    .user-stat-item {
        display: flex;
        align-items: center;
        gap: 1em;
        padding: 0.75em 1em;
        background-color: var(--card-bg);
        border-radius: 6px;
        cursor: pointer;
        transition: background-color 0.2s;
    }

    .user-stat-item:hover {
        background-color: var(--sidebar-bg);
    }

    .user-avatar-container {
        flex-shrink: 0;
    }

    .user-avatar {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        object-fit: cover;
    }

    .user-avatar-placeholder {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: var(--border-color);
    }

    .user-info {
        flex: 1;
        min-width: 0;
    }

    .user-name {
        font-weight: 500;
        color: var(--text-color);
    }

    .user-npub {
        font-family: monospace;
        font-size: 0.8em;
        color: var(--text-color);
        opacity: 0.6;
    }

    .npub-full {
        display: inline;
    }

    .npub-truncated {
        display: none;
    }

    .user-stats {
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        gap: 0.25em;
    }

    .user-stats .blob-count,
    .user-stats .total-size {
        font-size: 0.85em;
        color: var(--text-color);
        opacity: 0.7;
    }

    .login-prompt {
        text-align: center;
        padding: 2em;
        background-color: var(--card-bg);
        border-radius: 8px;
        border: 1px solid var(--border-color);
        max-width: 32em;
        margin: 1em;
    }

    .login-prompt p {
        margin: 0 0 1.5rem 0;
        color: var(--text-color);
        font-size: 1.1rem;
    }

    .login-btn {
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        padding: 0.75em 1.5em;
        border-radius: 4px;
        cursor: pointer;
        font-weight: bold;
        font-size: 0.9em;
    }

    .login-btn:hover {
        background-color: var(--accent-hover-color);
    }

    /* Modal styles */
    .modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0.8);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 1000;
    }

    .modal-content {
        background-color: var(--bg-color);
        border-radius: 8px;
        max-width: 90vw;
        max-height: 90vh;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .modal-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0.75em 1em;
        border-bottom: 1px solid var(--border-color);
        background-color: var(--card-bg);
    }

    .modal-title {
        display: flex;
        align-items: center;
        gap: 1em;
    }

    .modal-hash {
        font-family: monospace;
        color: var(--text-color);
    }

    .modal-type {
        font-size: 0.85em;
        color: var(--text-color);
        opacity: 0.7;
    }

    .modal-controls {
        display: flex;
        align-items: center;
        gap: 0.5em;
    }

    .zoom-btn {
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        width: 2em;
        height: 2em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 1em;
        font-weight: bold;
    }

    .zoom-btn:hover:not(:disabled) {
        background-color: var(--accent-hover-color);
    }

    .zoom-btn:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    .zoom-level {
        font-size: 0.85em;
        color: var(--text-color);
        min-width: 3em;
        text-align: center;
    }

    .close-btn {
        background: transparent;
        border: 1px solid var(--border-color);
        color: var(--text-color);
        width: 2em;
        height: 2em;
        border-radius: 4px;
        cursor: pointer;
        font-size: 1em;
        margin-left: 0.5em;
    }

    .close-btn:hover {
        background-color: var(--warning);
        border-color: var(--warning);
    }

    .modal-body {
        flex: 1;
        overflow: auto;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 1em;
        min-height: 200px;
    }

    .media-container {
        transition: transform 0.2s ease;
        transform-origin: center center;
    }

    .media-container img {
        max-width: 80vw;
        max-height: 70vh;
        object-fit: contain;
    }

    .media-container video {
        max-width: 80vw;
        max-height: 70vh;
    }

    .media-container.audio {
        width: 100%;
        padding: 2em;
    }

    .media-container audio {
        width: 100%;
    }

    .file-preview {
        text-align: center;
        padding: 2em;
        color: var(--text-color);
    }

    .file-icon {
        font-size: 4em;
        margin-bottom: 0.5em;
    }

    .download-link {
        display: inline-block;
        margin-top: 1em;
        padding: 0.75em 1.5em;
        background-color: var(--primary);
        color: var(--text-color);
        text-decoration: none;
        border-radius: 4px;
    }

    .download-link:hover {
        background-color: var(--accent-hover-color);
    }

    .modal-footer {
        display: flex;
        flex-direction: column;
        gap: 0.5em;
        padding: 0.75em 1em;
        border-top: 1px solid var(--border-color);
        background-color: var(--card-bg);
    }

    .blob-details {
        display: flex;
        gap: 1.5em;
        font-size: 0.85em;
        color: var(--text-color);
        opacity: 0.7;
    }

    .blob-url-section {
        display: flex;
        gap: 0.5em;
        width: 100%;
    }

    .blob-url-input {
        flex: 1;
        padding: 0.4em 0.6em;
        font-family: monospace;
        font-size: 0.85em;
        background-color: var(--bg-color);
        color: var(--text-color);
        border: 1px solid var(--border-color);
        border-radius: 4px;
        cursor: text;
    }

    .blob-url-input:focus {
        outline: none;
        border-color: var(--primary);
    }

    .copy-btn {
        padding: 0.4em 0.8em;
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.85em;
    }

    .copy-btn:hover {
        background-color: var(--accent-hover-color);
    }

    .modal-actions {
        display: flex;
        gap: 0.5em;
    }

    .action-btn {
        padding: 0.5em 1em;
        background-color: var(--primary);
        color: var(--text-color);
        border: none;
        border-radius: 4px;
        cursor: pointer;
        text-decoration: none;
        font-size: 0.9em;
    }

    .action-btn:hover {
        background-color: var(--accent-hover-color);
    }

    .action-btn.danger {
        background-color: transparent;
        border: 1px solid var(--warning);
        color: var(--warning);
    }

    .action-btn.danger:hover {
        background-color: var(--warning);
        color: var(--text-color);
    }

    @media (max-width: 720px) {
        .hash-full {
            display: none;
        }

        .hash-truncated {
            display: inline;
        }

        .npub-full {
            display: none;
        }

        .npub-truncated {
            display: inline;
        }
    }

    @media (max-width: 600px) {
        .blob-item {
            flex-wrap: wrap;
        }

        .blob-date {
            width: 100%;
            margin-top: 0.5em;
            padding-left: 3.5em;
        }

        .modal-footer {
            flex-direction: column;
            gap: 0.75em;
        }

        .blob-details {
            flex-direction: column;
            gap: 0.25em;
        }
    }
</style>
