<?php

/**
 * Media Manager View (G004)
 *
 * Renders the Media Management interface embedded in the Invitation Builder.
 * Supports per-slot upload (click + drag & drop), preview, replace, and delete.
 * All upload/delete operations use AJAX fetch — no page refresh required.
 *
 * Variables expected:
 *   $invitation_id — int    — Current invitation ID.
 *   $media         — array  — Current media metadata from invitation_sections.
 *   $media_urls    — array  — Serve URLs built from $media paths.
 *   $slots         — array  — All registered slot configs from config/storage.php.
 *   $couple_code   — string — workspace.slug used in path validation.
 *
 * @package Inveetaire
 * @ref     G004
 */

/** @var int    $invitation_id */
/** @var array  $media */
/** @var array  $media_urls */
/** @var array  $slots */
/** @var string $couple_code */
/** @var array  $flash */

$flash      = $flash      ?? [];
$media      = $media      ?? [];
$media_urls = $media_urls ?? [];
$slots      = $slots      ?? [];
?>
<link rel="stylesheet" href="<?= url('/assets/vendor/cropper/cropper.min.css') ?>">
<script src="<?= url('/assets/vendor/cropper/cropper.min.js') ?>"></script>
<?php
/**
 * Render a standard image-slot section (hero, couple, gallery, story, gift).
 *
 * @param string $slot       Slot key (e.g. 'hero').
 * @param string $label      Display label.
 * @param string $faIcon     FontAwesome icon name without 'fa-' (e.g. 'image').
 * @param array  $media      Current media metadata.
 * @param array  $mediaUrls  Serve URL map parallel to $media.
 * @param array  $slots      All slot configs.
 */
function mmRenderSlot(string $slot, string $label, string $faIcon, array $media, array $mediaUrls, array $slots): void
{
    $slotConfig = $slots[$slot] ?? ['max_count' => 1, 'max_size' => 10485760, 'allowed' => []];
    $maxCount   = (int) ($slotConfig['max_count'] ?? 1);
    $maxMb      = (int) round(($slotConfig['max_size'] ?? 10485760) / 1024 / 1024);
    $value      = $media[$slot] ?? null;
    $urls       = $mediaUrls[$slot] ?? null;

    // Normalise to arrays
    $paths   = is_array($value) ? array_values(array_filter($value)) : ($value ? [$value] : []);
    $urlList = is_array($urls)  ? array_values(array_filter($urls))  : ($urls  ? [$urls]  : []);
    $count   = count($paths);
    $atLimit = ($count >= $maxCount);

    $accept = implode(',', $slotConfig['allowed'] ?? ['image/jpeg', 'image/png', 'image/webp']);
    $multiAttr = ($maxCount > 1) ? 'multiple' : '';
    $slotSafe  = htmlspecialchars($slot, ENT_QUOTES, 'UTF-8');
    ?>
    <div class="mm-section">
        <div class="mm-section-header">
            <div class="mm-section-title">
                <i class="fa-solid fa-<?= $faIcon ?> text-slate-400"></i>
                <?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>
            </div>
            <span class="mm-limit-badge <?= $atLimit ? 'at-limit' : '' ?>" id="badge-<?= $slotSafe ?>">
                <?= $count ?> / <?= $maxCount ?>
            </span>
        </div>

        <!-- Existing file thumbnails -->
        <div class="mm-previews" id="previews-<?= $slotSafe ?>">
            <?php foreach ($paths as $i => $path):
                $url = $urlList[$i] ?? '';
                $pathSafe = htmlspecialchars($path, ENT_QUOTES, 'UTF-8');
            ?>
                <div class="mm-thumb" data-path="<?= $pathSafe ?>" data-slot="<?= $slotSafe ?>">
                    <img src="<?= htmlspecialchars($url, ENT_QUOTES, 'UTF-8') ?>"
                         alt="<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?> image"
                         loading="lazy">
                    <button class="mm-delete-btn"
                            onclick="mmDelete('<?= $slotSafe ?>', '<?= addslashes($path) ?>')"
                            title="Delete this image">
                        <i class="fa-solid fa-xmark"></i>
                    </button>
                </div>
            <?php endforeach; ?>
        </div>

        <!-- Drop zone -->
        <div class="mm-drop-zone <?= $atLimit ? 'at-limit' : '' ?>"
             id="drop-<?= $slotSafe ?>"
             onclick="if(!this.classList.contains('at-limit')) document.getElementById('file-<?= $slotSafe ?>').click()"
             ondragover="mmDragOver(event,'<?= $slotSafe ?>')"
             ondragleave="mmDragLeave(event,'<?= $slotSafe ?>')"
             ondrop="mmDrop(event,'<?= $slotSafe ?>')">
            <div class="mm-drop-icon"><i class="fa-solid fa-cloud-arrow-up"></i></div>
            <?php if ($atLimit): ?>
                <div class="mm-drop-text" style="color:#dc2626;">
                    <strong>Limit reached</strong> — delete a file to upload a new one
                </div>
            <?php else: ?>
                <div class="mm-drop-text"><strong>Click to upload</strong> or drag &amp; drop</div>
                <div class="mm-drop-subtext">
                    <?php 
                        $SLOT_SPECS = [
                            'hero' => 'Recommended: 1920 × 1080 px (16:9) · Min: 1600 × 900 px · Max ' . $maxMb . ' MB',
                            'couple' => 'Recommended: 1200 × 1500 px (4:5 Portrait) · Min: 800 × 1000 px · Max ' . $maxMb . ' MB',
                            'gallery' => 'Recommended: 1200 × 1600 px (3:4) · Max 10 images · Max ' . $maxMb . ' MB',
                            'story' => 'Recommended: 1200 × 1600 px (3:4) · Max 5 images · Max ' . $maxMb . ' MB',
                            'music_cover' => 'Recommended: 800 × 800 px (1:1 Square) · Max ' . $maxMb . ' MB',
                            'gift' => 'Recommended: 1000 × 1000 px (1:1 Square QRIS) · Max ' . $maxMb . ' MB',
                        ];
                        echo htmlspecialchars($SLOT_SPECS[$slot] ?? "JPG, PNG, WebP · Max {$maxMb} MB", ENT_QUOTES, 'UTF-8');
                    ?>
                </div>
            <?php endif; ?>
        </div>

        <input type="file"
               id="file-<?= $slotSafe ?>"
               accept="<?= htmlspecialchars($accept, ENT_QUOTES, 'UTF-8') ?>"
               style="display:none"
               <?= $multiAttr ?>
               onchange="mmUpload('<?= $slotSafe ?>',this)">
    </div>
    <?php
}

/**
 * Render a dual image-slot section (e.g. Hero, Couple) with Desktop and Mobile zones.
 */
