<?php
// admin/ticket_designer.php  —  Visual ticket designer with font management
require_once __DIR__ . '/../config.php';
adminGuard();
requireCsrf();  // reject POSTs without a valid CSRF token
require_once __DIR__ . '/_layout.php';

$db      = db();
$eventId = (int)($_GET['event_id'] ?? $_POST['event_id'] ?? 0);
$msg     = '';
$error   = '';

// ── Load all events ──────────────────────────────────────────
$allEvents = $db->prepare('SELECT id, title, slug, ticket_template, ticket_layout FROM events WHERE admin_id=? ORDER BY title');
$allEvents->execute([$_SESSION['admin_id']]);
$allEvents = $allEvents->fetchAll();

$event = null;
if ($eventId) {
    $s = $db->prepare('SELECT * FROM events WHERE id=? AND admin_id=?');
    $s->execute([$eventId, $_SESSION['admin_id']]);
    $event = $s->fetch();
}

// ── Handle template upload ───────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_template']) && $event) {
    $up = $_FILES['ticket_template'] ?? null;

    if (empty($up['name']) || $up['error'] === UPLOAD_ERR_NO_FILE) {
        $error = 'No file selected. Please choose a JPG or PNG image.';
    } elseif ($up['error'] !== UPLOAD_ERR_OK) {
        $phpErrors = [
            UPLOAD_ERR_INI_SIZE   => 'File exceeds server upload_max_filesize limit.',
            UPLOAD_ERR_FORM_SIZE  => 'File exceeds form MAX_FILE_SIZE limit.',
            UPLOAD_ERR_PARTIAL    => 'File was only partially uploaded.',
            UPLOAD_ERR_NO_TMP_DIR => 'Server missing temporary upload folder.',
            UPLOAD_ERR_CANT_WRITE => 'Server failed to write file to disk.',
            UPLOAD_ERR_EXTENSION  => 'Upload blocked by server extension.',
        ];
        $error = $phpErrors[$up['error']] ?? 'Upload error code: ' . $up['error'];
    } else {
        $ext = strtolower(pathinfo($up['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, ['jpg','jpeg','png'])) {
            $error = 'Only JPG or PNG files are accepted. Got: .' . $ext;
        } elseif ($up['size'] > 12 * 1024 * 1024) {
            $error = 'File too large (' . round($up['size']/1048576, 1) . ' MB). Max 12 MB.';
        } else {
            // Create upload directory — try BASE_PATH first, then DOCUMENT_ROOT
            $dir = null;
            foreach ([
                BASE_PATH . '/uploads/templates',
                ($_SERVER['DOCUMENT_ROOT'] ?? '') . '/uploads/templates',
            ] as $candidate) {
                $candidate = preg_replace('#/{2,}#', '/', $candidate);
                if (is_dir($candidate) || @mkdir($candidate, 0755, true)) {
                    if (is_writable($candidate)) { $dir = $candidate; break; }
                }
            }

            if (!$dir) {
                $error = 'Cannot create or write to uploads/templates directory. Check folder permissions.';
            } else {
                $fname = 'ticket_' . preg_replace('/[^a-z0-9\-]/', '', $event['slug']) . '_' . time() . '.' . $ext;
                $dest  = $dir . '/' . $fname;

                if (!move_uploaded_file($up['tmp_name'], $dest)) {
                    $error = 'File move failed. Destination: ' . $dest . ' — check permissions.';
                } else {
                    // Store relative path from the same base used in resolveUploadPath
                    $storedPath = 'uploads/templates/' . $fname;
                    $db->prepare('UPDATE events SET ticket_template=? WHERE id=?')
                       ->execute([$storedPath, $eventId]);
                    $s->execute([$eventId, $_SESSION['admin_id']]);
                    $event = $s->fetch();
                    $msg = 'Template uploaded successfully (' . round($up['size']/1024) . ' KB). Position your elements below.';
                }
            }
        }
    }
}

// ── Handle layout save (AJAX JSON) ──────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_LAYOUT_SAVE'])) {
    $body = json_decode(file_get_contents('php://input'), true);
    if ($body && $eventId) {
        $db->prepare('UPDATE events SET ticket_layout=? WHERE id=? AND admin_id=?')
           ->execute([json_encode($body), $eventId, $_SESSION['admin_id']]);
        jsonOut(['ok' => true]);
    }
    jsonOut(['ok' => false], 400);
}

// ── Handle template delete ───────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_template']) && $event) {
    $db->prepare('UPDATE events SET ticket_template=NULL, ticket_layout=NULL WHERE id=?')
       ->execute([$eventId]);
    $s->execute([$eventId, $_SESSION['admin_id']]);
    $event = $s->fetch();
    $msg = 'Template removed.';
}

// ── Handle bulk regenerate ───────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['regen_invites']) && $event) {
    require_once BASE_PATH . '/lib/qr_invite.php';
    $gstmt = $db->prepare('SELECT * FROM guests WHERE event_id=?');
    $gstmt->execute([$eventId]);
    $guestList = $gstmt->fetchAll();
    foreach ($guestList as $g) {
        $f = INVITE_PATH . '/' . $event['slug'] . '/' . $g['token'] . '.png';
        if (file_exists($f)) unlink($f);
    }
    $count = 0;
    foreach ($guestList as $g) {
        $path = buildInvite($g, $event);
        $db->prepare('UPDATE guests SET invite_path=? WHERE id=?')
           ->execute([str_replace(BASE_PATH . '/', '', $path), $g['id']]);
        $count++;
    }
    $msg = "Regenerated {$count} ticket(s) with the new layout.";
}

// ── Layout + template data ───────────────────────────────────
$layoutJson  = $event ? ($event['ticket_layout'] ?? 'null') : 'null';
$templateUrl = ($event && $event['ticket_template'])
    ? BASE_URL . '/' . $event['ticket_template'] : '';

