---
/* ---------------------------------------------------------------------------
 * ResourceEditPanel — right-slide panel for create + edit.
 *
 * Rendered alongside ResourceListView when the URL carries ?edit=<id> or
 * ?new=1. POSTs back to the same URL; the route handler in step 7 reads
 * _action (save | delete | <action.key>) and dispatches.
 *
 * Visible-when predicates are evaluated server-side against current values
 * (existing item or form defaults). Live-toggle while editing is Phase 2.
 * ------------------------------------------------------------------------- */

import FieldRenderer from './FieldRenderer.astro';
import PulseSubForm from '../embeds/PulseSubForm.astro';
import type { Field, FieldContext, Resource } from '../resource-types';

interface Props {
  resource: Resource;
  /** The item being edited, or null when creating. */
  item: Record<string, unknown> | null;
  /** Pre-validated form values from a failed prior submission (re-fill). */
  formValues?: Record<string, unknown>;
  errors?: Record<string, string>;
  actingUserId: number;
}

const { resource, item, formValues, errors = {}, actingUserId } = Astro.props;

const isCreate = item === null;
// Review mode = the panel is showing an existing item AND either the resource
// has no form, OR it has a summary that should be preferred over the form
// (e.g. invitations are write-once: form is for create, summary is for edit).
const isReviewMode =
  !isCreate &&
  (resource.form === null || resource.summary !== undefined);
const singular = resource.singularLabel.toLowerCase();
const title = isReviewMode
  ? `Review ${singular}`
  : isCreate
    ? `New ${singular}`
    : `Edit ${singular}`;

// In review mode, the resource MUST have a summary function — that's what
// fills the panel body when the form is suppressed.
if (isReviewMode && !resource.summary) {
  throw new Error(
    `ResourceEditPanel: ${resource.key} is in review mode but has no summary — define resource.summary.`,
  );
}

// And the create path needs a form.
if (isCreate && !resource.form) {
  throw new Error(
    `ResourceEditPanel: cannot render create panel for ${resource.key} — form is null.`,
  );
}

// Initial form values: prior failed submission > existing item > defaults
const seedValues: Record<string, unknown> = { ...(item ?? {}), ...(formValues ?? {}) };

const ctx: FieldContext = {
  formValues: seedValues,
  item,
  actingUserId,
};

function resolveDefault(field: Field): unknown {
  if (field.defaultValue === undefined) return undefined;
  if (typeof field.defaultValue === 'function') {
    return (field.defaultValue as (c: FieldContext) => unknown)(ctx);
  }
  return field.defaultValue;
}

function valueFor(field: Field): unknown {
  if (field.key in seedValues) return seedValues[field.key];
  return resolveDefault(field);
}

const visibleFields = resource.form
  ? resource.form.fields.filter((f) => !f.visibleWhen || f.visibleWhen(ctx))
  : [];

const embeds = resource.form?.embeds ?? [];

// Review-mode summary
const summaryEntries = isReviewMode && item
  ? resource.summary!(item)
  : [];

// One-shot invite link surfaced after create/action — read from URL
const inviteUrl = Astro.url.searchParams.get('invite_url');

// Build the close URL — drop edit/new but keep filter/q/page
const closeUrl = (() => {
  const next = new URLSearchParams(Astro.url.searchParams);
  next.delete('edit');
  next.delete('new');
  const s = next.toString();
  return s ? `${Astro.url.pathname}?${s}` : Astro.url.pathname;
})();

// Actions visible for this item — only when editing an existing item
const actions = isCreate
  ? []
  : (resource.actions ?? []).filter(
      (a) => !a.visibleWhen || a.visibleWhen(item!),
    );

// Form action URL: keep the panel-state params so a re-render after a
// validation failure stays on the same item.
const formAction = Astro.url.pathname + Astro.url.search;
---

<div class="bs-panel-scrim">
  <a href={closeUrl} class="bs-panel-scrim-link" aria-label="Close panel"></a>

  <aside class="bs-panel" role="dialog" aria-modal="true" aria-label={title}>
    <header class="bs-panel-head">
      <h2 class="bs-panel-title">{title}</h2>
      <a href={closeUrl} class="bs-panel-close" aria-label="Close">×</a>
    </header>

    <form method="POST" action={formAction} class="bs-panel-form" id="bs-panel-form">
      <div class="bs-panel-body">
        {inviteUrl && (
          <section class="bs-invite-result" data-invite-block>
            <p class="bs-invite-result-label">Magic link — copy and send now. It will not be shown again.</p>
            <div class="bs-invite-link-row">
              <code class="bs-invite-link" id="bs-invite-link">{inviteUrl}</code>
              <button type="button" class="bs-copy-btn" data-copy-target="#bs-invite-link">Copy</button>
            </div>
          </section>
        )}

        {isReviewMode ? (
          <dl class="bs-summary">
            {summaryEntries.map((entry) => (
              <div class="bs-summary-row">
                <dt class="bs-summary-label">{entry.label}</dt>
                <dd class="bs-summary-value">{entry.value}</dd>
              </div>
            ))}
          </dl>
        ) : (
          <>
            {visibleFields.map((field) => (
              <FieldRenderer
                field={field}
                value={valueFor(field)}
                error={errors[field.key]}
                item={item}
              />
            ))}

            {embeds.length > 0 && embeds.map((embed) => {
              const show = !embed.visibleWhen || embed.visibleWhen(ctx);
              if (!show) return null;
              return (
                <section class="bs-embed" data-embed={embed.key}>
                  <h3 class="bs-embed-title">{embed.title}</h3>
                  {embed.component === 'pulse-sub-form' && (
                    <PulseSubForm item={item} />
                  )}
                </section>
              );
            })}
          </>
        )}
      </div>

      <footer class="bs-panel-foot">
        <div class="bs-panel-foot-left">
          {!isReviewMode && (
            <button type="submit" name="_action" value="save" class="bs-panel-save">
              {isCreate ? `Create ${singular}` : 'Save'}
            </button>
          )}
          {actions.map((a) => (
            <button
              type="submit"
              name="_action"
              value={a.key}
              class:list={['bs-panel-secondary', { destructive: a.destructive }]}
              data-confirm={a.confirmText ?? null}
            >
              {a.label}
            </button>
          ))}
        </div>

        {!isCreate && !isReviewMode && resource.ops.delete && (
          <button
            type="submit"
            name="_action"
            value="delete"
            class="bs-panel-delete"
            data-confirm={`Delete this ${singular}? This cannot be undone.`}
          >
            Delete
          </button>
        )}
      </footer>
    </form>
  </aside>
