<?php
/**
 * Poet profile editor.
 *
 * Security model (mirrors my-bio.php):
 *   - Auth: checklogin.php (provides $_SESSION['poet_id'])
 *   - CSRF: per-session token, hash_equals(), rotated on success
 *   - Origin/Referer check (defense in depth)
 *   - PDO prepared statements; only updates the current poet's row
 *   - Server-side validation; trim, strip_tags, control-char strip, length caps
 *   - Email validated with filter_var() and uniqueness-checked
 *   - PRG redirect after save
 *
 * Email change flow (two-step verification):
 *   1. Poet submits a new email -> we write pending_email + token + expiry,
 *      then mail a confirmation link to the NEW address. The real `email`
 *      column is NOT changed yet.
 *   2. Poet clicks the link -> confirm-email-change.php validates the token,
 *      double-checks uniqueness, and performs the swap.
 *   3. Tokens expire after one hour. A pending change can be cancelled here.
 */
declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}
require_once __DIR__ . '/common.php';
require_once __DIR__ . '/checklogin.php';

const PROFILE_PEN_MAX     = 120;
const PROFILE_EMAIL_MAX   = 255;
const EMAIL_TOKEN_TTL_MIN = 60;   // confirmation link valid for 1 hour

// CSRF token
if (empty($_SESSION['profile_csrf']) || !is_string($_SESSION['profile_csrf'])) {
    $_SESSION['profile_csrf'] = bin2hex(random_bytes(32));
}

// Pull flash
$flash_msg = $_SESSION['profile_flash_msg'] ?? '';
$flash_err = $_SESSION['profile_flash_err'] ?? '';
unset($_SESSION['profile_flash_msg'], $_SESSION['profile_flash_err']);

$poet_id = (int)($_SESSION['poet_id'] ?? 0);

/**
 * Send the email-change confirmation link via PHPMailer using the SMTP
 * settings stored in the mail_settings table. Returns true on success.
 */
function send_email_change_confirmation(PDO $pdo, string $new_email, string $first_name, string $token): bool {
    try {
        $smtp_stmt = $pdo->query("SELECT setting_key, setting_value FROM mail_settings");
        $smtp = [];
        foreach ($smtp_stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
            $smtp[$r['setting_key']] = $r['setting_value'];
        }
        if (empty($smtp['mail_host']) || empty($smtp['mail_username'])) return false;

        require_once __DIR__ . '/phpmailer/src/PHPMailer.php';
        require_once __DIR__ . '/phpmailer/src/SMTP.php';
        require_once __DIR__ . '/phpmailer/src/Exception.php';

        $mail = new \PHPMailer\PHPMailer\PHPMailer(true);
        $mail->isSMTP();
        $mail->Host       = $smtp['mail_host'];
        $mail->SMTPAuth   = true;
        $mail->Username   = $smtp['mail_username'];
        $mail->Password   = $smtp['mail_password'] ?? '';
        $mail->SMTPSecure = $smtp['mail_encryption'] ?? 'ssl';
        $mail->Port       = (int)($smtp['mail_port'] ?? 465);

        $sname = (string)($GLOBALS['site_name'] ?? 'our site');
        $mail->setFrom($smtp['mail_from_address'] ?? $smtp['mail_username'], $smtp['mail_from_name'] ?? $sname);
        $mail->addAddress($new_email, $first_name);
        $mail->Subject = 'Confirm your new email address';

        // Build absolute URL to the confirm endpoint.
        $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host   = $_SERVER['HTTP_HOST'] ?? '';
        $base   = rtrim(dirname($_SERVER['REQUEST_URI'] ?? '/'), '/\\');
        $url    = $scheme . '://' . $host . $base . '/confirm-email-change.php?token=' . urlencode($token);

        $body  = 'Hello ' . htmlspecialchars($first_name, ENT_QUOTES) . ',<br><br>';
        $body .= 'A request was made to change the email address on your ' . htmlspecialchars($sname, ENT_QUOTES) . ' account to this one.<br><br>';
        $body .= 'To confirm the change, click the link below within ' . EMAIL_TOKEN_TTL_MIN . ' minutes:<br>';
        $body .= '<a href="' . htmlspecialchars($url, ENT_QUOTES) . '">' . htmlspecialchars($url, ENT_QUOTES) . '</a><br><br>';
        $body .= 'If you did not request this, you can safely ignore this message &mdash; the change will not take effect until the link is clicked.';
        $mail->isHTML(true);
        $mail->Body = $body;
        $mail->AltBody = "A request was made to change your " . $sname . " email to this address.\n"
                       . "Confirm with this link within " . EMAIL_TOKEN_TTL_MIN . " minutes:\n$url\n"
                       . "If you didn't request this, you can ignore this email.";

        $mail->send();
        return true;
    } catch (Throwable $e) {
        error_log('send_email_change_confirmation: ' . $e->getMessage());
        return false;
    }
}