$sampleName = 'Guest Name';
if ($event) {
    $sg = $db->prepare('SELECT name FROM guests WHERE event_id=? LIMIT 1');
    $sg->execute([$eventId]);
    $sn = $sg->fetchColumn();
    if ($sn) $sampleName = $sn;
}

layout_start('Ticket Designer', 'ticket');
event_tabs((int)$eventId, 'tickets', $event['title'] ?? '');
?>

<style>
/* ── Layout ─────────────────────────────────────────────── */
#designer-wrap { display:flex; gap:24px; align-items:flex-start; }
#canvas-panel  { flex:1; min-width:0; }
#controls-panel{ width:300px; flex-shrink:0; }

/* Stack canvas over controls on tablets/phones so nothing is squeezed */
@media (max-width: 820px) {
  #designer-wrap  { flex-direction:column; gap:18px; }
  #controls-panel { width:100%; }
}

/* ── Stage ──────────────────────────────────────────────── */
#ticket-stage {
  position:relative; display:inline-block;
  border:1px solid var(--border); border-radius:4px;
  overflow:hidden; max-width:100%; cursor:default; user-select:none;
}
#ticket-bg { display:block; max-width:100%; height:auto; pointer-events:none; }

/* ── Drag elements ──────────────────────────────────────── */
.drag-element {
  position:absolute; cursor:move; box-sizing:border-box;
  border:2px dashed rgba(197,158,89,.7); border-radius:3px;
  display:flex; align-items:center; justify-content:center;
  transition:border-color .15s, box-shadow .15s;
}
.drag-element:hover,.drag-element.active {
  border-color:var(--gold); box-shadow:0 0 0 2px rgba(197,158,89,.25);
}
.drag-element .elem-label {
  position:absolute; top:-20px; left:0;
  font-family:var(--font-ui); font-size:.6rem; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase; color:var(--gold);
  white-space:nowrap; pointer-events:none;
  background:rgba(12,12,18,.85); padding:2px 6px; border-radius:3px;
}
.drag-element .resize-handle {
  position:absolute; bottom:-5px; right:-5px;
  width:12px; height:12px; background:var(--gold);
  border-radius:2px; cursor:se-resize; z-index:10;
}
#el-qr { background:rgba(197,158,89,.07); }
#el-qr-inner {
  width:75%; height:75%;
  background:rgba(197,158,89,.15); border:1px solid rgba(197,158,89,.3);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-ui); font-size:.6rem; font-weight:700;
  letter-spacing:.06em; text-transform:uppercase; color:rgba(197,158,89,.7);
  pointer-events:none;
}
#el-name { background:rgba(92,159,224,.07); border-color:rgba(92,159,224,.6); }
#el-name:hover,#el-name.active { border-color:var(--info); box-shadow:0 0 0 2px rgba(92,159,224,.2); }
#el-name .elem-label { color:var(--info); }
#el-name .resize-handle { background:var(--info); }
#name-preview {
  pointer-events:none; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; padding:0 4px; width:100%;
}

/* ── Controls ───────────────────────────────────────────── */
.prop-row {
  display:flex; align-items:center; justify-content:space-between;
  margin-bottom:13px; gap:8px;
}
.prop-row label {
  font-family:var(--font-ui); font-size:.67rem; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase; color:var(--muted);
  flex-shrink:0;
}
.prop-row input[type=number],.prop-row select {
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:var(--radius); color:var(--white);
  padding:5px 8px; font-family:var(--font-body); font-size:.82rem;
  width:110px; outline:none; flex-shrink:0;
}
.prop-row input[type=number]:focus,.prop-row select:focus {
  border-color:var(--gold-dim);
}
.prop-row input[type=color] {
  width:36px; height:28px; border:1px solid var(--border);
  border-radius:var(--radius); cursor:pointer; background:none; padding:2px;
}
.section-title {
  font-family:var(--font-ui); font-size:.64rem; font-weight:700;
  letter-spacing:.12em; text-transform:uppercase; color:var(--gold);
  margin:16px 0 10px; padding-bottom:6px; border-bottom:1px solid var(--border);
}
#save-status {
  font-family:var(--font-ui); font-size:.68rem; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase;
  padding:5px 12px; border-radius:var(--radius); opacity:0; transition:opacity .3s;
}
#save-status.show { opacity:1; }
#save-status.ok  { color:var(--success); background:rgba(62,207,142,.1); border:1px solid rgba(62,207,142,.3); }
#save-status.err { color:var(--danger);  background:rgba(224,92,92,.1);  border:1px solid rgba(224,92,92,.3);  }

/* ── Font selector ──────────────────────────────────────── */
#font-select {
  width:100%; background:var(--surface-2); border:1px solid var(--border);
  border-radius:var(--radius); color:var(--white);
  padding:6px 10px; font-family:var(--font-body); font-size:.85rem;
  outline:none; cursor:pointer;
}
#font-select:focus { border-color:var(--gold-dim); }
.font-preview-text {
  font-size:.82rem; color:var(--muted); margin-top:5px;
  padding:6px 8px; background:var(--surface-2);
  border:1px solid var(--border); border-radius:var(--radius);
  min-height:32px; white-space:nowrap; overflow:hidden;
  text-overflow:ellipsis; line-height:1.4;
}

/* ── Font Manager panel ─────────────────────────────────── */
#font-manager {
  position:fixed; top:0; right:0; bottom:0; width:min(400px, 92vw);
  background:var(--deep); border-left:1px solid var(--border);
  z-index:500; transform:translateX(100%);
  transition:transform .3s cubic-bezier(.4,0,.2,1);
  display:flex; flex-direction:column; box-shadow:-8px 0 32px rgba(0,0,0,.5);
}
#font-manager.open { transform:translateX(0); }
.fm-header {
  display:flex; align-items:center; justify-content:space-between;
  padding:22px 24px 16px; border-bottom:1px solid var(--border);
}
.fm-header h3 { font-family:var(--font-display); font-size:1.3rem; }
.fm-body { flex:1; overflow-y:auto; padding:20px 24px; }
.fm-footer { padding:16px 24px; border-top:1px solid var(--border); }

