<?php
/**
 * Forms — create / edit a form.
 *
 * Uses a textarea DSL for the field list (parsed by FormsAPI). The
 * full drag-drop builder is a follow-up session. Webhook URLs are
 * one-per-line in another textarea — save replaces the list.
 */
require_once dirname(__DIR__, 3) . '/config.php';
require_once dirname(__DIR__) . '/FormsAPI.php';

Auth::require();
Auth::requirePerm('forms.manage');
FormsAPI::ensureSchema();

$tid = current_tenant_id();

$id   = (int)($_GET['id'] ?? 0);
$form = null;
$webhookUrlsText = '';

if ($id > 0) {
    $form = Database::row(
        "SELECT * FROM forms_definitions WHERE id = ? AND tenant_id = ?",
        [$id, $tid]
    );
    if (!$form) {
        http_response_code(404);
        require SLATE_ROOT . '/admin/partials/header.php';
        echo '<div class="card"><h1>Form not found</h1></div>';
        require SLATE_ROOT . '/admin/partials/footer.php';
        exit;
    }
    $hooks = Database::rows(
        "SELECT url FROM forms_webhooks WHERE form_id = ? AND tenant_id = ? AND is_active = 1 ORDER BY id",
        [$id, $tid]
    );
    $webhookUrlsText = implode("\n", array_column($hooks, 'url'));
}

$isNew = !$form;

// Defaults / form state
$state = [
    'title'             => $form['title']             ?? '',
    'slug'              => $form['slug']              ?? '',
    'description'       => $form['description']       ?? '',
    'fields_dsl'        => $form ? FormsAPI::fieldsToDsl(json_decode($form['fields_json'] ?? '[]', true) ?: []) : "text|full_name|Your name|required\nemail|email|Email|required\ntextarea|message|Message|required",
    'submit_label'      => $form['submit_label']      ?? 'Submit',
    'success_message'   => $form['success_message']   ?? 'Thanks — we got your submission. We\'ll be in touch.',
    'redirect_url'      => $form['redirect_url']      ?? '',
    'notify_email'      => $form['notify_email']      ?? '',
    'confirm_submitter' => (int)($form['confirm_submitter'] ?? 0),
    'confirm_subject'   => $form['confirm_subject']   ?? '',
    'confirm_body'      => $form['confirm_body']      ?? '',
    'submission_limit'  => $form['submission_limit']  ?? '',
    'status'            => $form['status']            ?? 'draft',
    'webhook_urls'      => $webhookUrlsText,
];