// --- Handle save ---------------------------------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $errs = [];

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

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

    // Cancel a pending email change?
    if (isset($_POST['cancel_email_change'])) {
        try {
            $stmt = $pdo->prepare('UPDATE sm_poets SET pending_email = NULL, email_change_token = NULL, email_change_expiry = NULL WHERE id = :id');
            $stmt->bindValue(':id', $poet_id, PDO::PARAM_INT);
            $stmt->execute();
            $_SESSION['profile_flash_msg'] = 'Pending email change cancelled.';
        } catch (Throwable $e) {
            error_log('my-profile cancel-email: ' . $e->getMessage());
            $_SESSION['profile_flash_err'] = 'Could not cancel the pending change.';
        }
        $_SESSION['profile_csrf'] = bin2hex(random_bytes(32));
        header('Location: my-profile.php', true, 303);
        exit;
    }

    // Sanitize inputs
    $pen   = trim(strip_tags((string)($_POST['pen_name'] ?? '')));
    $email = trim((string)($_POST['email'] ?? ''));
    $pen   = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/u', '', $pen);
    $email = preg_replace('/[\x00-\x1F\s]+/u', '', $email);

    if (mb_strlen($pen, 'UTF-8') > PROFILE_PEN_MAX) {
        $pen = mb_substr($pen, 0, PROFILE_PEN_MAX, 'UTF-8');
    }
    if (mb_strlen($email, 'UTF-8') > PROFILE_EMAIL_MAX) {
        $errs['email'] = 'Email address is too long.';
    }

    // Email validation
    if ($email === '') {
        $errs['email'] = 'Email is required.';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errs['email'] = 'That email address does not appear to be valid.';
    }

    // Visibility toggle (checked = visible -> no_authors_page = 0)
    $no_authors_page = isset($_POST['include_in_contribs']) ? 0 : 1;

    // Fetch current state (current email + first name for the email body)
    $current_email = '';
    $current_first = '';
    try {
        $cs = $pdo->prepare('SELECT email, author_name FROM sm_poets WHERE id = :id');
        $cs->bindValue(':id', $poet_id, PDO::PARAM_INT);
        $cs->execute();
        $cr = $cs->fetch(PDO::FETCH_ASSOC) ?: [];
        $current_email = (string)($cr['email'] ?? '');
        $current_first = (string)($cr['author_name'] ?? '');
    } catch (Throwable $e) {
        $errs['email'] = 'Could not read current account state.';
    }

    $email_changed = empty($errs['email']) && strcasecmp($email, $current_email) !== 0;

    // Uniqueness check (only if user is proposing a different email)
    if ($email_changed) {
        try {
            $u = $pdo->prepare('SELECT id FROM sm_poets WHERE (email = :email OR pending_email = :email) AND id <> :id LIMIT 1');
            $u->bindValue(':email', $email, PDO::PARAM_STR);
            $u->bindValue(':id',    $poet_id, PDO::PARAM_INT);
            $u->execute();
            if ($u->fetchColumn()) {
                $errs['email'] = 'That email is already in use (or pending) on another account.';
            }
        } catch (Throwable $e) {
            error_log('my-profile email-check: ' . $e->getMessage());
            $errs['email'] = 'Could not verify the email address. Please try again.';
        }
    }

    if (empty($errs)) {
        try {
            // Always update pen + visibility immediately.
            $up = $pdo->prepare('UPDATE sm_poets SET pen_name = :pen, no_authors_page = :no_auth WHERE id = :id');
            $up->bindValue(':pen',     $pen === '' ? null : $pen, $pen === '' ? PDO::PARAM_NULL : PDO::PARAM_STR);
            $up->bindValue(':no_auth', $no_authors_page, PDO::PARAM_INT);
            $up->bindValue(':id',      $poet_id, PDO::PARAM_INT);
            $up->execute();

            $msg_parts = ['Your profile has been updated.'];

            if ($email_changed) {
                // Stash pending email + token; do NOT update email column yet.
                $tok = bin2hex(random_bytes(32));
                $exp = (new DateTimeImmutable("+" . EMAIL_TOKEN_TTL_MIN . " minutes"))->format('Y-m-d H:i:s');
                $pe = $pdo->prepare('UPDATE sm_poets SET pending_email = :pe, email_change_token = :tok, email_change_expiry = :exp WHERE id = :id');
                $pe->bindValue(':pe',  $email, PDO::PARAM_STR);
                $pe->bindValue(':tok', $tok,   PDO::PARAM_STR);
                $pe->bindValue(':exp', $exp,   PDO::PARAM_STR);
                $pe->bindValue(':id',  $poet_id, PDO::PARAM_INT);
                $pe->execute();

                $sent = send_email_change_confirmation($pdo, $email, $current_first, $tok);
                if ($sent) {
                    $msg_parts[] = 'A confirmation link has been sent to ' . $email . '. Click that link within ' . EMAIL_TOKEN_TTL_MIN . ' minutes to complete the change. Until then your sign-in email remains unchanged.';
                } else {
                    // Roll back the pending change so the user isn't stuck.
                    $rb = $pdo->prepare('UPDATE sm_poets SET pending_email = NULL, email_change_token = NULL, email_change_expiry = NULL WHERE id = :id');
                    $rb->bindValue(':id', $poet_id, PDO::PARAM_INT);
                    $rb->execute();
                    $errs['email'] = 'Could not send the confirmation email. The change has not been applied.';
                }
            }

            if (empty($errs)) {
                $_SESSION['profile_csrf'] = bin2hex(random_bytes(32));
                $_SESSION['profile_flash_msg'] = implode(' ', $msg_parts);
            } else {
                $_SESSION['profile_flash_err'] = implode(' ', $errs);
            }
        } catch (Throwable $e) {
            error_log('my-profile update: ' . $e->getMessage());
            $_SESSION['profile_flash_err'] = 'Could not save your profile. Please try again.';
        }
    } else {
        $_SESSION['profile_flash_err'] = implode(' ', $errs);
    }

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