.font-item {
  display:flex; align-items:center; gap:12px;
  padding:12px 14px; background:var(--surface);
  border:1px solid var(--border); border-radius:var(--radius);
  margin-bottom:8px; transition:border-color .15s;
  cursor:pointer;
}
.font-item:hover { border-color:var(--gold-dim); }
.font-item.selected { border-color:var(--gold); background:rgba(197,158,89,.06); }
.font-item-name {
  flex:1; min-width:0;
  font-family:var(--font-ui); font-size:.78rem; font-weight:700;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.font-item-preview {
  font-size:1rem; color:var(--white); flex-shrink:0;
  max-width:100px; overflow:hidden; white-space:nowrap;
}
.font-item-size { font-size:.65rem; color:var(--muted); }

.drop-zone {
  border:2px dashed var(--border); border-radius:var(--radius-lg);
  padding:28px 20px; text-align:center; cursor:pointer;
  transition:border-color .2s, background .2s;
  margin-bottom:16px;
}
.drop-zone:hover,.drop-zone.dragover {
  border-color:var(--gold); background:rgba(197,158,89,.05);
}
.drop-zone svg { opacity:.4; margin-bottom:8px; }
.drop-zone p { font-size:.82rem; color:var(--muted); line-height:1.5; margin:0; }

.upload-progress {
  background:var(--surface-2); border-radius:var(--radius);
  overflow:hidden; height:4px; margin-top:8px; display:none;
}
.upload-progress-bar { height:100%; background:var(--gold); width:0; transition:width .3s; }

/* ── Overlay ────────────────────────────────────────────── */
#fm-overlay {
  position:fixed; inset:0; background:rgba(0,0,0,.4);
  z-index:499; display:none; backdrop-filter:blur(2px);
}
#fm-overlay.open { display:block; }

/* ── No template ────────────────────────────────────────── */
.no-template {
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:2px dashed var(--border);
  border-radius:var(--radius-lg); height:300px;
  flex-direction:column; gap:12px; color:var(--muted);
}

/* ── Preview modal ──────────────────────────────────────── */
#preview-modal {
  display:none; position:fixed; inset:0; z-index:600;
  background:rgba(0,0,0,.75); align-items:center; justify-content:center;
}
#preview-modal.open { display:flex; }
#preview-modal img { max-width:90vw; max-height:90vh; border-radius:8px; box-shadow:0 20px 60px rgba(0,0,0,.8); }
#preview-modal .close-btn {
  position:absolute; top:24px; right:24px; background:var(--surface);
  border:1px solid var(--border); color:var(--white);
  width:40px; height:40px; border-radius:50%; font-size:1.2rem;
  cursor:pointer; display:flex; align-items:center; justify-content:center;
}
</style>

<!-- Font Manager slide-out -->
<div id="fm-overlay" onclick="closeFontManager()"></div>
<div id="font-manager">
  <div class="fm-header">
    <h3>Font Library</h3>
    <button onclick="closeFontManager()" style="background:none;border:none;color:var(--muted);cursor:pointer;font-size:1.2rem;line-height:1">✕</button>
  </div>
  <div class="fm-body">
    <div class="drop-zone" id="drop-zone" onclick="document.getElementById('font-upload-input').click()">
      <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
        <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>
        <polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>
      </svg>
      <p><strong style="color:var(--white)">Click to upload fonts</strong><br>or drag &amp; drop .ttf / .otf files here<br><span style="font-size:.72rem">Multiple files allowed · Max 20 MB each</span></p>
    </div>
    <div class="upload-progress" id="upload-progress">
      <div class="upload-progress-bar" id="upload-progress-bar"></div>
    </div>
    <input type="file" id="font-upload-input" accept=".ttf,.otf" multiple style="display:none">

    <div class="section-title" style="margin-top:8px">Installed Fonts</div>
    <div id="font-list">
      <div class="text-muted text-sm">Loading…</div>
    </div>
  </div>
  <div class="fm-footer">
    <div class="text-muted text-sm" style="line-height:1.6">
      Upload any <strong>.ttf</strong> or <strong>.otf</strong> font file.
      Great sources: <a href="https://fonts.google.com" target="_blank" style="color:var(--gold)">Google Fonts</a>,
      <a href="https://www.dafont.com" target="_blank" style="color:var(--gold)">DaFont</a>.
      Download the <strong>TTF</strong> version and upload here.
    </div>
  </div>
</div>

<!-- Page header -->
<div class="page-header">
  <div>
    <div class="page-header-sub">Ticket Management</div>
    <h1>Ticket Designer</h1>
  </div>
  <div class="flex gap-2">
    <span id="save-status"></span>
    <button class="btn btn-outline" onclick="openFontManager()" data-ui>
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <path d="M9 7H6a2 2 0 00-2 2v9a2 2 0 002 2h9a2 2 0 002-2v-3"/>
        <path d="M9 15h3l8.5-8.5a1.5 1.5 0 00-3-3L9 12v3z"/>
      </svg>
      Manage Fonts
    </button>
    <?php if ($event && $event['ticket_template']): ?>
      <button id="btn-preview" class="btn btn-outline" onclick="previewSample()">Preview Ticket</button>
    <?php endif; ?>
    <?php if ($event): ?>
      <a href="<?= BASE_URL ?>/admin/tickets_download.php?event_id=<?= $eventId ?>"
         class="btn btn-outline"
         title="Download generated tickets as ZIP files">
        ⬇ Download Tickets
      </a>
      <form method="POST" style="display:inline">
        <input type="hidden" name="event_id" value="<?= $eventId ?>">
        <button class="btn btn-gold" name="regen_invites" type="submit"
                data-loader-text="Generating tickets…"
                onclick="return confirm('Generate tickets for every guest in this event? Existing tickets are rebuilt with the current layout.')">
          ⚡ Generate All Tickets
        </button>
      </form>
    <?php endif; ?>
  </div>