</div>

<script>
  // ── Confirm-before-submit for buttons with data-confirm ──────────────────
  document.addEventListener('click', (e) => {
    const btn = (e.target as HTMLElement | null)?.closest('button[data-confirm]');
    if (!(btn instanceof HTMLButtonElement)) return;
    const text = btn.getAttribute('data-confirm');
    if (text && !window.confirm(text)) {
      e.preventDefault();
      e.stopPropagation();
    }
  });

  // ── Escape to close ──────────────────────────────────────────────────────
  document.addEventListener('keydown', (e) => {
    if (e.key !== 'Escape') return;
    const closer = document.querySelector('.bs-panel-close') as HTMLAnchorElement | null;
    if (closer) closer.click();
  });

  // ── Markdown Write/Preview toggle ────────────────────────────────────────
  // Re-renders preview from the textarea's *current* value on every toggle.
  // The server-side initial render is only valid for the seed value — once
  // the admin types, only client-side rendering reflects what's there.
  document.querySelectorAll<HTMLElement>('.bs-md').forEach((root) => {
    const tabs = root.querySelectorAll<HTMLButtonElement>('.bs-md-tab');
    const input = root.querySelector<HTMLTextAreaElement>('.bs-md-input');
    const preview = root.querySelector<HTMLElement>('.bs-md-preview');
    if (!input || !preview) return;
    tabs.forEach((tab) => {
      tab.addEventListener('click', async () => {
        const mode = tab.getAttribute('data-md-mode');
        tabs.forEach((t) => t.classList.toggle('is-active', t === tab));
        if (mode === 'preview') {
          const value = input.value;
          if (value.trim() === '') {
            preview.innerHTML = '<p class="bs-md-empty">Nothing to preview yet.</p>';
          } else {
            const { marked } = await import('marked');
            marked.setOptions({ breaks: true, gfm: true });
            preview.innerHTML = await marked.parse(value);
          }
          input.hidden = true;
          preview.hidden = false;
        } else {
          input.hidden = false;
          preview.hidden = true;
        }
      });
    });
  });

  // ── Copy-to-clipboard for invite-link blocks ─────────────────────────────
  document.querySelectorAll<HTMLButtonElement>('.bs-copy-btn').forEach((btn) => {
    btn.addEventListener('click', async () => {
      const sel = btn.getAttribute('data-copy-target');
      const target = sel ? document.querySelector(sel) : null;
      const text = target?.textContent ?? '';
      try {
        await navigator.clipboard.writeText(text);
        const orig = btn.textContent;
        btn.textContent = 'Copied';
        setTimeout(() => { btn.textContent = orig; }, 1400);
      } catch {
        // clipboard blocked — leave the link visible for manual copy
      }
    });
  });

  // ── MultiTextField add / remove ──────────────────────────────────────────
  document.querySelectorAll<HTMLElement>('.bs-multitext').forEach((root) => {
    const rows = root.querySelector<HTMLElement>('.bs-multitext-rows');
    const addBtn = root.querySelector<HTMLButtonElement>('.bs-multitext-add');
    if (!rows || !addBtn) return;
    const fieldKey = root.dataset.multitext ?? 'option';
    const min = Number(root.dataset.min ?? '1');
    const max = Number(root.dataset.max ?? '10');

    function updateButtons() {
      const inputs = rows!.querySelectorAll<HTMLElement>('.bs-multitext-row');
      addBtn!.disabled = inputs.length >= max;
      inputs.forEach((row) => {
        const rm = row.querySelector<HTMLButtonElement>('.bs-multitext-remove');
        if (rm) rm.disabled = inputs.length <= min;
      });
    }

    rows.addEventListener('click', (e) => {
      const rm = (e.target as HTMLElement | null)?.closest('.bs-multitext-remove');
      if (!rm) return;
      const row = rm.closest('.bs-multitext-row');
      if (row && rows.querySelectorAll('.bs-multitext-row').length > min) {
        row.remove();
        updateButtons();
      }
    });

    addBtn.addEventListener('click', () => {
      const inputs = rows.querySelectorAll('.bs-multitext-row');
      if (inputs.length >= max) return;
      const row = document.createElement('div');
      row.className = 'bs-multitext-row';
      row.innerHTML =
        `<input type="text" name="${fieldKey}" class="bs-input" value="" placeholder="Option ${inputs.length + 1}" />` +
        `<button type="button" class="bs-multitext-remove" aria-label="Remove option">×</button>`;
      rows.appendChild(row);
      updateButtons();
    });

    updateButtons();
  });
</script>
