<?php
/**
 * Poet biography editor.
 *
 * Security model:
 *   - Auth: checklogin.php establishes $_SESSION['poet_id']
 *   - CSRF: per-session token, hash_equals(), rotated on success
 *   - Origin/Referer check
 *   - PDO prepared statements; only updates the current poet's own row
 *   - HTML sanitization via bio_sanitize() (DOM-based whitelist)
 *   - PRG redirect after save
 *
 * Editor:
 *   - TinyMCE with a deliberately tiny toolbar: bold, italic, underline,
 *     bullet list, numbered list, undo/redo. NO link, NO image, NO code,
 *     NO fullscreen. Anything pasted with disallowed tags is stripped on
 *     save by the server-side sanitizer.
 *   - Legacy bios (which may contain raw HTML from earlier versions) are
 *     loaded into the editor and rendered correctly. On save they pass
 *     through the same sanitizer, so old junk gets cleaned over time.
 */
declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

require_once __DIR__ . '/common.php';
require_once __DIR__ . '/checklogin.php';
require_once __DIR__ . '/bio_sanitize.php';

const BIO_MAX_CHARS = 2000; // raw character cap on the sanitized output

if (empty($_SESSION['bio_csrf']) || !is_string($_SESSION['bio_csrf'])) {
    $_SESSION['bio_csrf'] = bin2hex(random_bytes(32));
}

$flash_msg = $_SESSION['bio_flash_msg'] ?? '';
$flash_err = $_SESSION['bio_flash_err'] ?? '';
unset($_SESSION['bio_flash_msg'], $_SESSION['bio_flash_err']);

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $origin = $_SERVER['HTTP_ORIGIN'] ?? ($_SERVER['HTTP_REFERER'] ?? '');
    $host   = $_SERVER['HTTP_HOST'] ?? '';
    if ($origin === '' || ($host !== '' && stripos($origin, $host) === false)) {
        $_SESSION['bio_flash_err'] = 'Submission rejected (origin check).';
        header('Location: my-bio.php', true, 303);
        exit;
    }

    $token = (string)($_POST['csrf_token'] ?? '');
    if ($token === '' || !hash_equals($_SESSION['bio_csrf'], $token)) {
        $_SESSION['bio_flash_err'] = 'Submission rejected (token mismatch). Please try again.';
        header('Location: my-bio.php', true, 303);
        exit;
    }

    $raw = (string)($_POST['bio'] ?? '');
    $bio = bio_sanitize($raw);
    // Drop control chars (keep \n \r \t)
    $bio = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/u', '', $bio);

    // Length check: count VISIBLE text only, not the HTML markup overhead.
    $visible_len = mb_strlen(strip_tags($bio), 'UTF-8');
    if ($visible_len > BIO_MAX_CHARS) {
        $_SESSION['bio_flash_err'] = 'Your biography is ' . $visible_len . ' characters; the limit is ' . BIO_MAX_CHARS . '. Please shorten it.';
        $_SESSION['bio_flash_draft'] = $bio;  // preserve so user doesn't lose work
        header('Location: my-bio.php', true, 303);
        exit;
    }

    try {
        $poet_id = (int)$_SESSION['poet_id'];
        $stmt = $pdo->prepare('UPDATE sm_poets SET bio = :bio WHERE id = :id');
        $stmt->bindValue(':bio', $bio, PDO::PARAM_STR);
        $stmt->bindValue(':id',  $poet_id, PDO::PARAM_INT);
        $stmt->execute();
        $_SESSION['bio_csrf'] = bin2hex(random_bytes(32));
        $_SESSION['bio_flash_msg'] = 'Your biography has been updated.';
    } catch (Throwable $e) {
        error_log('my-bio update: ' . $e->getMessage());
        $_SESSION['bio_flash_err'] = 'Could not save your biography. Please try again.';
    }

    header('Location: my-bio.php', true, 303);
    exit;
}

// Pull preserved draft if a previous submission was rejected over-limit
$draft = $_SESSION['bio_flash_draft'] ?? '';
unset($_SESSION['bio_flash_draft']);

try {
    $poet_id = (int)$_SESSION['poet_id'];
    $stmt = $pdo->prepare('SELECT bio, author_name, author_lastname, pen_name FROM sm_poets WHERE id = :id');
    $stmt->bindValue(':id', $poet_id, PDO::PARAM_INT);
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
    $bio  = (string)($row['bio'] ?? '');
    $pen  = trim((string)($row['pen_name'] ?? ''));
    $real = trim(ucfirst((string)($row['author_lastname'] ?? '')) .
                 ((!empty($row['author_lastname']) && !empty($row['author_name'])) ? ', ' : '') .
                 ucfirst((string)($row['author_name'] ?? '')));
    $preview_name = $pen !== '' ? $pen : $real;
} catch (Throwable $e) {
    error_log('my-bio fetch: ' . $e->getMessage());
    $bio = '';
    $preview_name = '';
}

// Display in the editor: prefer the rejected draft (so the user doesn't
// lose work) over the stored bio. Sanitize either way -- the editor never
// receives anything dangerous.
$bio_for_editor = bio_sanitize($draft !== '' ? $draft : $bio);