</div>

<?php if ($msg): ?><div class="alert alert-success"><?= htmlspecialchars($msg) ?></div><?php endif; ?>
<?php if ($error): ?><div class="alert alert-danger"><?= htmlspecialchars($error) ?></div><?php endif; ?>

<?php if (!$event): ?>
<!-- Event selector (only when no event is in context) -->
<div class="card card-sm mb-4">
  <form method="GET" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
    <label class="label-ui" style="white-space:nowrap">Event</label>
    <select class="form-control" name="event_id" style="flex:1;max-width:360px">
      <option value="">— Choose an event —</option>
      <?php foreach ($allEvents as $ev): ?>
        <option value="<?= $ev['id'] ?>" <?= $eventId===$ev['id']?'selected':'' ?>>
          <?= htmlspecialchars($ev['title']) ?><?= $ev['ticket_template'] ? ' ✓' : '' ?>
        </option>
      <?php endforeach; ?>
    </select>
    <button class="btn btn-outline" type="submit">Load</button>
  </form>
</div>
<div class="alert alert-info">Open an event from the Events page to design its ticket.</div>
<?php else: ?>

<div id="designer-wrap">

  <!-- ── Canvas panel ─────────────────────────────────────── -->
  <div id="canvas-panel">

    <?php if (!$event['ticket_template']): ?>
    <div class="no-template">
      <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="opacity:.4">
        <rect x="3" y="3" width="18" height="18" rx="2"/>
        <path d="M3 9l4-4 4 4 4-4 4 4M3 15l4 4 4-4 4 4 4-4"/>
      </svg>
      <div style="font-family:var(--font-display);font-size:1.2rem;color:var(--white)">Upload Your Ticket Design</div>
      <div style="font-size:.85rem;max-width:300px;text-align:center;line-height:1.6;color:var(--muted)">
        Upload a JPG or PNG as your ticket background. Then drag the Name and QR blocks to their positions.
      </div>
      <form method="POST" enctype="multipart/form-data" style="display:flex;gap:10px;margin-top:8px">
        <input type="hidden" name="event_id" value="<?= $eventId ?>">
        <input type="file" name="ticket_template" accept=".jpg,.jpeg,.png"
               class="form-control" style="max-width:240px" required>
        <button class="btn btn-gold" name="upload_template" type="submit">Upload</button>
      </form>
    </div>

    <?php else: ?>

    <div style="margin-bottom:10px;display:flex;gap:10px;align-items:center;flex-wrap:wrap">
      <div class="badge badge-success">Template loaded</div>
      <span class="text-muted text-sm" id="template-dims">Loading…</span>
      <span style="margin-left:auto;display:flex;gap:8px">
        <form method="POST" enctype="multipart/form-data" style="display:inline" id="replace-form">
          <input type="hidden" name="event_id" value="<?= $eventId ?>">
          <input type="hidden" name="upload_template" value="1">
          <input type="file" name="ticket_template" accept=".jpg,.jpeg,.png" id="replaceInput"
                 style="display:none" onchange="document.getElementById('replace-form').submit()">
          <button type="button" class="btn btn-outline btn-sm"
                  onclick="document.getElementById('replaceInput').click()">Replace Image</button>
        </form>
        <form method="POST" onsubmit="return confirm('Remove this template?')" style="display:inline">
          <input type="hidden" name="event_id" value="<?= $eventId ?>">
          <button class="btn btn-danger btn-sm" name="delete_template" type="submit">Remove</button>
        </form>
      </span>
    </div>

    <div id="ticket-stage">
      <img id="ticket-bg" src="<?= htmlspecialchars($templateUrl) ?>"
           alt="Ticket template" onload="onImageLoad()">

      <div id="el-qr" class="drag-element" style="width:120px;height:120px;top:20px;left:20px">
        <span class="elem-label">QR Code</span>
        <div id="el-qr-inner">QR CODE</div>
        <div class="resize-handle" id="qr-resize"></div>
      </div>

      <div id="el-name" class="drag-element" style="width:200px;height:36px;top:160px;left:20px">
        <span class="elem-label">Guest Name</span>
        <div id="name-preview" style="font-size:18px;color:#ffffff;text-align:center;font-weight:bold">
          <?= htmlspecialchars($sampleName) ?>
        </div>
        <div class="resize-handle" id="name-resize"></div>
      </div>
    </div>

    <div class="text-muted text-sm mt-2" style="display:flex;gap:20px;flex-wrap:wrap">
      <span>🖱 Drag to position</span>
      <span>↘ Corner handle to resize</span>
      <span>🔤 Font selector in panel →</span>
    </div>

    <?php endif; ?>
  </div>

  <!-- ── Properties panel ─────────────────────────────────── -->
  <?php if ($event['ticket_template']): ?>
  <div id="controls-panel">
    <div class="card" style="padding:20px 22px">

      <!-- QR Code -->
      <div class="section-title">
        <span style="display:inline-block;width:10px;height:10px;background:var(--gold);border-radius:2px;margin-right:6px"></span>
        QR Code Block
      </div>
      <div class="prop-row"><label>X</label><input type="number" id="qr-x" min="0" oninput="applyQRProps()" step="1"> px</div>
      <div class="prop-row"><label>Y</label><input type="number" id="qr-y" min="0" oninput="applyQRProps()" step="1"> px</div>
      <div class="prop-row"><label>Size</label><input type="number" id="qr-size" min="40" max="600" oninput="applyQRProps()" step="1"> px</div>
      <div class="prop-row">
        <label>Padding</label>
        <select id="qr-padding" onchange="applyQRProps()">
          <option value="0">None</option>
          <option value="6" selected>Small</option>
          <option value="12">Medium</option>
          <option value="20">Large</option>
        </select>
      </div>

      <!-- Name Text -->
      <div class="section-title" style="margin-top:20px">
        <span style="display:inline-block;width:10px;height:10px;background:var(--info);border-radius:2px;margin-right:6px"></span>
        Name Text Block
      </div>
      <div class="prop-row"><label>X</label><input type="number" id="name-x" min="0" oninput="applyNameProps()" step="1"> px</div>
      <div class="prop-row"><label>Y</label><input type="number" id="name-y" min="0" oninput="applyNameProps()" step="1"> px</div>
      <div class="prop-row"><label>Max Width</label><input type="number" id="name-w" min="80" oninput="applyNameProps()" step="1"> px</div>
      <div class="prop-row"><label>Font Size</label><input type="number" id="name-fontsize" value="32" min="8" max="300" oninput="applyNameProps()" step="1"> px</div>
      <div class="prop-row"><label>Color</label><input type="color" id="name-color" value="#ffffff" oninput="applyNameProps()"></div>
      <div class="prop-row">
        <label>Align</label>
        <select id="name-align" onchange="applyNameProps()">
          <option value="left">Left</option>
          <option value="center" selected>Center</option>
          <option value="right">Right</option>
        </select>
      </div>
      <div class="prop-row">
        <label>Weight</label>
        <select id="name-style" onchange="applyNameProps()">
          <option value="bold" selected>Bold</option>
          <option value="normal">Normal</option>
        </select>
      </div>

      <!-- Font selector -->
      <div class="section-title" style="margin-top:18px">
        <span style="display:inline-block;width:10px;height:10px;background:var(--info);border-radius:2px;margin-right:6px"></span>
        Font Family
      </div>
      <select id="font-select" onchange="onFontChange()">
        <option value="">System Default</option>
      </select>
      <div class="font-preview-text" id="font-preview-text">Aa Bb Cc — 0123</div>

      <button class="btn btn-outline btn-sm w-100 mt-2" onclick="openFontManager()" style="font-size:.7rem">
        + Upload or Manage Fonts
      </button>

      <hr class="divider" style="margin:18px 0">

      <button id="btn-save" class="btn btn-gold w-100" onclick="saveLayout()">💾 Save Layout</button>
      <div style="margin-top:10px;text-align:center">
        <span id="save-status"></span>
      </div>

      <div class="alert alert-info mt-3" style="font-size:.72rem;line-height:1.6">
        Positions are in <em>template pixels</em> — accurate regardless of screen size.
      </div>

    </div>
  </div>
  <?php endif; ?>