function mmRenderDualSlot(string $slot, string $label, string $faIcon, array $media, array $mediaUrls, array $slots): void
{
    $slotConfig = $slots[$slot] ?? ['max_count' => 1, 'max_size' => 10485760, 'allowed' => []];
    $maxMb      = (int) round(($slotConfig['max_size'] ?? 10485760) / 1024 / 1024);
    $accept = implode(',', $slotConfig['allowed'] ?? ['image/jpeg', 'image/png', 'image/webp']);
    $slotSafe  = htmlspecialchars($slot, ENT_QUOTES, 'UTF-8');

    // Values can be arrays with 'desktop' and 'mobile' keys
    $value = $media[$slot] ?? null;
    $urls  = $mediaUrls[$slot] ?? null;

    $dPath = (is_array($value) && isset($value['desktop'])) ? $value['desktop'] : (is_string($value) ? $value : null);
    $mPath = (is_array($value) && isset($value['mobile']))  ? $value['mobile']  : null;
    $dUrl  = (is_array($urls) && isset($urls['desktop']))   ? $urls['desktop']  : (is_string($urls) ? $urls : null);
    $mUrl  = (is_array($urls) && isset($urls['mobile']))    ? $urls['mobile']   : null;

    $dSafePath = $dPath ? htmlspecialchars($dPath, ENT_QUOTES, 'UTF-8') : '';
    $mSafePath = $mPath ? htmlspecialchars($mPath, ENT_QUOTES, 'UTF-8') : '';
    $dSafeUrl  = $dUrl ? htmlspecialchars($dUrl, ENT_QUOTES, 'UTF-8') : '';
    $mSafeUrl  = $mUrl ? htmlspecialchars($mUrl, ENT_QUOTES, 'UTF-8') : '';

    $dId = $slotSafe . '_desktop';
    $mId = $slotSafe . '_mobile';
    ?>
    <div class="mm-section">
        <div class="mm-section-header">
            <div class="mm-section-title">
                <i class="fa-solid fa-<?= $faIcon ?> text-slate-400"></i>
                <?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>
            </div>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
            <!-- DESKTOP ZONE -->
            <div class="flex flex-col bg-slate-50 rounded-2xl p-5 border border-slate-200">
                <div class="text-sm font-bold text-slate-700 mb-4 flex items-center justify-between">
                    <span>Desktop / Tablet (16:9)</span>
                    <span class="text-[10px] font-bold uppercase tracking-wider text-slate-400 bg-white px-2 py-1 rounded border border-slate-200">1920 × 1080 px</span>
                </div>
                
                <div class="flex-1 flex flex-col">
                    <div class="mm-previews flex justify-center w-full mb-4" id="previews-<?= $dId ?>">
                        <?php if ($dPath): ?>
                            <div class="relative rounded-xl overflow-hidden border border-slate-200 shadow-sm w-full aspect-video" data-path="<?= $dSafePath ?>" data-slot="<?= $slotSafe ?>" data-subslot="desktop">
                                <img src="<?= $dSafeUrl ?>" alt="Desktop image" loading="lazy" class="w-full h-full object-cover">
                            </div>
                        <?php endif; ?>
                    </div>
                    
                    <div class="mm-drop-zone <?= $dPath ? 'hidden' : 'flex flex-col items-center justify-center' ?> w-full aspect-video bg-white mb-4" id="drop-<?= $dId ?>"
                         onclick="document.getElementById('file-<?= $dId ?>').click()"
                         ondragover="mmDragOver(event,'<?= $slotSafe ?>', 'desktop')"
                         ondragleave="mmDragLeave(event,'<?= $slotSafe ?>', 'desktop')"
                         ondrop="mmDrop(event,'<?= $slotSafe ?>', 'desktop')">
                        <div class="mm-drop-icon"><i class="fa-solid fa-image"></i></div>
                        <div class="mm-drop-text"><strong>Click or drag to upload</strong></div>
                    </div>

                    <div class="flex items-center justify-end gap-2 mt-auto pt-4 border-t border-slate-200">
                        <button type="button" onclick="document.getElementById('file-<?= $dId ?>').click()" class="btn px-4 py-2 text-xs font-bold text-slate-700 bg-white hover:bg-slate-50 border border-slate-200 rounded-xl transition shadow-sm flex items-center gap-1.5">
                            <i class="fa-solid fa-crop-simple"></i> Crop
                        </button>
                        <button type="button" onclick="document.getElementById('file-<?= $dId ?>').click()" class="btn px-4 py-2 text-xs font-bold text-white bg-amber-500 hover:bg-amber-600 rounded-xl transition shadow-sm flex items-center gap-1.5">
                            <i class="fa-solid fa-upload"></i> Upload
                        </button>
                        <button type="button" onclick="mmDelete('<?= $slotSafe ?>', document.querySelector('#previews-<?= $dId ?> > div')?.getAttribute('data-path') || '<?= addslashes($dPath) ?>', 'desktop')" class="btn px-4 py-2 text-xs font-bold text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 rounded-xl transition shadow-sm flex items-center gap-1.5 <?= $dPath ? '' : 'hidden' ?>" id="delete-btn-<?= $dId ?>">
                            <i class="fa-solid fa-trash-can"></i> Delete
                        </button>
                    </div>
                </div>
                <input type="file" id="file-<?= $dId ?>" accept="<?= htmlspecialchars($accept, ENT_QUOTES, 'UTF-8') ?>" style="display:none" onchange="mmUpload('<?= $slotSafe ?>',this, 'desktop')">
            </div>

            <!-- MOBILE ZONE -->
            <div class="flex flex-col bg-slate-50 rounded-2xl p-5 border border-slate-200">
                <div class="text-sm font-bold text-slate-700 mb-4 flex items-center justify-between">
                    <span>Phone (9:16)</span>
                    <span class="text-[10px] font-bold uppercase tracking-wider text-slate-400 bg-white px-2 py-1 rounded border border-slate-200">1080 × 1920 px</span>
                </div>
                
                <div class="flex-1 flex flex-col">
                    <div class="mm-previews flex justify-center w-full mb-4" id="previews-<?= $mId ?>">
                        <?php if ($mPath): ?>
                            <div class="relative rounded-xl overflow-hidden border border-slate-200 shadow-sm w-[200px] lg:w-[240px] aspect-[9/16]" data-path="<?= $mSafePath ?>" data-slot="<?= $slotSafe ?>" data-subslot="mobile">
                                <img src="<?= $mSafeUrl ?>" alt="Mobile image" loading="lazy" class="w-full h-full object-cover">
                            </div>
                        <?php endif; ?>
                    </div>
                    
                    <div class="mx-auto mm-drop-zone <?= $mPath ? 'hidden' : 'flex flex-col items-center justify-center' ?> w-[200px] lg:w-[240px] aspect-[9/16] bg-white mb-4" id="drop-<?= $mId ?>"
                         onclick="document.getElementById('file-<?= $mId ?>').click()"
                         ondragover="mmDragOver(event,'<?= $slotSafe ?>', 'mobile')"
                         ondragleave="mmDragLeave(event,'<?= $slotSafe ?>', 'mobile')"
                         ondrop="mmDrop(event,'<?= $slotSafe ?>', 'mobile')">
                        <div class="mm-drop-icon"><i class="fa-solid fa-mobile-screen"></i></div>
                        <div class="mm-drop-text"><strong>Click or drag to upload</strong></div>
                    </div>

                    <div class="flex items-center justify-end gap-2 mt-auto pt-4 border-t border-slate-200">
                        <button type="button" onclick="document.getElementById('file-<?= $mId ?>').click()" class="btn px-4 py-2 text-xs font-bold text-slate-700 bg-white hover:bg-slate-50 border border-slate-200 rounded-xl transition shadow-sm flex items-center gap-1.5">
                            <i class="fa-solid fa-crop-simple"></i> Crop
                        </button>
                        <button type="button" onclick="document.getElementById('file-<?= $mId ?>').click()" class="btn px-4 py-2 text-xs font-bold text-white bg-amber-500 hover:bg-amber-600 rounded-xl transition shadow-sm flex items-center gap-1.5">
                            <i class="fa-solid fa-upload"></i> Upload
                        </button>
                        <button type="button" onclick="mmDelete('<?= $slotSafe ?>', document.querySelector('#previews-<?= $mId ?> > div')?.getAttribute('data-path') || '<?= addslashes($mPath) ?>', 'mobile')" class="btn px-4 py-2 text-xs font-bold text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 rounded-xl transition shadow-sm flex items-center gap-1.5 <?= $mPath ? '' : 'hidden' ?>" id="delete-btn-<?= $mId ?>">
                            <i class="fa-solid fa-trash-can"></i> Delete
                        </button>
                    </div>
                </div>
                <input type="file" id="file-<?= $mId ?>" accept="<?= htmlspecialchars($accept, ENT_QUOTES, 'UTF-8') ?>" style="display:none" onchange="mmUpload('<?= $slotSafe ?>',this, 'mobile')">
            </div>
        </div>
    </div>
    <?php
}