$flash      = null;
$fieldErrors = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!csrf_verify()) {
        $flash = ['type' => 'error', 'msg' => 'Security check failed.'];
    } else {
        // Soak up posted state
        foreach (array_keys($state) as $k) {
            if (array_key_exists($k, $_POST)) {
                $state[$k] = is_string($_POST[$k]) ? $_POST[$k] : $state[$k];
            }
        }
        $state['confirm_submitter'] = !empty($_POST['confirm_submitter']) ? 1 : 0;

        // Validate
        $title = trim($state['title']);
        if ($title === '') {
            $fieldErrors['title'] = 'Title is required.';
        }
        if ($state['notify_email'] !== '' && !filter_var($state['notify_email'], FILTER_VALIDATE_EMAIL)) {
            $fieldErrors['notify_email'] = 'Notify email is not a valid email address.';
        }
        if ($state['redirect_url'] !== '' && !filter_var($state['redirect_url'], FILTER_VALIDATE_URL)) {
            $fieldErrors['redirect_url'] = 'Redirect URL is not valid.';
        }

        $parsed = FormsAPI::parseFieldDsl($state['fields_dsl']);
        if (!empty($parsed['errors'])) {
            $fieldErrors['fields_dsl'] = implode(' ', $parsed['errors']);
        }
        if (empty($parsed['fields'])) {
            $fieldErrors['fields_dsl'] = ($fieldErrors['fields_dsl'] ?? '') . ' At least one field is required.';
        }

        // Parse webhook URLs
        $hookUrls = [];
        foreach (preg_split('/\r\n|\n|\r/', $state['webhook_urls']) as $line) {
            $u = trim($line);
            if ($u === '') continue;
            if (!filter_var($u, FILTER_VALIDATE_URL)) {
                $fieldErrors['webhook_urls'] = 'One or more webhook URLs are invalid.';
                continue;
            }
            $hookUrls[] = $u;
        }

        if (empty($fieldErrors)) {
            $slug = trim($state['slug']);
            if ($slug === '') {
                $slug = FormsAPI::slugify($title, $isNew ? null : $id);
            } else {
                // Normalise + de-dup
                $slug = FormsAPI::slugify($slug, $isNew ? null : $id);
            }

            $row = [
                'tenant_id'         => $tid,
                'slug'              => $slug,
                'title'             => $title,
                'description'       => $state['description'] !== '' ? $state['description'] : null,
                'fields_json'       => json_encode($parsed['fields'], JSON_UNESCAPED_UNICODE),
                'submit_label'      => $state['submit_label'] !== '' ? $state['submit_label'] : 'Submit',
                'success_message'   => $state['success_message'] !== '' ? $state['success_message'] : null,
                'redirect_url'      => $state['redirect_url'] !== '' ? $state['redirect_url'] : null,
                'notify_email'      => $state['notify_email'] !== '' ? $state['notify_email'] : null,
                'confirm_submitter' => (int)$state['confirm_submitter'],
                'confirm_subject'   => $state['confirm_subject'] !== '' ? $state['confirm_subject'] : null,
                'confirm_body'      => $state['confirm_body'] !== '' ? $state['confirm_body'] : null,
                'submission_limit'  => $state['submission_limit'] !== '' && is_numeric($state['submission_limit'])
                                        ? (int)$state['submission_limit'] : null,
                'status'            => in_array($state['status'], ['draft','published','archived'], true)
                                        ? $state['status'] : 'draft',
            ];

            if ($isNew) {
                $id = Database::insert('forms_definitions', $row);
                AuditLog::record('forms.created', (string)$id, ['slug' => $slug]);
                $flash = ['type' => 'success', 'msg' => 'Form created.'];
                $isNew = false;
            } else {
                Database::update('forms_definitions', $row, 'id = ? AND tenant_id = ?', [$id, $tid]);
                AuditLog::record('forms.updated', (string)$id);
                $flash = ['type' => 'success', 'msg' => 'Form saved.'];
            }

            // Sync webhooks: simple replace strategy.
            Database::delete('forms_webhooks', 'form_id = ? AND tenant_id = ?', [$id, $tid]);
            foreach ($hookUrls as $u) {
                Database::insert('forms_webhooks', [
                    'tenant_id' => $tid,
                    'form_id'   => $id,
                    'url'       => $u,
                    'is_active' => 1,
                ]);
            }

            // Re-read the saved state so the form reflects what's stored
            $form = Database::row("SELECT * FROM forms_definitions WHERE id = ?", [$id]);
            $state['slug']        = $form['slug'];
            $state['fields_dsl']  = FormsAPI::fieldsToDsl($parsed['fields']);
            $state['webhook_urls'] = implode("\n", $hookUrls);
        } else {
            $flash = ['type' => 'error', 'msg' => 'Please fix the highlighted fields.'];
        }
    }
}

$pageTitle  = $isNew ? __('forms_new', 'New form') : ($state['title'] !== '' ? $state['title'] : __('forms_edit', 'Edit form'));
$currentNav = 'forms';

require SLATE_ROOT . '/admin/partials/header.php';

$publicUrl = ($state['slug'] !== '' && !$isNew) ? SLATE_URL . '/forms/' . $state['slug'] : null;
?>

<?php slate_breadcrumbs([
    ['label' => __('dashboard', 'Dashboard'), 'href' => SLATE_URL . '/admin/'],
    ['label' => __('forms', 'Forms'), 'href' => plugin_url('forms', 'admin/index.php')],
    ['label' => $isNew ? 'New form' : $state['title']],
]); ?>

