#!/usr/bin/env bash
##
## backfill-bichon-usernames.sh — migrate existing archival-enabled mailboxes to
## the bounded, uniform Bichon login username + populate email_accounts.bichon_username.
##
## Bichon caps usernames at 32 chars; WHP used to set the Bichon login username
## to the raw email address (broke enablement for addresses >32 chars). The new
## scheme derives "<local-part-trunc>-<sha1(email)[:8]>" (<=32, globally unique,
## deterministic). This renames existing Bichon users to that scheme via the
## Bichon API (UserUpdateRequest accepts a username change without a password)
## and records it locally.
##
## SELF-CONTAINED on purpose: update.sh runs update scripts BEFORE rsyncing
## web-files, so the new libs/emailmgmt.php (with deriveBichonUsername) is not in
## /docker/whp/web yet. We therefore inline the derivation and rely only on
## already-deployed libs (config.php, BichonClient, mysqlmgmt) + the column added
## by this version's migration. Idempotent: rows already on the target username
## are skipped.
##
## NON-FATAL by design: a reconciliation backfill must never abort an update.
## Every failure mode (invalid/expired Bichon token -> HTTP 401, Bichon
## unreachable, a request that throws, etc.) is caught and logged; the script
## always exits 0. (whp02/whp01 succeeded; a test host with a stale token must
## not block its own update.)
set -uo pipefail

php <<'PHP' || true
<?php
try {
    require_once('/docker/whp/web/configs/config.php');
    require_once('/docker/whp/web/libs/BichonClient.php');
    require_once('/docker/whp/web/libs/mysqlmgmt.php');

    if (!BichonClient::isConfigured()) {
        fwrite(STDERR, "Bichon not configured on this host; nothing to backfill.\n");
        exit(0);
    }

    // Mirror of emailmgmt::deriveBichonUsername (kept in sync deliberately; see
    // the self-contained note above for why this can't require the new lib).
    $derive = function (string $email): string {
        $email = strtolower(trim($email));
        $local = explode('@', $email)[0];
        $local = preg_replace('/[^a-z0-9._+-]/', '', $local);
        if ($local === '') { $local = 'mbox'; }
        $hash  = substr(sha1($email), 0, 8);
        $local = substr($local, 0, 32 - 1 - strlen($hash));
        return $local . '-' . $hash;
    };

    $db = (new mysqlmgmt())->getMySQLConnection();
    $bc = new BichonClient();

    $rows = $db->query(
        "SELECT id, email_address, bichon_user_id, bichon_username
         FROM whp.email_accounts
         WHERE archival_enabled = 1 AND bichon_user_id IS NOT NULL"
    )->fetchAll(PDO::FETCH_ASSOC);

    $renamed = 0; $okAlready = 0; $failed = 0;
    foreach ($rows as $r) {
        try {
            $want = $derive($r['email_address']);
            if (($r['bichon_username'] ?? '') === $want) { $okAlready++; continue; }

            $u = $bc->updateUser((string)$r['bichon_user_id'], ['username' => $want]);
            if (!empty($u['ok'])) {
                $db->prepare("UPDATE whp.email_accounts SET bichon_username = ? WHERE id = ?")
                   ->execute([$want, $r['id']]);
                echo "  renamed email_id={$r['id']} ({$r['email_address']}) -> {$want}\n";
                $renamed++;
            } else {
                fwrite(STDERR, "  WARN email_id={$r['id']}: Bichon updateUser failed: " . substr((string)($u['msg'] ?? 'unknown'), 0, 160) . "\n");
                $failed++;
            }
        } catch (\Throwable $e) {
            fwrite(STDERR, "  WARN email_id={$r['id']}: " . $e->getMessage() . "\n");
            $failed++;
        }
    }
    echo "bichon-username backfill: {$renamed} renamed, {$okAlready} already-correct, {$failed} failed, " . count($rows) . " total.\n";
} catch (\Throwable $e) {
    fwrite(STDERR, "bichon-username backfill skipped (non-fatal): " . $e->getMessage() . "\n");
}
exit(0);
PHP

exit 0