</div><!-- /designer-wrap -->
<?php endif; ?>

<!-- Preview modal -->
<div id="preview-modal">
  <button class="close-btn" onclick="closePreview()">✕</button>
  <div id="preview-loading" style="display:flex;flex-direction:column;align-items:center;gap:16px">
    <div style="width:48px;height:48px;border:3px solid rgba(197,158,89,.2);
                border-top-color:var(--gold);border-radius:50%;
                animation:spin .8s linear infinite"></div>
    <div style="color:var(--muted);font-family:var(--font-ui);font-size:.72rem;
                letter-spacing:.1em;text-transform:uppercase">Generating ticket…</div>
  </div>
  <img id="preview-img" src="" alt="" style="display:none;max-width:90vw;max-height:90vh;
       border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,.8)">
</div>
<style>
@keyframes spin { to { transform:rotate(360deg); } }
</style>
<script>
const previewLoadingHTML = document.getElementById('preview-loading')?.innerHTML ?? '';
</script>

<script>
const EVENT_ID   = <?= (int)$eventId ?>;
const BASE_URL   = '<?= BASE_URL ?>';
const SAMPLE_NAME= <?= json_encode($sampleName) ?>;
let savedLayout  = <?= $layoutJson ?>;

let scale = 1, templateW = 0, templateH = 0;

// All loaded fonts: { file, name, url, cssFace }
let installedFonts = [];
let activeFontFile = '';

// ── Image load ───────────────────────────────────────────────
function onImageLoad() {
  const img = document.getElementById('ticket-bg');
  templateW = img.naturalWidth;
  templateH = img.naturalHeight;
  scale     = img.clientWidth / templateW;
  document.getElementById('template-dims').textContent = `${templateW} × ${templateH} px`;

  loadFonts().then(() => {
    if (savedLayout) {
      restoreLayout(savedLayout);
    } else {
      setQR(templateW - 180, templateH - 180, 160);
      setName(60, Math.round(templateH * 0.55), Math.round(templateW * 0.5), 36, '#ffffff', 'center', 'bold', '');
    }
    updatePanel();
    setupDrag();
  });
}

// ── Scale helpers ─────────────────────────────────────────────
const t2d = v => Math.round(v * scale);
const d2t = v => Math.round(v / scale);

// ── Set QR position ───────────────────────────────────────────
function setQR(tx, ty, tsize) {
  const el = document.getElementById('el-qr');
  el.style.left   = t2d(tx) + 'px';
  el.style.top    = t2d(ty) + 'px';
  el.style.width  = t2d(tsize) + 'px';
  el.style.height = t2d(tsize) + 'px';
}

// ── Set name position + styling ───────────────────────────────
function setName(tx, ty, tw, tfontSize, color, align, weight, fontFile) {
  const el  = document.getElementById('el-name');
  const prv = document.getElementById('name-preview');
  if (!el || !prv) return;
  el.style.left   = t2d(tx) + 'px';
  el.style.top    = t2d(ty) + 'px';
  el.style.width  = t2d(tw) + 'px';
  el.style.height = t2d(tfontSize * 1.5) + 'px';
  prv.style.fontSize   = t2d(tfontSize) + 'px';
  prv.style.color      = color;
  prv.style.textAlign  = align;
  prv.style.fontWeight = weight;
  // Apply CSS font family
  if (fontFile) {
    const face = getFontFaceFamily(fontFile);
    prv.style.fontFamily = face ? `'${face}', sans-serif` : '';
  } else {
    prv.style.fontFamily = '';
  }
}