<div class="page-header">
    <div>
        <h1><?= $isNew ? 'New form' : e($state['title']) ?></h1>
        <?php if ($publicUrl): ?>
            <p class="page-header-sub">
                <code style="word-break:break-all;"><?= e($publicUrl) ?></code>
                <?php if ($state['status'] === 'published'): ?>
                    · <a href="<?= e($publicUrl) ?>" target="_blank">Open ↗</a>
                <?php endif; ?>
            </p>
        <?php endif; ?>
    </div>
</div>

<?php if ($flash): ?>
    <div class="alert alert-<?= e($flash['type']) ?>" role="status"><?= e($flash['msg']) ?></div>
<?php endif; ?>

<form method="post">
    <?= csrf_field() ?>

    <?php slate_page_layout('with-aside'); ?>

        <div class="page-main">

            <div class="card">
                <div class="card-header"><h2>General</h2></div>

                <div class="field">
                    <label class="field-label" for="title">Title <span class="field-required">*</span></label>
                    <input type="text" id="title" name="title" required maxlength="200"
                           value="<?= e($state['title']) ?>">
                    <?php if (!empty($fieldErrors['title'])): ?>
                        <div class="field-error"><?= e($fieldErrors['title']) ?></div>
                    <?php endif; ?>
                </div>

                <div class="field-row field-row-2">
                    <div class="field">
                        <label class="field-label" for="slug">URL slug</label>
                        <input type="text" id="slug" name="slug" maxlength="80"
                               value="<?= e($state['slug']) ?>"
                               placeholder="<?= e($isNew ? 'auto-generated from title' : '') ?>">
                        <div class="field-hint">Lowercase letters / digits / hyphens. Leave blank to auto-generate.</div>
                    </div>
                    <div class="field">
                        <label class="field-label" for="status">Status</label>
                        <select id="status" name="status">
                            <option value="draft"     <?= $state['status'] === 'draft'     ? 'selected' : '' ?>>Draft (not public)</option>
                            <option value="published" <?= $state['status'] === 'published' ? 'selected' : '' ?>>Published (live)</option>
                            <option value="archived"  <?= $state['status'] === 'archived'  ? 'selected' : '' ?>>Archived</option>
                        </select>
                    </div>
                </div>

                <div class="field">
                    <label class="field-label" for="description">Description</label>
                    <textarea id="description" name="description" maxlength="1000"><?= e($state['description']) ?></textarea>
                    <div class="field-hint">Shown above the form on the public page.</div>
                </div>
            </div>

            <div class="card" id="fb-card">
                <div class="card-header">
                    <h2>Fields</h2>
                    <button type="button" class="btn btn-sm" id="fb-toggle">Advanced (DSL)</button>
                </div>

                <div class="fb-palette" id="fb-palette">
                    <span class="fb-palette-label">Add a field</span>
                    <button type="button" class="fb-add" data-type="text">Text</button>
                    <button type="button" class="fb-add" data-type="textarea">Paragraph</button>
                    <button type="button" class="fb-add" data-type="email">Email</button>
                    <button type="button" class="fb-add" data-type="tel">Phone</button>
                    <button type="button" class="fb-add" data-type="number">Number</button>
                    <button type="button" class="fb-add" data-type="date">Date</button>
                    <button type="button" class="fb-add" data-type="url">URL</button>
                    <button type="button" class="fb-add" data-type="select">Dropdown</button>
                    <button type="button" class="fb-add" data-type="radio">Choice</button>
                    <button type="button" class="fb-add" data-type="checkbox">Checkbox</button>
                    <button type="button" class="fb-add" data-type="file">File</button>
                </div>

                <div class="fb-list" id="fb-list"></div>
                <div class="fb-empty" id="fb-empty">No fields yet — click a type above to add one. Drag the ⠿ handle to reorder.</div>

                <!-- Always-posted source of truth; the builder writes here. Power
                     users can edit it directly via the Advanced toggle. -->
                <div class="field" id="fb-advanced" hidden>
                    <label class="field-label" for="fields_dsl">Field DSL — <code>type|name|label|required|placeholder|options</code></label>
                    <textarea id="fields_dsl" name="fields_dsl" rows="14"
                              style="font-family:var(--font-mono); font-size:12.5px; min-height:240px;"
                              spellcheck="false"><?= e($state['fields_dsl']) ?></textarea>
                    <div class="field-hint">
                        Supported types: text, email, tel, url, number, date, textarea, select, radio, checkbox, file.
                        For <code>select</code>/<code>radio</code>, options are comma-separated. Switch back to the
                        visual builder to sync your edits.
                    </div>
                </div>
                <?php if (!empty($fieldErrors['fields_dsl'])): ?>
                    <div class="field-error" style="margin-top:8px;"><?= e($fieldErrors['fields_dsl']) ?></div>
                <?php endif; ?>
            </div>

            <div class="card">
                <div class="card-header"><h2>After submit</h2></div>

                <div class="field-row field-row-2">
                    <div class="field">
                        <label class="field-label" for="submit_label">Submit button label</label>
                        <input type="text" id="submit_label" name="submit_label" maxlength="80"
                               value="<?= e($state['submit_label']) ?>">
                    </div>
                    <div class="field">
                        <label class="field-label" for="submission_limit">Submission limit</label>
                        <input type="number" id="submission_limit" name="submission_limit" min="0"
                               value="<?= e($state['submission_limit']) ?>"
                               placeholder="unlimited">
                        <div class="field-hint">Form closes once this many submissions land.</div>
                    </div>
                </div>

                <div class="field">
                    <label class="field-label" for="success_message">Success message</label>
                    <textarea id="success_message" name="success_message" maxlength="1000"><?= e($state['success_message']) ?></textarea>
                    <div class="field-hint">Shown on the thank-you page after a successful submit (unless redirect URL is set).</div>
                </div>

                <div class="field">
                    <label class="field-label" for="redirect_url">Redirect URL <span class="text-muted text-xs">(optional)</span></label>
                    <input type="url" id="redirect_url" name="redirect_url" maxlength="500"
                           value="<?= e($state['redirect_url']) ?>"
                           placeholder="https://example.com/thanks">
                    <?php if (!empty($fieldErrors['redirect_url'])): ?>
                        <div class="field-error"><?= e($fieldErrors['redirect_url']) ?></div>
                    <?php endif; ?>
                    <div class="field-hint">If set, the submitter is redirected here instead of seeing the success message.</div>
                </div>
            </div>

            <div class="card">
                <div class="card-header"><h2>Notifications</h2></div>

                <div class="field">
                    <label class="field-label" for="notify_email">Notify email</label>
                    <input type="email" id="notify_email" name="notify_email" maxlength="200"
                           value="<?= e($state['notify_email']) ?>"
                           placeholder="you@example.com">
                    <?php if (!empty($fieldErrors['notify_email'])): ?>
                        <div class="field-error"><?= e($fieldErrors['notify_email']) ?></div>
                    <?php endif; ?>
                    <div class="field-hint">Where to send a copy of each submission. Leave blank to skip.</div>
                </div>

                <div class="field">
                    <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400;">
                        <input type="checkbox" name="confirm_submitter" value="1"
                               <?= $state['confirm_submitter'] ? 'checked' : '' ?>>
                        <span>Email the submitter a confirmation (uses the form's <code>email</code> field)</span>
                    </label>
                </div>

                <div class="field-row field-row-2">
                    <div class="field">
                        <label class="field-label" for="confirm_subject">Confirmation subject</label>
                        <input type="text" id="confirm_subject" name="confirm_subject" maxlength="200"
                               value="<?= e($state['confirm_subject']) ?>"
                               placeholder="We got your message">
                    </div>
                </div>

                <div class="field">
                    <label class="field-label" for="confirm_body">Confirmation body (HTML)</label>
                    <textarea id="confirm_body" name="confirm_body" rows="4"><?= e($state['confirm_body']) ?></textarea>
                    <div class="field-hint">Sent to the submitter when the checkbox above is on and the form has an <code>email</code> field.</div>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2>Webhooks</h2>
                    <span class="text-xs text-muted">POST JSON on submit</span>
                </div>
                <div class="field">
                    <textarea id="webhook_urls" name="webhook_urls" rows="3"
                              style="font-family:var(--font-mono); font-size:12.5px;"
                              placeholder="https://hooks.zapier.com/...&#10;https://internal.example.com/forms"><?= e($state['webhook_urls']) ?></textarea>
                    <?php if (!empty($fieldErrors['webhook_urls'])): ?>
                        <div class="field-error"><?= e($fieldErrors['webhook_urls']) ?></div>
                    <?php endif; ?>
                    <div class="field-hint">One URL per line. Each submission is POSTed as JSON. Save and we'll start firing.</div>
                </div>
            </div>

            <div class="flex gap-2 mt-3">
                <button type="submit" class="btn btn-primary btn-lg"><?= $isNew ? 'Create form' : 'Save changes' ?></button>
                <a href="<?= e(plugin_url('forms', 'admin/index.php')) ?>" class="btn btn-ghost">Cancel</a>
            </div>
        </div>

        <aside class="page-aside">
            <?php if (!$isNew && $state['status'] === 'published' && $publicUrl): ?>
                <div class="aside-card">
                    <div class="aside-card-title">Public URL</div>
                    <p class="text-sm" style="margin:0 0 var(--space-3);"><a href="<?= e($publicUrl) ?>" target="_blank"><?= e($publicUrl) ?></a></p>
                    <div class="aside-card-title" style="margin-top:var(--space-3);">Embed snippet</div>
                    <pre class="snippet" style="white-space:pre-wrap;">&lt;iframe src=&quot;<?= e($publicUrl) ?>?embed=1&quot;
  style=&quot;width:100%;border:0;min-height:520px&quot;&gt;
&lt;/iframe&gt;</pre>
                </div>
            <?php endif; ?>

            <div class="aside-card">
                <div class="aside-card-title">DSL cheatsheet</div>
                <ul class="kv-list">
                    <li class="kv-row"><span class="kv-label kv-mono">type</span><span class="kv-value">text · email · tel · url · number · date · textarea · select · radio · checkbox</span></li>
                    <li class="kv-row"><span class="kv-label kv-mono">name</span><span class="kv-value">lowercase, letters/digits/underscores</span></li>
                    <li class="kv-row"><span class="kv-label kv-mono">label</span><span class="kv-value">shown next to the input</span></li>
                    <li class="kv-row"><span class="kv-label kv-mono">required</span><span class="kv-value">literal word <code>required</code></span></li>
                    <li class="kv-row"><span class="kv-label kv-mono">placeholder</span><span class="kv-value">optional hint</span></li>
                    <li class="kv-row"><span class="kv-label kv-mono">options</span><span class="kv-value">for select/radio, comma-separated</span></li>
                </ul>
            </div>

            <?php if (!$isNew): ?>
                <div class="aside-card">
                    <div class="aside-card-title">Submissions</div>
                    <p class="text-sm text-muted" style="margin:0 0 var(--space-3);">View, filter, and export the inbox for this form.</p>
                    <a href="<?= e(plugin_url('forms', 'admin/submissions.php')) ?>?form_id=<?= (int)$id ?>" class="btn btn-block">Open inbox</a>
                </div>
            <?php endif; ?>
        </aside>

    <?php slate_page_layout_end(); ?>
</form>

<style>
.fb-palette{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-bottom:14px;}
.fb-palette-label{font-size:12px;font-weight:600;color:var(--muted);margin-right:4px;}
.fb-add{border:1px solid var(--border);background:var(--card,#fff);border-radius:8px;padding:5px 11px;font-size:12.5px;cursor:pointer;color:var(--text);transition:border-color .12s,color .12s,background .12s;}
.fb-add:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft);}
.fb-list{display:flex;flex-direction:column;gap:10px;}
.fb-item{border:1px solid var(--border);border-radius:10px;background:var(--card,#fff);overflow:hidden;}
.fb-item.fb-dragging{opacity:.45;}
.fb-item.fb-over{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);}
.fb-head{display:flex;align-items:center;gap:10px;padding:9px 12px;background:var(--bg-soft,#FAFAF7);border-bottom:1px solid var(--border);}
.fb-handle{cursor:grab;color:var(--muted);font-size:15px;line-height:1;user-select:none;}
.fb-badge{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--accent);background:var(--accent-soft);padding:2px 8px;border-radius:999px;}
.fb-head-label{flex:1;font-weight:600;font-size:13px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.fb-del{border:0;background:transparent;color:var(--muted);cursor:pointer;font-size:15px;line-height:1;padding:3px 7px;border-radius:6px;}
.fb-del:hover{color:#DC2626;background:#FEF2F2;}
.fb-body{padding:12px;display:grid;grid-template-columns:1fr 1fr;gap:10px;}
.fb-full{grid-column:1/-1;}
.fb-body label{display:block;font-size:11px;font-weight:600;color:var(--muted);margin-bottom:3px;}
.fb-body input[type=text]{width:100%;}
.fb-req{display:flex !important;align-items:center;gap:6px;font-weight:400 !important;color:var(--text) !important;font-size:13px !important;cursor:pointer;margin:0;}
.fb-req input{width:16px;height:16px;accent-color:var(--accent);}
.fb-empty{text-align:center;color:var(--muted);font-size:13px;padding:20px;border:1px dashed var(--border);border-radius:10px;}
</style>
<script>
(function(){
    var TYPES = {text:'Text',textarea:'Paragraph',email:'Email',tel:'Phone',number:'Number',
                 date:'Date',url:'URL',select:'Dropdown',radio:'Choice',checkbox:'Checkbox',file:'File'};
    var hasOptions = function(t){ return t === 'select' || t === 'radio'; };

    var ta       = document.getElementById('fields_dsl');
    var list     = document.getElementById('fb-list');
    var empty    = document.getElementById('fb-empty');
    var palette  = document.getElementById('fb-palette');
    var advanced = document.getElementById('fb-advanced');
    var toggle   = document.getElementById('fb-toggle');
    if (!ta || !list) return;

    function slugify(s){
        return (String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'_').replace(/^_+|_+$/g,'').slice(0,50)) || 'field';
    }
    function uniqueName(base, exceptIdx){
        base = slugify(base); var n = base, i = 2;
        var taken = function(nm){ return fields.some(function(f,j){ return j !== exceptIdx && (f.name||'') === nm; }); };
        while (taken(n)) { n = base + '_' + i; i++; }
        return n;
    }
    function parse(text){
        var out = [];
        String(text||'').split(/\r\n|\n|\r/).forEach(function(line){
            line = line.trim(); if (!line || line[0] === '#') return;
            var p = line.split('|').map(function(s){ return s.trim(); });
            var type = (p[0]||'').toLowerCase();
            if (!TYPES[type]) return;
            out.push({
                type: type, name: p[1]||'', label: p[2]||'',
                required: (p[3]||'').toLowerCase() === 'required',
                placeholder: p[4]||'',
                options: p[5] ? p[5].split(',').map(function(s){return s.trim();}).filter(Boolean) : [],
                _named: !!(p[1])
            });
        });
        return out;
    }
    function toDsl(){
        return fields.map(function(f){
            var parts = [f.type, f.name || slugify(f.label), f.label || '',
                         f.required ? 'required' : '', f.placeholder || '',
                         hasOptions(f.type) ? (f.options||[]).join(',') : ''];
            while (parts.length > 3 && parts[parts.length-1] === '') parts.pop();
            return parts.join('|');
        }).join('\n');
    }
    function sync(){ ta.value = toDsl(); empty.style.display = fields.length ? 'none' : ''; }

    function textField(label, val, oninput, full){
        var w = document.createElement('div'); if (full) w.className = 'fb-full';
        var l = document.createElement('label'); l.textContent = label;
        var i = document.createElement('input'); i.type = 'text'; i.value = val || '';
        i.addEventListener('input', function(){ oninput(i.value); });
        w.appendChild(l); w.appendChild(i); return w;
    }
    function card(f, idx){
        var item = document.createElement('div'); item.className = 'fb-item'; item.dataset.idx = idx;

        var head = document.createElement('div'); head.className = 'fb-head'; head.draggable = true;
        var handle = document.createElement('span'); handle.className = 'fb-handle'; handle.textContent = '⠿'; handle.title = 'Drag to reorder';
        var badge = document.createElement('span'); badge.className = 'fb-badge'; badge.textContent = TYPES[f.type] || f.type;
        var hlabel = document.createElement('span'); hlabel.className = 'fb-head-label'; hlabel.textContent = f.label || '(untitled)';
        var del = document.createElement('button'); del.type = 'button'; del.className = 'fb-del'; del.textContent = '✕'; del.title = 'Remove field';
        del.addEventListener('click', function(){ fields.splice(idx,1); render(); });
        head.appendChild(handle); head.appendChild(badge); head.appendChild(hlabel); head.appendChild(del);

        head.addEventListener('dragstart', function(e){ item.classList.add('fb-dragging'); e.dataTransfer.setData('text/plain', String(idx)); e.dataTransfer.effectAllowed = 'move'; });
        head.addEventListener('dragend', function(){ item.classList.remove('fb-dragging'); });
        item.addEventListener('dragover', function(e){ e.preventDefault(); item.classList.add('fb-over'); });
        item.addEventListener('dragleave', function(){ item.classList.remove('fb-over'); });
        item.addEventListener('drop', function(e){
            e.preventDefault(); item.classList.remove('fb-over');
            var from = parseInt(e.dataTransfer.getData('text/plain'), 10);
            if (isNaN(from) || from === idx) return;
            var moved = fields.splice(from, 1)[0];
            fields.splice(idx, 0, moved); render();
        });

        var body = document.createElement('div'); body.className = 'fb-body';
        body.appendChild(textField('Label', f.label, function(v){
            f.label = v; hlabel.textContent = v || '(untitled)';
            if (!f._named) f.name = uniqueName(v, idx);
            sync();
        }));
        body.appendChild(textField('Field key', f.name, function(v){
            f.name = v; f._named = true; sync();
        }));
        if (f.type !== 'checkbox' && f.type !== 'file') {
            body.appendChild(textField('Placeholder', f.placeholder, function(v){ f.placeholder = v; sync(); }));
        }
        if (hasOptions(f.type)) {
            body.appendChild(textField('Options (comma-separated)', (f.options||[]).join(', '), function(v){
                f.options = v.split(',').map(function(s){return s.trim();}).filter(Boolean); sync();
            }, true));
        }
        var reqWrap = document.createElement('div'); reqWrap.className = 'fb-full';
        var rl = document.createElement('label'); rl.className = 'fb-req';
        var cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = !!f.required;
        cb.addEventListener('change', function(){ f.required = cb.checked; sync(); });
        rl.appendChild(cb); rl.appendChild(document.createTextNode(' Required'));
        reqWrap.appendChild(rl); body.appendChild(reqWrap);

        item.appendChild(head); item.appendChild(body);
        return item;
    }
    function render(){
        list.innerHTML = '';
        fields.forEach(function(f, i){ list.appendChild(card(f, i)); });
        sync();
    }

    var fields = parse(ta.value);

    Array.prototype.forEach.call(document.querySelectorAll('.fb-add'), function(btn){
        btn.addEventListener('click', function(){
            var t = btn.dataset.type;
            fields.push({ type: t, name: uniqueName(t), label: TYPES[t],
                          required: false, placeholder: '',
                          options: hasOptions(t) ? ['Option 1','Option 2'] : [], _named: false });
            render();
        });
    });

    var showingAdvanced = false;
    toggle.addEventListener('click', function(){
        showingAdvanced = !showingAdvanced;
        if (showingAdvanced) {
            sync(); advanced.hidden = false; list.style.display = 'none';
            palette.style.display = 'none'; empty.style.display = 'none';
            toggle.textContent = 'Visual builder';
        } else {
            fields = parse(ta.value); advanced.hidden = true; list.style.display = '';
            palette.style.display = ''; toggle.textContent = 'Advanced (DSL)'; render();
        }
    });

    var form = ta.closest('form');
    if (form) form.addEventListener('submit', function(){ if (!showingAdvanced) sync(); });

    render();
})();
</script>

<?php require SLATE_ROOT . '/admin/partials/footer.php'; ?>