?>

<style>
/* ── Media Manager Styles (G004) ─────────────────────────────────── */
.mm-section {
    background: #fff;
    border: 1px solid #e2e8f0;
    border-radius: 12px;
    padding: 1.5rem;
    margin-bottom: 1.5rem;
}
.mm-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid #f1f5f9;
    padding-bottom: 1rem;
    margin-bottom: 1.25rem;
}
.mm-section-title {
    font-size: 0.9rem;
    font-weight: 700;
    color: #1e293b;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.mm-limit-badge {
    font-size: 0.675rem;
    font-weight: 600;
    background: #f1f5f9;
    color: #64748b;
    padding: 0.2rem 0.6rem;
    border-radius: 20px;
    white-space: nowrap;
}
.mm-limit-badge.at-limit {
    background: #fef2f2;
    color: #dc2626;
}
.mm-previews {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1rem;
    min-height: 0;
}
.mm-thumb {
    position: relative;
    width: 96px;
    height: 96px;
    border-radius: 8px;
    overflow: hidden;
    border: 2px solid #e2e8f0;
    background: #f8fafc;
    flex-shrink: 0;
}
.mm-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.mm-audio-thumb {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #4f46e5, #7c3aed);
    color: white;
    font-size: 2rem;
}
.mm-delete-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: rgba(220,38,38,0.9);
    border: none;
    color: white;
    font-size: 0.6rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.15s;
    line-height: 1;
}
.mm-thumb:hover .mm-delete-btn {
    opacity: 1;
}
.mm-drop-zone {
    border: 2px dashed #cbd5e1;
    border-radius: 10px;
    padding: 1.5rem 1rem;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.2s, background 0.2s;
    background: #f8fafc;
    user-select: none;
}
.mm-drop-zone:hover,
.mm-drop-zone.drag-over {
    border-color: #b5835a;
    background: #fdf8f3;
}
.mm-drop-zone.at-limit {
    cursor: not-allowed;
    opacity: 0.55;
    pointer-events: none;
}
.mm-drop-icon { font-size: 1.75rem; color: #94a3b8; margin-bottom: 0.5rem; }
.mm-drop-text { font-size: 0.8rem; color: #64748b; line-height: 1.4; }
.mm-drop-text strong { color: #b5835a; }
.mm-drop-subtext { font-size: 0.7rem; color: #94a3b8; margin-top: 0.25rem; }
.mm-uploading-overlay {
    position: absolute;
    inset: 0;
    background: rgba(255,255,255,0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-size: 0.75rem;
    color: #b5835a;
    font-weight: 600;
    z-index: 2;
}
.mm-spinner {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 2px solid #e2e8f0;
    border-top-color: #b5835a;
    border-radius: 50%;
    animation: mm-spin 0.7s linear infinite;
    margin-right: 0.4rem;
    flex-shrink: 0;
}
@keyframes mm-spin { to { transform: rotate(360deg); } }

/* Music sub-section label */
.mm-sub-label {
    font-size: 0.7rem;
    font-weight: 600;
    color: #64748b;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 0.6rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.mm-divider { border: none; border-top: 1px solid #f1f5f9; margin: 1.25rem 0; }

/* Toast */
.mm-toast {
    position: fixed;
    bottom: 2rem;
    right: 2rem;
    padding: 0.75rem 1.25rem;
    border-radius: 10px;
    font-size: 0.8rem;
    font-weight: 600;
    color: white;
    z-index: 9999;
    box-shadow: 0 4px 16px rgba(0,0,0,0.18);
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.25s, transform 0.25s;
    pointer-events: none;
    max-width: 320px;
}
.mm-toast.show { opacity: 1; transform: translateY(0); }
.mm-toast.success { background: #16a34a; }
.mm-toast.error   { background: #dc2626; }
</style>

<div class="w-full mx-auto px-4 sm:px-6 lg:px-8">

    <!-- ── Page Header ── -->
    <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-8">
        <div>
            <div class="page-breadcrumb">
                <a href="<?= url('/app/couple/dashboard') ?>">Dashboard</a>
                <span class="sep">/</span>
                <a href="<?= url('/app/couple/invitation/edit') ?>">Invitation</a>
                <span class="sep">/</span>
                <span>Media</span>
            </div>
            <h1 class="page-h1">
                <span class="page-header-icon"><i class="fa-solid fa-photo-film"></i></span>
                Media Manager
            </h1>
            <p class="page-subtitle">Upload, preview, and manage all visual and audio assets for your invitation.</p>
        </div>

        <!-- Header Save Action & Status Badge -->
        <div class="flex items-center gap-3 bg-white p-2.5 rounded-2xl border border-slate-200 shadow-xs">
            <div class="flex items-center gap-2 text-xs px-2">
                <span id="top-save-status-dot" class="w-2.5 h-2.5 rounded-full bg-emerald-500"></span>
                <span id="top-save-status-text" class="font-semibold text-slate-700 text-xs">All Changes Saved</span>
            </div>
            <button type="button" id="top-save-btn" onclick="triggerMusicSave()" class="btn btn-primary px-5 py-2.5 text-xs font-bold text-white flex items-center gap-2 rounded-xl transition-all shadow-sm opacity-50 cursor-not-allowed" disabled>
                <i class="fa-solid fa-floppy-disk"></i> Save Changes
            </button>
        </div>
    </div>

    <!-- Flash error (server-side only) -->
    <?php if (!empty($flash['error'])): ?>
        <div class="card p-4 mb-6" style="border-left:3px solid #dc2626; background:#fef2f2;">
            <p class="text-sm" style="color:#dc2626;"><?= escape_html($flash['error']) ?></p>
        </div>
    <?php endif; ?>

    <!-- ── HERO IMAGE ──────────────────────────────────────────────────────── -->
    <?php mmRenderDualSlot('hero',   'Hero Image',    'image',     $media, $media_urls, $slots); ?>

    <!-- ── SECTION BACKGROUND ────────────────────────────────────────────────── -->
    <?php 
    // If 'background' slot is registered in storage.php
    if (isset($slots['background'])) {
        mmRenderDualSlot('background', 'Section Background', 'image', $media, $media_urls, $slots); 
    }
    ?>

    <!-- ── COUPLE PHOTOS ──────────────────────────────────────────────────── -->
    <?php mmRenderSlot('couple', 'Couple Photos', 'heart',     $media, $media_urls, $slots); ?>

    <!-- ── GALLERY ────────────────────────────────────────────────────────── -->
    <?php mmRenderSlot('gallery','Gallery',       'images',    $media, $media_urls, $slots); ?>

    <!-- ── STORY IMAGES ───────────────────────────────────────────────────── -->
    <?php mmRenderSlot('story',  'Story Images',  'book-open', $media, $media_urls, $slots); ?>

    <!-- ── MUSIC ──────────────────────────────────────────────────────────── -->
    <?php
        $coverPath  = $media['music_cover'] ?? null;
        $coverUrl   = $media_urls['music_cover'] ?? null;
        $coverCount = $coverPath ? 1 : 0;
        $coverMax   = (int) ($slots['music_cover']['max_count'] ?? 1);

        $musicPath  = $media['music_file'] ?? null;
        $musicCount = $musicPath ? 1 : 0;
        $musicMax   = (int) ($slots['music_file']['max_count'] ?? 1);
    ?>
    <div class="mm-section">
        <div class="mm-section-header">
            <div class="mm-section-title">
                <i class="fa-solid fa-music text-slate-400"></i>
                Music
            </div>
        </div>

        <!-- Music Cover Art sub-slot -->
        <div class="mm-sub-label">
            Cover Art
            <span class="mm-limit-badge <?= $coverCount >= $coverMax ? 'at-limit' : '' ?>" id="badge-music_cover">
                <?= $coverCount ?> / <?= $coverMax ?>
            </span>
        </div>
        <div class="mm-previews" id="previews-music_cover">
            <?php if ($coverPath && $coverUrl): ?>
                <div class="mm-thumb" data-path="<?= escape_html($coverPath) ?>" data-slot="music_cover">
                    <img src="<?= escape_html($coverUrl) ?>" alt="Music cover art" loading="lazy">
                    <button class="mm-delete-btn"
                            onclick="mmDelete('music_cover','<?= addslashes($coverPath) ?>')"
                            title="Delete">
                        <i class="fa-solid fa-xmark"></i>
                    </button>
                </div>
            <?php endif; ?>
        </div>
        <div class="mm-drop-zone <?= $coverCount >= $coverMax ? 'at-limit' : '' ?>"
             id="drop-music_cover"
             onclick="if(!this.classList.contains('at-limit')) document.getElementById('file-music_cover').click()"
             ondragover="mmDragOver(event,'music_cover')"
             ondragleave="mmDragLeave(event,'music_cover')"
             ondrop="mmDrop(event,'music_cover')">
            <div class="mm-drop-icon"><i class="fa-solid fa-image"></i></div>
            <?php if ($coverCount >= $coverMax): ?>
                <div class="mm-drop-text" style="color:#dc2626;"><strong>Limit reached</strong> — delete to replace</div>
            <?php else: ?>
                <div class="mm-drop-text"><strong>Click to upload</strong> or drag &amp; drop</div>
                <div class="mm-drop-subtext">JPG, PNG, WebP · Max 5 MB</div>
            <?php endif; ?>
        </div>
        <input type="file" id="file-music_cover" accept="image/jpeg,image/png,image/webp"
               style="display:none" onchange="mmUpload('music_cover',this)">

        <hr class="mm-divider">

        <!-- Music File (MP3) sub-slot -->
        <div class="mm-sub-label" style="margin-top: 0.25rem;">
            MP3 Track
            <span class="mm-limit-badge <?= $musicCount >= $musicMax ? 'at-limit' : '' ?>" id="badge-music_file">
                <?= $musicCount ?> / <?= $musicMax ?>
            </span>
        </div>
        <div class="mm-previews" id="previews-music_file">
            <?php if ($musicPath): ?>
                <div class="mm-thumb" data-path="<?= escape_html($musicPath) ?>" data-slot="music_file">
                    <div class="mm-audio-thumb"><i class="fa-solid fa-music"></i></div>
                    <button class="mm-delete-btn"
                            onclick="mmDelete('music_file','<?= addslashes($musicPath) ?>')"
                            title="Delete">
                        <i class="fa-solid fa-xmark"></i>
                    </button>
                </div>
            <?php endif; ?>
        </div>
        <div class="mm-drop-zone <?= $musicCount >= $musicMax ? 'at-limit' : '' ?>"
             id="drop-music_file"
             onclick="if(!this.classList.contains('at-limit')) document.getElementById('file-music_file').click()"
             ondragover="mmDragOver(event,'music_file')"
             ondragleave="mmDragLeave(event,'music_file')"
             ondrop="mmDrop(event,'music_file')">
            <div class="mm-drop-icon"><i class="fa-solid fa-file-audio"></i></div>
            <?php if ($musicCount >= $musicMax): ?>
                <div class="mm-drop-text" style="color:#dc2626;"><strong>Limit reached</strong> — delete to replace</div>
            <?php else: ?>
                <div class="mm-drop-text"><strong>Click to upload</strong> or drag &amp; drop</div>
                <div class="mm-drop-subtext">MP3 only · Max 20 MB</div>
            <?php endif; ?>
        </div>
        <input type="file" id="file-music_file" accept="audio/mpeg,audio/mp3"
               style="display:none" onchange="mmUpload('music_file',this)">

        <hr class="mm-divider">

        <!-- Music Settings Section (G007) -->
        <div class="mm-sub-label" style="margin-top: 0.25rem;">
            Music Configuration
        </div>

        <form id="music-settings-form" class="space-y-4 text-slate-700" onsubmit="saveMusicSettings(event)">
            <!-- Enable music check -->
            <div class="flex items-center gap-2.5">
                <input type="checkbox" id="music_enabled" name="music_enabled" value="1"
                       class="h-4 w-4 rounded border-slate-300 text-[#D4A28C] focus:ring-[#D4A28C]"
                       <?= ($media['music_enabled'] ?? false) ? 'checked' : '' ?>>
                <label for="music_enabled" class="text-xs font-semibold text-slate-800">
                    Enable Background Music on Invitation Page
                </label>
            </div>

            <!-- Song Title & Artist -->
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div class="form-group">
                    <label for="music_title" class="block text-[10px] uppercase font-bold text-slate-400 mb-1">Song Title</label>
                    <input type="text" id="music_title" name="music_title" 
                           placeholder="e.g. Beautiful in White" 
                           value="<?= htmlspecialchars((string) ($media['music_title'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                           class="w-full text-xs p-2.5 bg-slate-50 border border-slate-200 rounded-lg text-slate-700 focus:outline-none focus:border-[#D4A28C]">
                </div>
                <div class="form-group">
                    <label for="music_artist" class="block text-[10px] uppercase font-bold text-slate-400 mb-1">Artist Name</label>
                    <input type="text" id="music_artist" name="music_artist" 
                           placeholder="e.g. Shane Filan" 
                           value="<?= htmlspecialchars((string) ($media['music_artist'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                           class="w-full text-xs p-2.5 bg-slate-50 border border-slate-200 rounded-lg text-slate-700 focus:outline-none focus:border-[#D4A28C]">
                </div>
            </div>

            <!-- Playback flags -->
            <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-1.5">
                <div class="flex items-center gap-2">
                    <input type="checkbox" id="music_autoplay" name="music_autoplay" value="1"
                           class="h-4 w-4 rounded border-slate-300 text-[#D4A28C] focus:ring-[#D4A28C]"
                           <?= ($media['music_autoplay'] ?? true) ? 'checked' : '' ?>>
                    <label for="music_autoplay" class="text-xs text-slate-600">Autoplay Track</label>
                </div>
                <div class="flex items-center gap-2">
                    <input type="checkbox" id="music_loop" name="music_loop" value="1"
                           class="h-4 w-4 rounded border-slate-300 text-[#D4A28C] focus:ring-[#D4A28C]"
                           <?= ($media['music_loop'] ?? true) ? 'checked' : '' ?>>
                    <label for="music_loop" class="text-xs text-slate-600">Loop Audio</label>
                </div>
                <div class="flex items-center gap-2">
                    <input type="checkbox" id="music_minimized" name="music_minimized" value="1"
                           class="h-4 w-4 rounded border-slate-300 text-[#D4A28C] focus:ring-[#D4A28C]"
                           <?= ($media['music_minimized'] ?? false) ? 'checked' : '' ?>>
                    <label for="music_minimized" class="text-xs text-slate-600">Start Player Minimized</label>
                </div>
            </div>

            <!-- Default Volume Slider -->
            <div class="space-y-1.5 pt-1.5">
                <div class="flex justify-between items-center">
                    <label for="music_volume" class="block text-[10px] uppercase font-bold text-slate-400">Default Volume</label>
                    <span id="volume-indicator" class="text-xs font-semibold text-[#D4A28C] font-mono">
                        <?= (int) ($media['music_volume'] ?? 50) ?>%
                    </span>
                </div>
                <input type="range" id="music_volume" name="music_volume" min="0" max="100" 
                       value="<?= (int) ($media['music_volume'] ?? 50) ?>"
                       oninput="document.getElementById('volume-indicator').innerText = this.value + '%'"
                       class="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-[#D4A28C]">
            </div>

            <!-- Submit Button -->
            <div class="pt-3">
                <button type="submit" id="save-settings-btn"
                        class="w-full md:w-auto inline-flex justify-center items-center px-6 py-2.5 text-xs font-bold text-white bg-[#D4A28C] hover:bg-[#c48d75] rounded-xl transition shadow-sm gap-2">
                    <i class="fa-solid fa-floppy-disk"></i> Save Music Settings
                </button>
            </div>
        </form>
    </div>

    <!-- ── GIFT / QRIS ─────────────────────────────────────────────────────── -->
    <?php mmRenderSlot('gift', 'Gift / QRIS', 'gift', $media, $media_urls, $slots); ?>

</div>

<!-- Cropper Modal (Tailwind CSS) -->
<div id="cropper-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black/75 backdrop-blur-sm transition-opacity duration-300">
    <div class="bg-white rounded-2xl max-w-2xl w-full p-6 shadow-2xl space-y-4 transform scale-95 transition-transform duration-300 flex flex-col max-h-[90vh]">
        <div class="flex items-center justify-between border-b pb-3">
            <h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
                <i class="fa-solid fa-crop-simple text-amber-600"></i>
                Crop Invitation Image
            </h3>
            <button onclick="closeCropperModal()" class="text-slate-400 hover:text-slate-600 transition-colors">
                <i class="fa-solid fa-xmark text-lg"></i>
            </button>
        </div>
        
        <div class="flex-1 min-h-[300px] max-h-[50vh] bg-slate-950 rounded-lg overflow-hidden flex items-center justify-center relative">
            <img id="cropper-image" src="" alt="Image to crop" class="max-w-full max-h-full block">
        </div>
        
        <!-- Controls -->
        <div class="flex flex-wrap items-center justify-between gap-3 pt-2">
            <div class="flex items-center gap-2">
                <button type="button" id="btn-zoom-in" class="p-2 bg-slate-100 hover:bg-slate-200 active:bg-slate-300 text-slate-700 rounded-lg transition-colors" title="Zoom In">
                    <i class="fa-solid fa-magnifying-glass-plus"></i>
                </button>
                <button type="button" id="btn-zoom-out" class="p-2 bg-slate-100 hover:bg-slate-200 active:bg-slate-300 text-slate-700 rounded-lg transition-colors" title="Zoom Out">
                    <i class="fa-solid fa-magnifying-glass-minus"></i>
                </button>
                <button type="button" id="btn-rotate-left" class="p-2 bg-slate-100 hover:bg-slate-200 active:bg-slate-300 text-slate-700 rounded-lg transition-colors" title="Rotate Left">
                    <i class="fa-solid fa-rotate-left"></i>
                </button>
                <button type="button" id="btn-rotate-right" class="p-2 bg-slate-100 hover:bg-slate-200 active:bg-slate-300 text-slate-700 rounded-lg transition-colors" title="Rotate Right">
                    <i class="fa-solid fa-rotate-right"></i>
                </button>
            </div>
            
            <div class="flex gap-2">
                <button type="button" onclick="closeCropperModal()" class="btn btn-secondary px-4 py-2 text-xs font-bold">
                    Cancel
                </button>
                <button type="button" id="btn-crop-submit" class="btn btn-primary px-5 py-2 text-xs font-bold text-white flex items-center gap-1.5">
                    <i class="fa-solid fa-check"></i> Crop &amp; Upload
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Sticky Save Indicator & Action Bar -->
<div id="sticky-save-bar" class="fixed bottom-6 right-6 z-40 flex items-center gap-3.5 bg-slate-900/90 backdrop-blur-md border border-slate-800 text-white px-5 py-3 rounded-2xl shadow-2xl transition-all duration-300">
    <div class="flex items-center gap-2 text-xs">
        <span id="save-status-dot" class="w-2.5 h-2.5 rounded-full bg-emerald-400"></span>
        <span id="save-status-text" class="font-medium text-slate-300 text-xxs">All changes saved</span>
    </div>
    <button type="button" id="sticky-save-btn" onclick="triggerMusicSave()" class="btn bg-pink-600 hover:bg-pink-500 active:bg-pink-700 text-white text-xs font-bold px-4 py-2 rounded-xl transition-all opacity-40 cursor-not-allowed" disabled>
        <i class="fa-solid fa-floppy-disk mr-1"></i> Save Changes
    </button>
</div>

<!-- Toast notification element -->
<div id="mm-toast" class="mm-toast" role="alert" aria-live="assertive"></div>

<script>
/**
 * Media Engine — Client-side Upload & Delete (G004)
 * All state updates happen in-place via fetch() — no page reload required.
 */
(function () {
    'use strict';

    const CSRF    = '<?= escape_javascript(\App\Core\Csrf::getToken()) ?>';
    const UP_URL  = '<?= url('/app/couple/invitation/media/upload') ?>';
    const DEL_URL = '<?= url('/app/couple/invitation/media/delete') ?>';

    // ── G005 Image Processing Variables & Dimensions ──────────────────────────
    const SLOT_DIMENSIONS = {
        hero: 1920,
        couple: 1600,
        gallery: 1600,
        story: 1600,
        gift: 1200,
        music_cover: 1200
    };

    let cropQueue = [];
    let currentCropItem = null;
    let cropperInstance = null;

    // Initialize rotation & zoom buttons
    document.getElementById('btn-zoom-in').onclick     = () => cropperInstance && cropperInstance.zoom(0.1);
    document.getElementById('btn-zoom-out').onclick    = () => cropperInstance && cropperInstance.zoom(-0.1);
    document.getElementById('btn-rotate-left').onclick  = () => cropperInstance && cropperInstance.rotate(-90);
    document.getElementById('btn-rotate-right').onclick = () => cropperInstance && cropperInstance.rotate(90);

    // ── Upload Queue & Modal Management ───────────────────────────────────────

    window.mmUpload = function (slot, input, subSlot = '') {
        const files = Array.from(input.files || []);
        if (!files.length) return;
        input.value = ''; // Reset so the same file can be re-chosen later
        
        files.forEach(file => {
            if (slot === 'music_file' || !file.type.startsWith('image/')) {
                doUploadDirect(slot, file, subSlot);
            } else {
                cropQueue.push({ slot, file, subSlot });
            }
        });
        processCropQueue();
    };

    function processCropQueue() {
        if (currentCropItem || cropQueue.length === 0) return;

        currentCropItem = cropQueue.shift();
        const { slot, file, subSlot } = currentCropItem;
        openCropper(slot, file, subSlot);
    }

    function openCropper(slot, file, subSlot) {
        const modal = document.getElementById('cropper-modal');
        const cropImg = document.getElementById('cropper-image');
        if (!modal || !cropImg) return;

        const reader = new FileReader();
        reader.onload = function (e) {
            if (cropperInstance) {
                cropperInstance.destroy();
                cropperInstance = null;
            }

            cropImg.onload = function () {
                modal.classList.remove('hidden');

                // Official Media Specification Crop Ratios
                let aspectRatio = NaN;
                if (subSlot === 'desktop') {
                    aspectRatio = 16 / 9;
                } else if (subSlot === 'mobile') {
                    aspectRatio = 9 / 16;
                } else {
                    const CROP_RATIOS = {
                        hero: 16 / 9,        // Fallback for hero
                        background: 16 / 9,  // Fallback for background
                        couple: 4 / 5,
                        gallery: 3 / 4,
                        story: 3 / 4,
                        gift: 1,
                        music_cover: 1
                    };
                    aspectRatio = CROP_RATIOS[slot] || NaN;
                }

                // Client-side Advisory Resolution Checks
                const MIN_RECOMMENDED_RES = {
                    hero: { w: 1600, h: 900 },
                    couple: { w: 800, h: 1000 },
                    gallery: { w: 900, h: 1200 },
                    story: { w: 900, h: 1200 },
                    gift: { w: 500, h: 500 },
                    music_cover: { w: 400, h: 400 }
                };

                const minRes = MIN_RECOMMENDED_RES[slot];
                if (minRes && (cropImg.naturalWidth < minRes.w || cropImg.naturalHeight < minRes.h)) {
                    toast('warning', `Notice: Image resolution (${cropImg.naturalWidth}×${cropImg.naturalHeight}px) is below recommended minimum (${minRes.w}×${minRes.h}px).`);
                }

                cropperInstance = new Cropper(cropImg, {
                    aspectRatio: aspectRatio,
                    viewMode: 1,
                    dragMode: 'move',
                    autoCropArea: 0.9,
                    restore: false,
                    guides: true,
                    center: true,
                    highlight: false,
                    cropBoxMovable: true,
                    cropBoxResizable: true,
                    toggleDragModeOnDblclick: false,
                });
            };

            cropImg.src = e.target.result;

            // Bind crop submission
            const submitBtn = document.getElementById('btn-crop-submit');
            submitBtn.onclick = function () {
                if (!cropperInstance) return;

                const maxDim = SLOT_DIMENSIONS[slot] || 1600;
                const croppedCanvas = cropperInstance.getCroppedCanvas({
                    maxWidth: maxDim,
                    maxHeight: maxDim,
                    imageSmoothingEnabled: true,
                    imageSmoothingQuality: 'high'
                });

                if (!croppedCanvas) {
                    toast('error', 'Cropping failed — invalid canvas.');
                    return;
                }

                croppedCanvas.toBlob((blob) => {
                    if (!blob) {
                        toast('error', 'Failed to generate image.');
                        return;
                    }
                    const croppedFile = new File([blob], file.name.substring(0, file.name.lastIndexOf('.')) + '.webp', {
                        type: 'image/webp',
                        lastModified: Date.now()
                    });

                    closeCropperModal();
                    doUploadDirect(slot, croppedFile);
                }, 'image/webp', 0.85);
            };
        };
        reader.readAsDataURL(file);
    }

    window.closeCropperModal = function () {
        const modal = document.getElementById('cropper-modal');
        if (modal) modal.classList.add('hidden');
        if (cropperInstance) {
            cropperInstance.destroy();
            cropperInstance = null;
        }
        currentCropItem = null;
        // Continue processing the queue
        processCropQueue();
    };

    function doUploadDirect(slot, file, subSlot = '') {
        const dropElId = subSlot ? 'drop-' + slot + '_' + subSlot : 'drop-' + slot;
        const dropEl = document.getElementById(dropElId);
        if (!dropEl) return;

        // Show spinner on drop zone
        dropEl.classList.add('at-limit');
        dropEl.style.position = 'relative';
        const overlay = makeOverlay();
        dropEl.appendChild(overlay);

        const fd = new FormData();
        fd.append('file', file);
        fd.append('slot', slot);
        if (subSlot) fd.append('sub_slot', subSlot);
        fd.append('_csrf', CSRF);

        fetch(UP_URL, { method: 'POST', body: fd })
            .then(r => r.json())
            .then(resp => {
                safeRemove(dropEl, overlay);

                if (resp.status === 'success' && resp.data) {
                    const { path, url, count, max } = resp.data;
                    const isAudio = (slot === 'music_file');

                    // Insert new thumbnail into previews container
                    const containerId = subSlot ? 'previews-' + slot + '_' + subSlot : 'previews-' + slot;
                    const container = document.getElementById(containerId);
                    if (container) {
                        // Clear container first for subSlot (max 1)
                        if (subSlot) container.innerHTML = '';
                        container.appendChild(makeThumb(slot, path, url, isAudio, subSlot));
                    }

                    if (!subSlot) {
                        refreshBadge(slot, count, max);
                        refreshDropZone(slot, dropEl, count, max);
                    } else {
                        dropEl.classList.add('hidden');
                        dropEl.classList.remove('flex', 'flex-col', 'items-center', 'justify-center');
                        const delBtn = document.getElementById('delete-btn-' + slot + '_' + subSlot);
                        if (delBtn) {
                            delBtn.classList.remove('hidden');
                            // update path attribute for delete
                            delBtn.setAttribute('onclick', `mmDelete('${slot}', '${path.replace(/'/g, "\\'")}', '${subSlot}')`);
                        }
                    }
                    toast('success', resp.message || 'Uploaded.');
                } else {
                    dropEl.classList.remove('at-limit');
                    refreshFromBadge(slot, dropEl, subSlot);
                    toast('error', resp.message || 'Upload failed.');
                }
            })
            .catch(() => {
                safeRemove(dropEl, overlay);
                dropEl.classList.remove('at-limit');
                refreshFromBadge(slot, dropEl, subSlot);
                toast('error', 'Network error — please try again.');
            });
    }

    // fallback when upload fails so drop zone re-enables based on current count
    function refreshFromBadge(slot, dropEl, subSlot = '') {
        // Only if it's not a dual slot since dual slots max is always 1 per subslot
        if (!subSlot) {
            const badge = document.getElementById('badge-' + slot);
            if (badge) {
                const parts = badge.textContent.split('/');
                if (parts.length === 2) {
                    const c = parseInt(parts[0].trim());
                    const m = parseInt(parts[1].trim());
                    refreshDropZone(slot, dropEl, c, m);
                    return;
                }
            }
        } else {
            // For dual slot, if it failed, it means count is 0
            refreshDropZone(slot, dropEl, 0, 1);
            return;
        }
        dropEl.classList.remove('at-limit');
    }

    // ── Dirty State & Unsaved Changes Guard ───────────────────────────────────
    let isDirty = false;

    function markDirty() {
        isDirty = true;
        window.isMediaDirty = true;

        ['sticky-save-btn', 'top-save-btn'].forEach(id => {
            const btn = document.getElementById(id);
            if (btn) {
                btn.disabled = false;
                btn.classList.remove('opacity-40', 'cursor-not-allowed', 'opacity-50');
                btn.classList.add('opacity-100', 'cursor-pointer');
            }
        });

        ['save-status-dot', 'top-save-status-dot'].forEach(id => {
            const dot = document.getElementById(id);
            if (dot) dot.className = 'w-2.5 h-2.5 rounded-full bg-amber-400 animate-pulse';
        });

        ['save-status-text', 'top-save-status-text'].forEach(id => {
            const txt = document.getElementById(id);
            if (txt) txt.textContent = 'Unsaved changes';
        });
    }

    function setSavedState() {
        isDirty = false;
        window.isMediaDirty = false;

        ['sticky-save-btn', 'top-save-btn'].forEach(id => {
            const btn = document.getElementById(id);
            if (btn) {
                btn.disabled = true;
                btn.classList.add('opacity-40', 'cursor-not-allowed');
                btn.classList.remove('opacity-100', 'cursor-pointer');
                btn.innerHTML = '<i class="fa-solid fa-check mr-1"></i> Saved ✓';
                setTimeout(() => {
                    btn.innerHTML = '<i class="fa-solid fa-floppy-disk mr-1"></i> Save Changes';
                }, 2500);
            }
        });

        ['save-status-dot', 'top-save-status-dot'].forEach(id => {
            const dot = document.getElementById(id);
            if (dot) dot.className = 'w-2.5 h-2.5 rounded-full bg-emerald-500';
        });

        ['save-status-text', 'top-save-status-text'].forEach(id => {
            const txt = document.getElementById(id);
            if (txt) txt.textContent = 'All changes saved';
        });
    }

    // Attach dirty listeners to all inputs in media settings form
    function bindDirtyEvents() {
        const musicForm = document.getElementById('music-settings-form') || document.querySelector('form');
        if (musicForm) {
            musicForm.querySelectorAll('input, select, textarea').forEach(input => {
                input.addEventListener('change', markDirty);
                input.addEventListener('input', markDirty);
            });
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', bindDirtyEvents);
    } else {
        bindDirtyEvents();
    }

    // Delegated event listener to guarantee changes are captured even if dynamically updated
    document.addEventListener('change', (e) => {
        if (e.target && (e.target.closest('#music-settings-form') || e.target.name?.startsWith('music_'))) {
            markDirty();
        }
    });
    document.addEventListener('input', (e) => {
        if (e.target && (e.target.closest('#music-settings-form') || e.target.name?.startsWith('music_'))) {
            markDirty();
        }
    });

    // Intercept link navigation when dirty
    document.addEventListener('click', (e) => {
        if (!isDirty) return;
        const link = e.target.closest('a');
        if (link && link.href && !link.href.startsWith('javascript:') && !link.getAttribute('target')) {
            const confirmLeave = confirm('Anda memiliki perubahan yang belum disimpan. Yakin ingin meninggalkan halaman ini tanpa menyimpan?');
            if (!confirmLeave) {
                e.preventDefault();
                e.stopPropagation();
            }
        }
    }, true);

    window.addEventListener('beforeunload', (e) => {
        if (isDirty) {
            e.preventDefault();
            e.returnValue = 'Anda memiliki perubahan yang belum disimpan. Yakin ingin keluar?';
            return e.returnValue;
        }
    });

    window.triggerMusicSave = function () {
        const musicForm = document.getElementById('music-settings-form') || document.querySelector('form');
        if (musicForm) {
            saveMusicSettings();
        }
    };

    // ── Music Settings (G007) ──────────────────────────────────────────────────

    window.saveMusicSettings = function (event) {
        if (event) event.preventDefault();
        const btn = document.getElementById('sticky-save-btn') || document.getElementById('top-save-btn');
        const originalContent = btn ? btn.innerHTML : '';
        
        ['sticky-save-btn', 'top-save-btn'].forEach(id => {
            const b = document.getElementById(id);
            if (b) {
                b.disabled = true;
                b.innerHTML = '<span class="mm-spinner"></span> Saving...';
            }
        });

        const formEl = document.getElementById('music-settings-form') || (event ? event.target : document.querySelector('form'));
        const formData = new FormData(formEl);
        if (!formData.has('music_enabled')) formData.append('music_enabled', '0');
        if (!formData.has('music_autoplay')) formData.append('music_autoplay', '0');
        if (!formData.has('music_loop')) formData.append('music_loop', '0');
        if (!formData.has('music_minimized')) formData.append('music_minimized', '0');
        formData.append('_csrf', CSRF);

        fetch('<?= url('/app/couple/invitation/media/music-settings') ?>', {
            method: 'POST',
            body: formData
        })
        .then(res => {
            if (!res.ok) {
                return res.json().then(json => { throw new Error(json.message || 'Failed to save settings.'); });
            }
            return res.json();
        })
        .then(data => {
            setSavedState();
            toast('success', 'Music settings saved successfully!');
        })
        .catch(err => {
            toast('error', err.message || 'An error occurred while saving.');
            if (btn) {
                btn.disabled = false;
                btn.innerHTML = originalContent;
            }
        });
    };

    // ── Delete ────────────────────────────────────────────────────────────────

    window.mmDelete = function (slot, path, subSlot = '') {
        if (!confirm('Delete this file? This cannot be undone.')) return;

        const fd = new FormData();
        fd.append('slot', slot);
        fd.append('path', path);
        if (subSlot) fd.append('sub_slot', subSlot);
        fd.append('_csrf', CSRF);

        fetch(DEL_URL, { method: 'POST', body: fd })
            .then(r => r.json())
            .then(resp => {
                if (resp.status === 'success' && resp.data) {
                    const { count, max } = resp.data;

                    // Remove thumb from DOM
                    const containerId = subSlot ? 'previews-' + slot + '_' + subSlot : 'previews-' + slot;
                    const container = document.getElementById(containerId);
                    if (container) {
                        const thumb = container.querySelector('[data-path="' + CSS.escape(path) + '"]');
                        if (thumb) thumb.remove();
                    }

                    const dropElId = subSlot ? 'drop-' + slot + '_' + subSlot : 'drop-' + slot;
                    const dropEl = document.getElementById(dropElId);
                    
                    if (!subSlot) {
                        refreshBadge(slot, count, max);
                        if (dropEl) refreshDropZone(slot, dropEl, count, max);
                    } else {
                        // For dual slot, show dropzone, hide delete btn
                        if (dropEl) {
                            dropEl.classList.remove('hidden');
                            dropEl.classList.add('flex', 'flex-col', 'items-center', 'justify-center');
                        }
                        const delBtn = document.getElementById('delete-btn-' + slot + '_' + subSlot);
                        if (delBtn) delBtn.classList.add('hidden');
                    }

                    toast('success', resp.message || 'Deleted.');
                } else {
                    toast('error', resp.message || 'Delete failed.');
                }
            })
            .catch(() => toast('error', 'Network error — please try again.'));
    };

    // ── Drag & Drop ───────────────────────────────────────────────────────────

    window.mmDragOver = function (e, slot, subSlot = '') {
        e.preventDefault();
        const dropElId = subSlot ? 'drop-' + slot + '_' + subSlot : 'drop-' + slot;
        const el = document.getElementById(dropElId);
        if (el && !el.classList.contains('at-limit')) el.classList.add('drag-over');
    };

    window.mmDragLeave = function (e, slot, subSlot = '') {
        e.preventDefault();
        const dropElId = subSlot ? 'drop-' + slot + '_' + subSlot : 'drop-' + slot;
        const el = document.getElementById(dropElId);
        if (el) el.classList.remove('drag-over');
    };

    window.mmDrop = function (e, slot, subSlot = '') {
        e.preventDefault();
        const dropElId = subSlot ? 'drop-' + slot + '_' + subSlot : 'drop-' + slot;
        const el = document.getElementById(dropElId);
        if (el) el.classList.remove('drag-over');
        if (el && el.classList.contains('at-limit')) return;

        const files = Array.from(e.dataTransfer?.files || []);
        files.forEach(file => {
            if (slot === 'music_file' || !file.type.startsWith('image/')) {
                doUploadDirect(slot, file, subSlot);
            } else {
                cropQueue.push({ slot, file, subSlot });
            }
        });
        processCropQueue();
    };

    // ── DOM Helpers ───────────────────────────────────────────────────────────

    function makeThumb(slot, path, url, isAudio, subSlot = '') {
        const div = document.createElement('div');
        
        if (subSlot === 'desktop') {
            div.className = 'relative rounded-xl overflow-hidden border border-slate-200 shadow-sm w-full aspect-video';
        } else if (subSlot === 'mobile') {
            div.className = 'relative rounded-xl overflow-hidden border border-slate-200 shadow-sm w-[200px] lg:w-[240px] aspect-[9/16]';
        } else {
            div.className = 'mm-thumb';
        }
        
        div.setAttribute('data-path', path);
        div.setAttribute('data-slot', slot);
        if (subSlot) div.setAttribute('data-subslot', subSlot);

        if (isAudio) {
            const inner = document.createElement('div');
            inner.className = 'mm-audio-thumb';
            inner.innerHTML = '<i class="fa-solid fa-music"></i>';
            div.appendChild(inner);
        } else {
            const img = document.createElement('img');
            img.src = url;
            img.alt = slot;
            img.loading = 'lazy';
            if (subSlot) {
                img.className = 'w-full h-full object-cover';
            }
            div.appendChild(img);
        }

        if (!subSlot) {
            const btn = document.createElement('button');
            btn.className = 'mm-delete-btn';
            btn.title = 'Delete';
            btn.innerHTML = '<i class="fa-solid fa-xmark"></i>';
            btn.addEventListener('click', () => window.mmDelete(slot, path, subSlot));
            div.appendChild(btn);
        }

        return div;
    }

    function makeOverlay() {
        const d = document.createElement('div');
        d.className = 'mm-uploading-overlay';
        d.innerHTML = '<span class="mm-spinner"></span>Uploading…';
        return d;
    }

    function safeRemove(parent, child) {
        if (parent && child && parent.contains(child)) parent.removeChild(child);
    }

    function refreshBadge(slot, count, max) {
        const badge = document.getElementById('badge-' + slot);
        if (!badge) return;
        badge.textContent = count + ' / ' + max;
        badge.classList.toggle('at-limit', count >= max);
    }

    function refreshDropZone(slot, el, count, max) {
        el.classList.toggle('at-limit', count >= max);
        if (count >= max) {
            el.querySelector('.mm-drop-text') && (el.querySelector('.mm-drop-text').innerHTML =
                '<strong style="color:#dc2626;">Limit reached</strong> — delete a file to upload a new one');
            const sub = el.querySelector('.mm-drop-subtext');
            if (sub) sub.remove();
        } else {
            const txt = el.querySelector('.mm-drop-text');
            if (txt) txt.innerHTML = '<strong>Click to upload</strong> or drag &amp; drop';
            if (!el.querySelector('.mm-drop-subtext')) {
                const sub = document.createElement('div');
                sub.className = 'mm-drop-subtext';
                sub.textContent = 'JPG, PNG, WebP · Max size applies';
                el.appendChild(sub);
            }
        }
    }

    // When upload is cancelled, restore drop zone based on current badge
    function refreshFromBadge(slot, el) {
        const badge = document.getElementById('badge-' + slot);
        if (!badge) { el.classList.remove('at-limit'); return; }
        const [cur, max] = badge.textContent.split('/').map(s => parseInt(s.trim(), 10));
        refreshFromBadgeData(slot, el, cur, max);
    }

    function refreshFromBadgeData(slot, el, cur, max) {
        refreshDropZone(slot, el, cur, max);
    }

    // ── Toast ─────────────────────────────────────────────────────────────────

    function toast(type, msg) {
        const el = document.getElementById('mm-toast');
        if (!el) return;
        el.textContent = msg;
        el.className = 'mm-toast ' + type;
        requestAnimationFrame(() => {
            el.classList.add('show');
            clearTimeout(el._t);
            el._t = setTimeout(() => el.classList.remove('show'), 3400);
        });
    }

}());
</script>