// ── Update property panel from current DOM state ──────────────
function updatePanel() {
  const qr  = document.getElementById('el-qr');
  const name= document.getElementById('el-name');
  const prv = document.getElementById('name-preview');
  if (!qr || !name || !prv) return;

  document.getElementById('qr-x').value    = d2t(parseInt(qr.style.left));
  document.getElementById('qr-y').value    = d2t(parseInt(qr.style.top));
  document.getElementById('qr-size').value = d2t(parseInt(qr.style.width));

  document.getElementById('name-x').value       = d2t(parseInt(name.style.left));
  document.getElementById('name-y').value       = d2t(parseInt(name.style.top));
  document.getElementById('name-w').value       = d2t(parseInt(name.style.width));
  document.getElementById('name-fontsize').value = d2t(parseInt(prv.style.fontSize) || 18);
  document.getElementById('name-color').value   = rgbToHex(prv.style.color) || '#ffffff';
  document.getElementById('name-align').value   = prv.style.textAlign || 'center';
  document.getElementById('name-style').value   = prv.style.fontWeight === 'bold' ? 'bold' : 'normal';
}

function applyQRProps() {
  setQR(
    parseInt(document.getElementById('qr-x').value) || 0,
    parseInt(document.getElementById('qr-y').value) || 0,
    parseInt(document.getElementById('qr-size').value) || 120
  );
}

function applyNameProps() {
  setName(
    parseInt(document.getElementById('name-x').value)       || 0,
    parseInt(document.getElementById('name-y').value)       || 0,
    parseInt(document.getElementById('name-w').value)       || 200,
    parseInt(document.getElementById('name-fontsize').value)|| 32,
    document.getElementById('name-color').value,
    document.getElementById('name-align').value,
    document.getElementById('name-style').value,
    activeFontFile
  );
}

// ── Restore saved layout ──────────────────────────────────────
function restoreLayout(layout) {
  if (!layout) return;
  if (layout.qr) {
    setQR(layout.qr.x, layout.qr.y, layout.qr.size);
    const pad = document.getElementById('qr-padding');
    if (pad) pad.value = layout.qr.padding ?? 6;
  }
  if (layout.name) {
    const n = layout.name;
    activeFontFile = n.fontFile || '';
    setName(n.x, n.y, n.maxWidth, n.fontSize, n.color||'#ffffff',
            n.align||'center', n.fontStyle||'bold', activeFontFile);
    // Update font dropdown
    const sel = document.getElementById('font-select');
    if (sel) sel.value = activeFontFile;
    updateFontPreview(activeFontFile);
  }
  updatePanel();
}

// ── Build layout JSON ─────────────────────────────────────────
function buildLayout() {
  const qr  = document.getElementById('el-qr');
  const prv = document.getElementById('name-preview');
  const name= document.getElementById('el-name');
  return {
    templateW, templateH,
    qr: {
      x:       d2t(parseInt(qr.style.left)),
      y:       d2t(parseInt(qr.style.top)),
      size:    d2t(parseInt(qr.style.width)),
      padding: parseInt(document.getElementById('qr-padding')?.value) || 6,
    },
    name: {
      x:         d2t(parseInt(name.style.left)),
      y:         d2t(parseInt(name.style.top)),
      maxWidth:  d2t(parseInt(name.style.width)),
      fontSize:  d2t(parseInt(prv.style.fontSize) || 18),
      color:     rgbToHex(prv.style.color) || '#ffffff',
      align:     prv.style.textAlign || 'center',
      fontStyle: prv.style.fontWeight === 'bold' ? 'bold' : 'normal',
      fontFile:  activeFontFile || '',
    }
  };
}

// ── Save ──────────────────────────────────────────────────────
function saveLayout() {
  const btn = document.getElementById('btn-save');
  if (btn) btnLoading(btn);
  saveLayoutAsync().then(ok => {
    if (btn) btnReset(btn);
    if (ok)  showToast('Layout saved.', 'success', 2000);
    else     showToast('Save failed. Check your connection.', 'error');
  });
}

function showStatus(type, text) {
  const s = document.getElementById('save-status');
  if (!s) return;
  s.className = 'show ' + type;
  s.textContent = text;
  setTimeout(() => s.className = '', 3000);
}

// ── Preview ───────────────────────────────────────────────────
async function previewSample() {
  const btn = document.getElementById('btn-preview');
  if (btn) btnLoading(btn);

  const saved = await saveLayoutAsync();
  if (!saved) {
    if (btn) btnReset(btn);
    showToast('Could not save layout. Check your connection.', 'error');
    return;
  }

  const modal   = document.getElementById('preview-modal');
  const img     = document.getElementById('preview-img');
  const loading = document.getElementById('preview-loading');
  img.style.display     = 'none';
  if (loading) { loading.style.display = 'flex'; loading.innerHTML = previewLoadingHTML; }
  modal.classList.add('open');

  const tok = '<?= substr(md5('preview' . $eventId . date('YmdH')), 0, 12) ?>';
  const url = BASE_URL + '/api/preview_ticket.php?event_id=' + EVENT_ID
            + '&tok=' + tok + '&t=' + Date.now();

  const newImg  = new Image();
  newImg.onload = () => {
    img.src               = newImg.src;
    img.style.display     = 'block';
    if (loading) loading.style.display = 'none';
    if (btn) btnReset(btn);
  };
  newImg.onerror = () => {
    fetch(url).then(r => r.text()).then(txt => {
      if (loading) loading.innerHTML = `
        <div style="color:var(--danger);font-size:.82rem;max-width:480px;
                    padding:20px;text-align:left;line-height:1.7">
          <strong style="display:block;margin-bottom:8px;font-size:.9rem">Preview failed</strong>
          <code style="font-size:.72rem;word-break:break-all;background:rgba(255,255,255,.06);
                       padding:8px;border-radius:4px;display:block">
            ${esc(txt.substring(0, 500))}
          </code>
        </div>`;
    });
    if (btn) btnReset(btn);
  };
  newImg.src = url;
}