// --- Fetch current values ------------------------------------------
// Resilient against partially-applied migrations: try the full SELECT
// first, fall back to the base columns if pen_name / pending_email /
// email_change_expiry don't exist yet.
$row = [];
$missing_columns = [];
try {
    $stmt = $pdo->prepare('SELECT email, author_name, author_lastname, pen_name, signup_date, no_authors_page, pending_email, email_change_expiry FROM sm_poets WHERE id = :id');
    $stmt->bindValue(':id', $poet_id, PDO::PARAM_INT);
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {
    $err = $e->getMessage();
    error_log('my-profile fetch (full SELECT): ' . $err);
    // Detect which migration columns are missing (best-effort parse)
    foreach (['pen_name', 'pending_email', 'email_change_token', 'email_change_expiry'] as $c) {
        if (stripos($err, "'" . $c . "'") !== false || stripos($err, '`' . $c . '`') !== false) {
            $missing_columns[] = $c;
        }
    }
    try {
        $stmt2 = $pdo->prepare('SELECT email, author_name, author_lastname, signup_date, no_authors_page FROM sm_poets WHERE id = :id');
        $stmt2->bindValue(':id', $poet_id, PDO::PARAM_INT);
        $stmt2->execute();
        $row = $stmt2->fetch(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e2) {
        error_log('my-profile fetch (base SELECT): ' . $e2->getMessage());
    }
}

$cur_email     = (string)($row['email'] ?? '');
$cur_first     = (string)($row['author_name'] ?? '');
$cur_last      = (string)($row['author_lastname'] ?? '');
$cur_pen       = (string)($row['pen_name'] ?? '');
$cur_since     = $row['signup_date'] ?? '';
$cur_include   = empty($row['no_authors_page']);
$pending_email = trim((string)($row['pending_email'] ?? ''));
$pending_exp   = $row['email_change_expiry'] ?? '';
$pending_active = $pending_email !== '' && $pending_exp && strtotime($pending_exp) > time();

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

<div class="container" style="max-width: 640px; padding: 44px 48px 40px;">
  <h2>Your Profile</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; ?>
  <?php if (!empty($missing_columns)): ?>
    <div style="background: #fdf4e8; border-left: 3px solid var(--c-brass); padding: 10px 14px; margin: 0 0 18px; font-size: 13px; color: var(--c-brass-deep);">
      <strong>Database migration pending.</strong>
      The following columns are missing from <code>sm_poets</code>:
      <code><?= htmlspecialchars(implode(', ', $missing_columns), ENT_QUOTES, 'UTF-8') ?></code>.
      Run the v4 and v5 SQL migrations in phpMyAdmin to enable pen names and email-change confirmation.
    </div>
  <?php endif; ?>

  <section style="margin: 16px 0 28px; text-align: left;">
    <h3 style="font-style: italic; color: var(--c-slate); margin: 0 0 12px; font-size: 1.05rem;">Account Information</h3>
    <dl style="display: grid; grid-template-columns: 180px 1fr; gap: 8px 18px; margin: 0; font-size: 15px;">
      <dt style="font-family: var(--sans); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-slate);">Real Name</dt>
      <dd style="margin: 0;"><?= htmlspecialchars(trim($cur_first . ' ' . $cur_last), ENT_QUOTES, 'UTF-8') ?: '—' ?></dd>

      <dt style="font-family: var(--sans); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-slate);">Member Since</dt>
      <dd style="margin: 0;"><?= $cur_since ? htmlspecialchars(date('F j, Y', strtotime($cur_since)), ENT_QUOTES, 'UTF-8') : '—' ?></dd>

      <dt style="font-family: var(--sans); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-slate);">Current Email Address</dt>
      <dd style="margin: 0;"><?= htmlspecialchars($cur_email, ENT_QUOTES, 'UTF-8') ?: '—' ?></dd>
    </dl>

    <div style="margin-top: 16px;">
      <button type="button" id="show-email-change-btn" class="button-secondary" style="font-size: 11px; padding: 8px 18px; letter-spacing: 0.16em;">Change My Email Address</button>
      <p style="margin: 6px 0 0; font-size: 12px; color: var(--c-slate); font-style: italic;">Where PayPal Payments and Notices are sent</p>
    </div>
  </section>

  <?php if ($pending_active): ?>
    <div style="background: #fdf4e8; border: 1px solid #e6c590; border-left: 3px solid var(--c-brass); padding: 14px 18px; margin: 0 0 24px; text-align: left;">
      <div style="font-family: var(--sans); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--c-brass-deep); font-weight: 700;">Pending Email Change</div>
      <p style="margin: 8px 0 4px; font-size: 14px;">A confirmation link was sent to <strong><?= htmlspecialchars($pending_email, ENT_QUOTES, 'UTF-8') ?></strong>. Click the link in that email to complete the change. Expires <?= htmlspecialchars(date('M j, Y g:i a', strtotime($pending_exp)), ENT_QUOTES, 'UTF-8') ?>.</p>
      <form method="post" action="my-profile.php" style="margin: 8px 0 0;">
        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['profile_csrf'], ENT_QUOTES, 'UTF-8') ?>">
        <button type="submit" name="cancel_email_change" value="1" style="padding: 4px 12px; font-size: 11px;">Cancel pending change</button>
      </form>
    </div>
  <?php endif; ?>

  <hr style="border: 0; border-top: 1px solid var(--c-rule); margin: 0 0 24px;">

  <h3 style="font-style: italic; color: var(--c-slate); margin: 0 0 12px; font-size: 1.05rem; text-align: left;">Editable</h3>

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

    <div class="form-group">
      <label for="pen_name">Pen Name <span style="color: var(--c-slate); font-weight: 400; text-transform: none; letter-spacing: 0; font-style: italic;">(optional)</span></label>
      <input id="pen_name" name="pen_name" type="text"
             maxlength="<?= PROFILE_PEN_MAX ?>"
             value="<?= htmlspecialchars($cur_pen, ENT_QUOTES, 'UTF-8') ?>"
             placeholder="Leave blank to publish under your real name">
    </div>

    <div class="form-group" id="email-change-group" hidden>
      <label for="email">New Email Address</label>
      <input id="email" name="email" type="email"
             maxlength="<?= PROFILE_EMAIL_MAX ?>"
             value="<?= htmlspecialchars($cur_email, ENT_QUOTES, 'UTF-8') ?>">
      <p style="margin: 4px 0 0; font-size: 12px; color: var(--c-slate); font-style: italic;">When you click <em>Save Profile</em>, a confirmation link will be sent to the <strong>new</strong> address. Your sign-in email won&rsquo;t change until you click that link.</p>
    </div>
    <!-- Hidden mirror so the form always submits an email value (current by default) -->
    <noscript>
      <div class="form-group">
        <label for="email-fallback">Email Address (JavaScript disabled — edit directly)</label>
        <input id="email-fallback" name="email" type="email" value="<?= htmlspecialchars($cur_email, ENT_QUOTES, 'UTF-8') ?>">
      </div>
    </noscript>

    <div class="form-group" style="background: var(--c-frost); border: 1px solid var(--c-rule); padding: 14px 18px; margin-top: 24px;">
      <label style="display: flex; align-items: flex-start; gap: 12px; margin: 0; text-transform: none; letter-spacing: 0; color: var(--c-ink); font-family: var(--serif); font-size: 15px; font-weight: 400;">
        <input type="checkbox" name="include_in_contribs" value="1" style="margin-top: 4px; width: auto;" <?= $cur_include ? 'checked' : '' ?>>
        <span>
          <strong style="color: var(--c-navy);">List me on the public Contributors page</strong>
          <br><span style="font-size: 12px; color: var(--c-slate); font-style: italic;">Uncheck to hide your name and bio from the directory. Your published work still appears in the journal &mdash; only your contributor entry is suppressed.</span>
        </span>
      </label>
    </div>

    <input type="submit" name="sub_update_profile" value="Save Profile" class="button-primary">
  </form>
</div>

<script>
(function () {
  var btn = document.getElementById('show-email-change-btn');
  var grp = document.getElementById('email-change-group');
  if (!btn || !grp) return;
  btn.addEventListener('click', function () {
    grp.hidden = false;
    grp.scrollIntoView({ behavior: 'smooth', block: 'center' });
    var inp = document.getElementById('email');
    if (inp) { inp.focus(); inp.select(); }
    btn.style.display = 'none';
  });
})();
</script>

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