include_once __DIR__ . '/header.php';
include_once __DIR__ . '/poets-menu.php';
?>

<div class="container" style="max-width: 720px; padding: 44px 48px 40px;">
  <h2>Edit Your Biography</h2>

  <?php if ($flash_msg !== ''): ?>
    <span class="msg"><?= htmlspecialchars($flash_msg, ENT_QUOTES, 'UTF-8') ?></span>
  <?php endif; ?>
  <?php if ($flash_err !== ''): ?>
    <span class="error"><?= htmlspecialchars($flash_err, ENT_QUOTES, 'UTF-8') ?></span>
  <?php endif; ?>

  <p style="color: var(--c-slate); font-style: italic; margin-bottom: 18px;">
    A short biography for the public contributors page. You can use bold, italic, underline, and bullet lists.
    Up to <?= BIO_MAX_CHARS ?> characters.
  </p>

  <form method="post" action="my-bio.php" autocomplete="off">
    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['bio_csrf'], ENT_QUOTES, 'UTF-8') ?>">

    <div class="form-group">
      <textarea
        id="bio"
        name="bio"
        rows="12"
        style="width: 100%;"
        ><?= htmlspecialchars($bio_for_editor, ENT_QUOTES, 'UTF-8') ?></textarea>
      <div id="bio-counter" style="text-align: right; font-family: var(--sans); font-size: 12px; color: var(--c-slate); letter-spacing: 0.06em; margin-top: 6px;">
        <span id="bio-count">0</span> / <?= BIO_MAX_CHARS ?>
      </div>
    </div>

    <input type="submit" id="bio-submit" name="sub_update_bio" value="Save Biography" class="button-primary">
  </form>

  <div class="bio-preview-wrap" style="margin-top: 36px;">
    <div style="font-family: var(--sans); font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--c-brass-deep); font-weight: 700; text-align: left; margin-bottom: 10px;">
      Preview &mdash; how this will appear on the Contributors page
    </div>
    <div id="bio-preview-card"
         style="border: 1px solid var(--c-rule); background: #fff;
                box-shadow: 0 12px 30px -22px rgba(13,27,47,0.18);
                padding: 28px 32px 32px; text-align: left;
                font-family: Georgia, 'Times New Roman', serif; color: #222;">
      <h3 style="margin: 0 0 14px; font-weight: 400; font-size: 22px; letter-spacing: 0.3px; color: #1f1f1f;">
        <?= htmlspecialchars($preview_name !== '' ? $preview_name : 'Your Name', ENT_QUOTES, 'UTF-8') ?>
      </h3>
      <div id="bio-preview-text" style="line-height: 1.65; font-size: 15px;">
        <em style="color: #999;">No biography provided.</em>
      </div>
    </div>
  </div>
</div>

<!-- TinyMCE: tightly-restricted toolbar. No link, image, code, or fullscreen. -->
<script src="tinymce/tinymce.min.js"></script>
<script>
  const BIO_MAX = <?= BIO_MAX_CHARS ?>;
  tinymce.init({
    selector: '#bio',
    height: 320,
    menubar: false,
    branding: false,
    promotion: false,
    plugins: 'lists',
    toolbar: 'bold italic underline | bullist numlist | undo redo',
    toolbar_mode: 'wrap',
    valid_elements: 'p,br,strong,em,b,i,u,ul,ol,li',
    paste_as_text: false,
    paste_block_drop: true,
    content_style: "body { font-family: Georgia, serif; font-size: 16px; line-height: 1.6; padding: 12px; }",
    setup: function (editor) {
      const counter    = document.getElementById('bio-count');
      const counterBox = document.getElementById('bio-counter');
      const submitBtn  = document.getElementById('bio-submit');
      const previewEl = document.getElementById('bio-preview-text');
      function update() {
        try {
          const txt = editor.getContent({ format: 'text' }) || '';
          const len = txt.length;
          counter.textContent = len;
          if (len > BIO_MAX) {
            counterBox.style.color = '#8c3a4a';
            counterBox.style.fontWeight = '600';
            submitBtn.disabled = true;
            submitBtn.style.opacity = '0.5';
            submitBtn.style.cursor = 'not-allowed';
          } else {
            counterBox.style.color = '';
            counterBox.style.fontWeight = '';
            submitBtn.disabled = false;
            submitBtn.style.opacity = '';
            submitBtn.style.cursor = '';
          }
          // Live preview
          if (previewEl) {
            const html = (editor.getContent() || '').trim();
            if (html === '') {
              previewEl.innerHTML = '<em style="color:#999;">No biography provided.</em>';
            } else {
              // TinyMCE's valid_elements is locked to the same whitelist
              // bio_sanitize() enforces server-side, so this is safe.
              previewEl.innerHTML = html;
            }
          }
        } catch (_) { /* never let counter or preview logic kill the editor */ }
      }
      editor.on('init', update);
      editor.on('input keyup change SetContent NodeChange', update);
    }
  });
</script>

<?php include_once __DIR__ . '/footer.php'; ?>