// Save and return Promise<boolean>
function saveLayoutAsync() {
  return new Promise(resolve => {
    const layout = buildLayout();
    fetch(window.location.href, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json', 'X-Layout-Save': '1' },
      body:    JSON.stringify(layout)
    })
    .then(r => r.json())
    .then(d => {
      if (d.ok) { savedLayout = layout; showStatus('ok', '✓ Saved'); }
      resolve(!!d.ok);
    })
    .catch(() => resolve(false));
  });
}

function closePreview() {
  document.getElementById('preview-modal').classList.remove('open');
  document.getElementById('preview-img').style.display = 'none';
  document.getElementById('preview-img').src = '';
  const loading = document.getElementById('preview-loading');
  if (loading) { loading.style.display = 'flex'; loading.innerHTML = previewLoadingHTML; }
}
document.getElementById('preview-modal')?.addEventListener('click', e => {
  if (e.target === document.getElementById('preview-modal')) closePreview();
});

// ── Drag & Resize ─────────────────────────────────────────────
function setupDrag() {
  makeDraggable('el-qr',   'qr-resize',   updatePanel, true);
  makeDraggable('el-name', 'name-resize', updatePanel, false);
}

function makeDraggable(elId, resizeId, onChange, isSquare) {
  const el     = document.getElementById(elId);
  const rHandle= document.getElementById(resizeId);
  const stage  = document.getElementById('ticket-stage');
  if (!el || !stage) return;

  let dragging=false, resizing=false;
  let startX,startY,startL,startT,startW,startH;

  el.addEventListener('mousedown', e => {
    if (e.target===rHandle||e.target.closest?.('.resize-handle')) return;
    dragging=true; startX=e.clientX; startY=e.clientY;
    startL=parseInt(el.style.left); startT=parseInt(el.style.top);
    document.querySelectorAll('.drag-element').forEach(x=>x.classList.remove('active'));
    el.classList.add('active'); e.preventDefault();
  });

  rHandle?.addEventListener('mousedown', e => {
    resizing=true; startX=e.clientX; startY=e.clientY;
    startW=parseInt(el.style.width); startH=parseInt(el.style.height);
    e.stopPropagation(); e.preventDefault();
  });

  document.addEventListener('mousemove', e => {
    const sr = stage.getBoundingClientRect();
    if (dragging) {
      const nl = Math.max(0, Math.min(sr.width  - parseInt(el.style.width),  startL + e.clientX - startX));
      const nt = Math.max(0, Math.min(sr.height - parseInt(el.style.height), startT + e.clientY - startY));
      el.style.left = nl+'px'; el.style.top = nt+'px'; onChange();
    }
    if (resizing) {
      const nw = Math.max(40, startW + e.clientX - startX);
      const nh = isSquare ? nw : Math.max(20, startH + e.clientY - startY);
      el.style.width = nw+'px'; el.style.height = nh+'px';
      if (elId==='el-name') {
        const prv = document.getElementById('name-preview');
        if (prv) prv.style.fontSize = Math.round(nh/1.5)+'px';
      }
      onChange();
    }
  });
  document.addEventListener('mouseup', ()=>{ dragging=false; resizing=false; });

  // Touch
  el.addEventListener('touchstart', e => {
    if (e.target===rHandle) return;
    const t=e.touches[0]; dragging=true;
    startX=t.clientX; startY=t.clientY;
    startL=parseInt(el.style.left); startT=parseInt(el.style.top);
    e.preventDefault();
  }, {passive:false});
  document.addEventListener('touchmove', e => {
    if (!dragging) return;
    const t=e.touches[0]; const sr=stage.getBoundingClientRect();
    el.style.left = Math.max(0,Math.min(sr.width-parseInt(el.style.width),  startL+t.clientX-startX))+'px';
    el.style.top  = Math.max(0,Math.min(sr.height-parseInt(el.style.height),startT+t.clientY-startY))+'px';
    onChange(); e.preventDefault();
  }, {passive:false});
  document.addEventListener('touchend', ()=>{ dragging=false; });
}

window.addEventListener('resize', () => {
  const img = document.getElementById('ticket-bg');
  if (!img||!templateW) return;
  const layout = buildLayout();
  scale = img.clientWidth / templateW;
  restoreLayout(layout); updatePanel();
});

// ────────────────────────────────────────────────────────────
// ── FONT SYSTEM ─────────────────────────────────────────────
// ────────────────────────────────────────────────────────────

async function loadFonts() {
  try {
    const r = await fetch(BASE_URL + '/api/fonts.php?action=list');
    const d = await r.json();
    installedFonts = d.fonts || [];
    injectFontFaces(installedFonts);
    populateFontDropdown(installedFonts);
    if (document.getElementById('font-manager').classList.contains('open')) {
      renderFontList(installedFonts);
    }
  } catch(e) { console.warn('Font load failed:', e); }
}

function injectFontFaces(fonts) {
  // Remove old injected faces
  document.getElementById('injected-fonts')?.remove();
  if (!fonts.length) return;
  const style = document.createElement('style');
  style.id = 'injected-fonts';
  style.textContent = fonts.map(f => `
    @font-face {
      font-family: '${cssEsc(f.name)}';
      src: url('${f.url}') format('${f.ext==='otf'?'opentype':'truetype'}');
      font-display: swap;
    }`).join('\n');
  document.head.appendChild(style);
}

function populateFontDropdown(fonts) {
  const sel = document.getElementById('font-select');
  if (!sel) return;
  // Keep current value
  const cur = sel.value;
  sel.innerHTML = '<option value="">System Default</option>';
  fonts.forEach(f => {
    const opt = document.createElement('option');
    opt.value = f.file;
    opt.textContent = f.name;
    opt.style.fontFamily = `'${f.name}', sans-serif`;
    sel.appendChild(opt);
  });
  sel.value = cur || activeFontFile || '';
  updateFontPreview(sel.value);
}

function getFontFaceFamily(file) {
  const f = installedFonts.find(x => x.file === file);
  return f ? f.name : null;
}

function onFontChange() {
  activeFontFile = document.getElementById('font-select').value;
  updateFontPreview(activeFontFile);
  applyNameProps();
}

function updateFontPreview(fontFile) {
  const el = document.getElementById('font-preview-text');
  if (!el) return;
  if (fontFile) {
    const f = installedFonts.find(x => x.file === fontFile);
    el.style.fontFamily = f ? `'${f.name}', sans-serif` : '';
    el.textContent = f ? `${f.name} — Aa Bb Cc 123` : 'Aa Bb Cc — 0123';
  } else {
    el.style.fontFamily = '';
    el.textContent = 'System Default — Aa Bb Cc 123';
  }
}

// ── Font Manager UI ──────────────────────────────────────────
function openFontManager() {
  document.getElementById('font-manager').classList.add('open');
  document.getElementById('fm-overlay').classList.add('open');
  loadFonts().then(() => renderFontList(installedFonts));
}
function closeFontManager() {
  document.getElementById('font-manager').classList.remove('open');
  document.getElementById('fm-overlay').classList.remove('open');
}

function renderFontList(fonts) {
  const el = document.getElementById('font-list');
  if (!el) return;
  if (!fonts.length) {
    el.innerHTML = '<div class="text-muted text-sm" style="padding:12px 0">No fonts uploaded yet. Use the upload area above.</div>';
    return;
  }
  el.innerHTML = fonts.map(f => `
    <div class="font-item ${activeFontFile===f.file?'selected':''}"
         onclick="selectFontFromManager('${escJs(f.file)}')">
      <div style="flex:1;min-width:0">
        <div class="font-item-name">${esc(f.name)}</div>
        <div class="font-item-size">${(f.size/1024).toFixed(1)} KB · .${f.ext}</div>
      </div>
      <div class="font-item-preview" style="font-family:'${cssEsc(f.name)}',sans-serif">
        Aa Bb 123
      </div>
      <button onclick="event.stopPropagation();deleteFont('${escJs(f.file)}')"
              style="background:none;border:none;color:var(--muted);cursor:pointer;
                     padding:4px;font-size:1rem;line-height:1;flex-shrink:0"
              title="Remove font">✕</button>
    </div>`).join('');
}

function selectFontFromManager(file) {
  activeFontFile = file;
  const sel = document.getElementById('font-select');
  if (sel) sel.value = file;
  updateFontPreview(file);
  applyNameProps();
  // Update selected state in list
  document.querySelectorAll('.font-item').forEach(el => {
    el.classList.toggle('selected', el.onclick?.toString().includes(`'${file}'`));
  });
  // Re-render to update selected state cleanly
  renderFontList(installedFonts);
}

async function deleteFont(file) {
  if (!confirm(`Remove font "${file}"? This cannot be undone.`)) return;
  const r = await fetch(BASE_URL + '/api/fonts.php?action=delete', {
    method:  'POST',
    headers: { 'Content-Type': 'application/json' },
    body:    JSON.stringify({ file })
  });
  const d = await r.json();
  if (d.ok) {
    if (activeFontFile === file) {
      activeFontFile = '';
      const sel = document.getElementById('font-select');
      if (sel) sel.value = '';
      applyNameProps();
    }
    await loadFonts();
    renderFontList(installedFonts);
  } else {
    alert('Could not delete: ' + (d.error || 'unknown error'));
  }
}

// ── Font upload ───────────────────────────────────────────────
const uploadInput = document.getElementById('font-upload-input');
const dropZone    = document.getElementById('drop-zone');

uploadInput?.addEventListener('change', () => uploadFonts(uploadInput.files));

dropZone?.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('dragover'); });
dropZone?.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
dropZone?.addEventListener('drop', e => {
  e.preventDefault(); dropZone.classList.remove('dragover');
  uploadFonts(e.dataTransfer.files);
});

async function uploadFonts(files) {
  if (!files?.length) return;
  const bar  = document.getElementById('upload-progress');
  const fill = document.getElementById('upload-progress-bar');
  bar.style.display = 'block'; fill.style.width = '20%';

  const fd = new FormData();
  Array.from(files).forEach(f => fd.append('fonts[]', f));

  try {
    fill.style.width = '60%';
    const r = await fetch(BASE_URL + '/api/fonts.php?action=upload', { method:'POST', body:fd });
    fill.style.width = '90%';
    const d = await r.json();
    const fails = (d.results||[]).filter(x=>!x.ok);
    if (fails.length) alert('Some fonts failed:\n' + fails.map(x=>`${x.file}: ${x.msg}`).join('\n'));
    fill.style.width = '100%';
    await loadFonts();
    renderFontList(installedFonts);
  } catch(e) {
    alert('Upload failed: ' + e.message);
  } finally {
    setTimeout(() => { bar.style.display='none'; fill.style.width='0'; }, 800);
    if (uploadInput) uploadInput.value = '';
  }
}

// ── Utilities ─────────────────────────────────────────────────
function rgbToHex(rgb) {
  if (!rgb) return '#ffffff';
  if (rgb.startsWith('#')) return rgb;
  const m = rgb.match(/\d+/g);
  if (!m||m.length<3) return '#ffffff';
  return '#' + m.slice(0,3).map(n=>parseInt(n).toString(16).padStart(2,'0')).join('');
}
const esc    = s => String(s||'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const escJs  = s => String(s||'').replace(/\\/g,'\\\\').replace(/'/g,"\\'");
const cssEsc = s => String(s||'').replace(/'/g,"\\'");
</script>

<?php layout_end(); ?>
