import type { Member, MemberStatus, Role } from "../domain/types";
import { KEYS } from "../data/mock/keys";
import { newId } from "./id";
import {
  generateTempPassword,
  isPasswordPolicyOk,
  PASSWORD_POLICY_ZH,
} from "./passwordPolicy";
import { readJson, writeJson } from "./storage";
import { nowUnixNano } from "./time";

/** 租戶內會員帳號（含密碼，僅 mock 本機） */
export type TenantUserRecord = {
  uid: string;
  email: string;
  password: string;
  display_name: string;
  roles: Role[];
  /** 帳號狀態：active 正常 · suspended 停權 */
  status: MemberStatus;
  bio?: string;
  timezone?: string;
  notify_email?: boolean;
  /** 會員頭像（mock data URL / https） */
  avatar_url?: string | null;
  email_verified: boolean;
  email_verified_at?: number | null;
  /**
   * 邀請碼（唯一、大寫字母數字）。
   * 分享給下線；日後活動／抽獎可綁此碼。
   */
  invite_code: string;
  /**
   * 上線 uid。null = 根節點（無邀請人）。
   */
  parent_uid: string | null;
  created_at: number;
  updated_at: number;
  onboarding_status?: string;
  onboarding_done_at?: number;
};

/** 管理員列表用（無密碼） */
export type MemberAdminView = {
  uid: string;
  email: string;
  display_name: string;
  roles: Role[];
  status: MemberStatus;
  bio?: string;
  timezone?: string;
  notify_email?: boolean;
  avatar_url?: string | null;
  email_verified: boolean;
  email_verified_at?: number | null;
  invite_code: string;
  parent_uid: string | null;
  created_at: number;
  updated_at: number;
  onboarding_status?: string;
  onboarding_done_at?: number;
};

export type AdminCreateMemberInput = {
  display_name: string;
  email: string;
  /** 未填則產生臨時密碼 */
  password?: string;
  roles?: Role[];
  /** 預設 true（管理者代建通常直接可用） */
  email_verified?: boolean;
  bio?: string;
  /**
   * 上線 uid。未傳 = 掛在建立者下；null = 根節點。
   */
  parent_uid?: string | null;
};

function normalizeStatus(s: unknown): MemberStatus {
  return s === "suspended" ? "suspended" : "active";
}

const DEMO_EMAIL = "demo@harbor.local";
const DEMO_PASSWORD = "demo";

/** 產生唯一邀請碼（mock） */
export function generateInviteCode(seed?: string): string {
  const raw = (seed || newId("inv")).replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
  const body = (raw.slice(-6) || "XXXXXX").padStart(6, "X").slice(0, 6);
  return `HX-${body}`;
}

/**
 * 租戶會員 seed 版本。升高會重灌示範資料（含百人邀請樹）。
 * v2：+100 bulk 島民（多根、深鏈、寬扇出）
 */
export const TENANT_USERS_SEED_VERSION = 2;

const BULK_COUNT = 100;

const BULK_NAMES = [
  "小周",
  "阿城",
  "佩珊",
  "昱廷",
  "佳玲",
  "冠宇",
  "詩涵",
  "彥廷",
  "怡君",
  "志偉",
  "雅婷",
  "柏翰",
  "心怡",
  "俊傑",
  "家豪",
  "筱雯",
  "子晴",
  "宗憲",
  "雨萱",
  "建宏",
  "思妤",
  "承恩",
  "婉婷",
  "皓宇",
  "欣怡",
  "明哲",
  "語彤",
  "宇軒",
  "靜怡",
  "鈞浩",
];

/**
 * Seed 邀請關係（複雜示範）：
 * - 核心：Demo / Alice / Bob / 停權 + 島民 1–12
 * - bulk 100 人：
 *   · ~15 人無邀請人（獨立進來）
 *   · 深鏈、寬扇出、多棵旁支
 *   · 少數停權
 */
function seedUsers(): TenantUserRecord[] {
  const now = nowUnixNano();
  const day = 86_400_000_000_000;
  const hour = 3_600_000_000_000;
  const base: TenantUserRecord[] = [
    {
      uid: "user_demo",
      email: DEMO_EMAIL,
      password: DEMO_PASSWORD,
      display_name: "Demo 島民",
      roles: ["member", "admin"],
      status: "active",
      bio: "系統管理員",
      timezone: "Asia/Taipei",
      notify_email: true,
      email_verified: true,
      email_verified_at: now - day,
      invite_code: "HX-DEMO01",
      parent_uid: null,
      created_at: now - 30 * day,
      updated_at: now,
    },
    {
      uid: "user_alice",
      email: "alice@harbor.local",
      password: "alice",
      display_name: "Alice 編輯",
      roles: ["member"],
      status: "active",
      bio: "一般會員 · 負責內容",
      timezone: "Asia/Taipei",
      notify_email: true,
      email_verified: true,
      email_verified_at: now - 7 * day,
      invite_code: "HX-ALIC01",
      parent_uid: "user_demo",
      created_at: now - 20 * day,
      updated_at: now,
    },
    {
      uid: "user_bob",
      email: "bob@harbor.local",
      password: "bob",
      display_name: "Bob 待驗證",
      roles: ["member"],
      status: "active",
      bio: "新加入，尚未驗證信箱",
      timezone: "Asia/Taipei",
      notify_email: true,
      email_verified: false,
      email_verified_at: null,
      invite_code: "HX-BOB001",
      parent_uid: "user_alice",
      created_at: now - 2 * day,
      updated_at: now,
    },
    {
      uid: "user_suspended_demo",
      email: "suspended@harbor.local",
      password: "suspended",
      display_name: "已停權帳號",
      roles: ["member"],
      status: "suspended",
      bio: "停權中",
      timezone: "Asia/Taipei",
      notify_email: false,
      email_verified: true,
      email_verified_at: now - 10 * day,
      invite_code: "HX-SUSP01",
      parent_uid: "user_demo",
      created_at: now - 15 * day,
      updated_at: now - day,
    },
  ];

  // 固定 12 人：小樹 + 分頁
  const extras: TenantUserRecord[] = Array.from({ length: 12 }, (_, i) => {
    const n = i + 1;
    let parent_uid: string | null = "user_demo";
    if (n === 1 || n === 2) parent_uid = "user_alice";
    else if (n === 3) parent_uid = "user_demo";
    else if (n === 4) parent_uid = "user_extra_3";
    else if (n === 5) parent_uid = "user_extra_4";
    else if (n === 8) parent_uid = null;
    else parent_uid = "user_demo";

    return {
      uid: `user_extra_${n}`,
      email: `user${n}@harbor.local`,
      password: `user${n}`,
      display_name: `島民 ${n}`,
      roles: ["member"] as Role[],
      status: (n === 7 ? "suspended" : "active") as MemberStatus,
      bio: `分頁測試帳 #${n}`,
      timezone: "Asia/Taipei",
      notify_email: n % 2 === 0,
      email_verified: n % 3 !== 0,
      email_verified_at: n % 3 !== 0 ? now - n * day : null,
      invite_code: `HX-U${String(n).padStart(4, "0")}`,
      parent_uid,
      created_at: now - (n + 3) * day,
      updated_at: now - n * hour,
    };
  });

  // bulk uid 表（先建好再決定 parent，避免前向引用）
  const bulkUids = Array.from(
    { length: BULK_COUNT },
    (_, i) => `user_bulk_${String(i + 1).padStart(3, "0")}`,
  );

  /**
   * 決定 bulk[i] 的邀請人（index 0-based）
   * 0–14  ：無邀請人（獨立加入）
   * 15–24 ：掛 Demo
   * 25–29 ：掛 Alice
   * 30–34 ：掛 Bob
   * 35–49 ：深鏈（一人邀一人，約 15 層）
   * 50–64 ：寬扇（掛在獨立根 bulk_001 下，15 人）
   * 65–74 ：掛 bulk_051 下（第二層）
   * 75–84 ：掛獨立根 bulk_002
   * 85–92 ：掛島民 8（另一獨立根旁支）
   * 93–99 ：交錯掛在較早 bulk，製造複雜分叉
   */
  function bulkParent(i: number): string | null {
    if (i < 15) return null;
    if (i < 25) return "user_demo";
    if (i < 30) return "user_alice";
    if (i < 35) return "user_bob";
    if (i < 50) {
      // 深鏈：35 接 user_extra_5，其後每人接上一個
      if (i === 35) return "user_extra_5";
      return bulkUids[i - 1];
    }
    if (i < 65) return bulkUids[0]; // 寬扇：獨立根 001
    if (i < 75) return bulkUids[50]; // 掛在 051
    if (i < 85) return bulkUids[1]; // 獨立根 002 的樹
    if (i < 93) return "user_extra_8"; // 旁支獨立根
    // 交錯：掛在 0–49 之間，略避開自指
    const p = (i * 13 + 7) % 50;
    return bulkUids[p === i ? (p + 1) % 50 : p];
  }

  const bulk: TenantUserRecord[] = bulkUids.map((uid, i) => {
    const n = i + 1;
    const nameBase = BULK_NAMES[i % BULK_NAMES.length];
    const display_name = `${nameBase}·${String(n).padStart(3, "0")}`;
    const parent_uid = bulkParent(i);
    const isRoot = !parent_uid;
    return {
      uid,
      email: `bulk${n}@harbor.local`,
      password: `bulk${n}`,
      display_name,
      roles: ["member"] as Role[],
      status: (n % 29 === 0 ? "suspended" : "active") as MemberStatus,
      bio: isRoot
        ? `獨立加入 · bulk #${n}`
        : `邀請加入 · bulk #${n}`,
      timezone: "Asia/Taipei",
      notify_email: n % 2 === 0,
      email_verified: n % 4 !== 0,
      email_verified_at: n % 4 !== 0 ? now - n * hour * 3 : null,
      invite_code: `HX-B${String(n).padStart(4, "0")}`,
      parent_uid,
      created_at: now - (n + 20) * hour * 5,
      updated_at: now - n * hour,
    };
  });

  return [...base, ...extras, ...bulk];
}

function migrateUserRecord(raw: TenantUserRecord & { status?: MemberStatus }): TenantUserRecord {
  return {
    ...raw,
    status: normalizeStatus(raw.status),
    email_verified: raw.email_verified === true,
    invite_code: (raw.invite_code || "").trim() || generateInviteCode(raw.uid),
    parent_uid: raw.parent_uid === undefined ? null : raw.parent_uid?.trim() || null,
  };
}

/** 為缺 invite 欄位的舊資料補碼；必要時重播 seed 樹結構 */
function ensureInviteFields(list: TenantUserRecord[]): {
  list: TenantUserRecord[];
  dirty: boolean;
} {
  const used = new Set<string>();
  let dirty = false;
  const next = list.map((u) => {
    let invite_code = (u.invite_code || "").trim().toUpperCase();
    if (!invite_code || used.has(invite_code)) {
      invite_code = generateInviteCode(u.uid + invite_code);
      // 避免碰撞
      let n = 0;
      while (used.has(invite_code)) {
        n += 1;
        invite_code = generateInviteCode(`${u.uid}_${n}`);
      }
      dirty = true;
    }
    used.add(invite_code);
    const parent_uid =
      u.parent_uid === undefined ? null : u.parent_uid?.trim() || null;
    if (u.invite_code !== invite_code || u.parent_uid !== parent_uid) dirty = true;
    return { ...u, invite_code, parent_uid };
  });

  // 若全員都沒 parent（舊 seed），套用示範樹拓撲
  const hasAnyParent = next.some((u) => Boolean(u.parent_uid));
  if (!hasAnyParent && next.length > 1) {
    const seed = seedUsers();
    const seedParent = new Map(seed.map((s) => [s.uid, s.parent_uid]));
    const seedCode = new Map(seed.map((s) => [s.uid, s.invite_code]));
    for (let i = 0; i < next.length; i++) {
      const uid = next[i].uid;
      if (seedParent.has(uid)) {
        next[i] = {
          ...next[i],
          parent_uid: seedParent.get(uid) ?? null,
          invite_code: seedCode.get(uid) || next[i].invite_code,
        };
        dirty = true;
      }
    }
  }

  return { list: next, dirty };
}

/** 讀目前 seed 版本（舊資料 = 0） */
function readTenantSeedVer(): number {
  const v = readJson<number | null>(KEYS.tenantUsersSeedVer, null);
  if (typeof v === "number" && Number.isFinite(v)) return v;
  return 0;
}

/**
 * 重灌 seed：保留已知帳號密碼／驗證狀態（demo、alice…），其餘用新樹。
 */
function reseedTenantUsers(prev: TenantUserRecord[]): TenantUserRecord[] {
  const byEmail = new Map(prev.map((u) => [u.email.toLowerCase(), u]));
  const next = seedUsers().map((u) => {
    const old = byEmail.get(u.email.toLowerCase());
    if (!old) return u;
    return {
      ...u,
      password: old.password || u.password,
      // 保留頭像等使用者改過的欄位
      avatar_url: old.avatar_url !== undefined ? old.avatar_url : u.avatar_url,
      display_name: old.display_name || u.display_name,
      bio: old.bio !== undefined ? old.bio : u.bio,
      timezone: old.timezone || u.timezone,
      notify_email: old.notify_email ?? u.notify_email,
      // 邀請拓撲以 seed 為準（為了示範樹）
      invite_code: u.invite_code,
      parent_uid: u.parent_uid,
      roles: u.roles.length ? u.roles : old.roles,
      status: u.status,
      email_verified: old.email_verified ?? u.email_verified,
      email_verified_at: old.email_verified_at ?? u.email_verified_at,
    };
  });
  saveTenantUsers(next);
  writeJson(KEYS.tenantUsersSeedVer, TENANT_USERS_SEED_VERSION);
  return next;
}

export function loadTenantUsers(): TenantUserRecord[] {
  let list = readJson(KEYS.tenantUsers, [] as TenantUserRecord[]);
  const seedVer = readTenantSeedVer();

  if (!list.length) {
    list = seedUsers();
    saveTenantUsers(list);
    writeJson(KEYS.tenantUsersSeedVer, TENANT_USERS_SEED_VERSION);
    return list;
  }

  // seed 版本落後 → 重灌百人示範樹（保留已知帳密）
  if (seedVer < TENANT_USERS_SEED_VERSION) {
    return reseedTenantUsers(list);
  }

  // 舊資料補 status / invite
  let dirty = false;
  list = list.map((u) => {
    if (
      (u.status === "active" || u.status === "suspended") &&
      (u.invite_code || "").trim() &&
      u.parent_uid !== undefined
    ) {
      return u;
    }
    dirty = true;
    return migrateUserRecord(u);
  });
  const ensured = ensureInviteFields(list);
  list = ensured.list;
  if (ensured.dirty) dirty = true;
  // 確保 demo 永遠在
  if (!list.some((u) => u.email.toLowerCase() === DEMO_EMAIL)) {
    list = [...seedUsers().filter((u) => u.email === DEMO_EMAIL), ...list];
    dirty = true;
  }
  if (dirty) saveTenantUsers(list);
  return list;
}

export function saveTenantUsers(list: TenantUserRecord[]): void {
  writeJson(KEYS.tenantUsers, list);
}

export function findUserByEmail(email: string): TenantUserRecord | null {
  const e = email.trim().toLowerCase();
  return loadTenantUsers().find((u) => u.email.toLowerCase() === e) || null;
}

export function findUserByUid(uid: string): TenantUserRecord | null {
  return loadTenantUsers().find((u) => u.uid === uid) || null;
}

export function upsertTenantUser(user: TenantUserRecord): TenantUserRecord {
  const list = loadTenantUsers();
  const i = list.findIndex((u) => u.uid === user.uid);
  const next = { ...user, updated_at: nowUnixNano() };
  if (i >= 0) list[i] = next;
  else list.push(next);
  saveTenantUsers(list);
  return next;
}

export function toMember(u: TenantUserRecord): Member {
  return {
    tenant_id: "default",
    uid: u.uid,
    email: u.email,
    display_name: u.display_name,
    roles: u.roles,
    status: normalizeStatus(u.status),
    bio: u.bio || "",
    timezone: u.timezone || "Asia/Taipei",
    notify_email: u.notify_email ?? true,
    avatar_url: u.avatar_url?.trim() || null,
    email_verified: u.email_verified === true,
    email_verified_at: u.email_verified_at ?? null,
    invite_code: (u.invite_code || "").trim() || undefined,
    parent_uid: u.parent_uid?.trim() || null,
  };
}

export function toAdminView(u: TenantUserRecord): MemberAdminView {
  return {
    uid: u.uid,
    email: u.email,
    display_name: u.display_name,
    roles: u.roles,
    status: normalizeStatus(u.status),
    bio: u.bio,
    timezone: u.timezone,
    notify_email: u.notify_email,
    avatar_url: u.avatar_url?.trim() || null,
    email_verified: u.email_verified === true,
    email_verified_at: u.email_verified_at ?? null,
    invite_code: (u.invite_code || "").trim() || generateInviteCode(u.uid),
    parent_uid: u.parent_uid?.trim() || null,
    created_at: u.created_at,
    updated_at: u.updated_at,
    onboarding_status: u.onboarding_status,
    onboarding_done_at: u.onboarding_done_at,
  };
}

export function isMemberSuspended(u: Pick<TenantUserRecord, "status"> | Pick<Member, "status"> | null | undefined): boolean {
  return normalizeStatus(u?.status) === "suspended";
}

export function listAdminViews(): MemberAdminView[] {
  return loadTenantUsers()
    .map(toAdminView)
    .sort((a, b) => b.updated_at - a.updated_at);
}

export function requireAdminSession(sessionMember: Member | null): Member {
  if (!sessionMember) throw new Error("尚未登入");
  if (!sessionMember.roles.includes("admin")) throw new Error("需要管理員權限");
  return sessionMember;
}

export function adminSetEmailVerified(
  actor: Member,
  uid: string,
  verified: boolean,
): MemberAdminView {
  requireAdminSession(actor);
  const u = findUserByUid(uid);
  if (!u) throw new Error("找不到使用者");
  const now = nowUnixNano();
  const next = upsertTenantUser({
    ...u,
    email_verified: verified,
    email_verified_at: verified ? now : null,
  });
  return toAdminView(next);
}

export function adminResetPassword(
  actor: Member,
  uid: string,
  newPassword?: string,
): { user: MemberAdminView; temporary_password: string } {
  requireAdminSession(actor);
  const u = findUserByUid(uid);
  if (!u) throw new Error("找不到使用者");
  const temporary_password = (newPassword || "").trim() || generateTempPassword();
  if (!isPasswordPolicyOk(temporary_password)) {
    throw new Error(PASSWORD_POLICY_ZH);
  }
  const next = upsertTenantUser({ ...u, password: temporary_password });
  return { user: toAdminView(next), temporary_password };
}

/** 管理員指派角色（member / admin） */
export function adminSetRoles(
  actor: Member,
  uid: string,
  roles: Role[],
): MemberAdminView {
  requireAdminSession(actor);
  const u = findUserByUid(uid);
  if (!u) throw new Error("找不到島民");

  const normalized = normalizeRoles(roles);
  if (!normalized.length) throw new Error("至少保留一個角色（一般會員）");

  // 不可拔掉系統最後一位「在用」管理員
  const wasAdmin = u.roles.includes("admin");
  const willBeAdmin = normalized.includes("admin");
  if (wasAdmin && !willBeAdmin) {
    ensureAnotherActiveAdmin(uid);
  }

  const next = upsertTenantUser({ ...u, roles: normalized });
  return toAdminView(next);
}

/** 管理者新增島民（可回傳臨時密碼） */
export function adminCreateMember(
  actor: Member,
  input: AdminCreateMemberInput,
): { user: MemberAdminView; temporary_password: string } {
  requireAdminSession(actor);
  const display_name = (input.display_name || "").trim();
  if (!display_name) throw new Error("請輸入島民名稱");
  if (display_name.length > 40) throw new Error("名稱請在 40 字內");

  const email = (input.email || "").trim().toLowerCase();
  if (!email || !email.includes("@")) throw new Error("Email 格式不正確");
  if (findUserByEmail(email)) throw new Error("此 Email 已被使用");

  const temporary_password = (input.password || "").trim() || generateTempPassword();
  if (!isPasswordPolicyOk(temporary_password)) {
    throw new Error(PASSWORD_POLICY_ZH);
  }

  const now = nowUnixNano();
  const verified = input.email_verified !== false;
  const parent: string | null =
    input.parent_uid === undefined ? actor.uid : input.parent_uid?.trim() || null;
  if (parent && !findUserByUid(parent)) {
    throw new Error("invite.err.parentNotFound");
  }
  // 新島民預設掛在建立者下（方便示範邀請樹）
  const rec: TenantUserRecord = {
    uid: newId("user"),
    email,
    password: temporary_password,
    display_name,
    roles: normalizeRoles(input.roles || ["member"]),
    status: "active",
    bio: (input.bio || "").trim().slice(0, 200),
    timezone: "Asia/Taipei",
    notify_email: true,
    email_verified: verified,
    email_verified_at: verified ? now : null,
    invite_code: generateInviteCode(email),
    parent_uid: parent,
    created_at: now,
    updated_at: now,
  };
  // 確保邀請碼不重複
  const existingCodes = new Set(loadTenantUsers().map((u) => u.invite_code));
  let guard = 0;
  while (existingCodes.has(rec.invite_code) && guard < 8) {
    rec.invite_code = generateInviteCode(`${email}_${guard}`);
    guard += 1;
  }
  const next = upsertTenantUser(rec);
  return { user: toAdminView(next), temporary_password };
}

/**
 * 停權／復權。
 * - 不可停自己
 * - 停權管理員時，需另有至少一位在用管理員
 */
export function adminSetSuspended(
  actor: Member,
  uid: string,
  suspended: boolean,
): MemberAdminView {
  requireAdminSession(actor);
  const u = findUserByUid(uid);
  if (!u) throw new Error("找不到島民");
  if (actor.uid === uid && suspended) {
    throw new Error("不能停權自己的帳號");
  }

  const nextStatus: MemberStatus = suspended ? "suspended" : "active";
  if (normalizeStatus(u.status) === nextStatus) {
    return toAdminView(u);
  }

  if (suspended && u.roles.includes("admin")) {
    ensureAnotherActiveAdmin(uid);
  }

  const next = upsertTenantUser({ ...u, status: nextStatus });
  return toAdminView(next);
}

/** 除了 targetUid 以外，是否還有 active 管理員 */
function ensureAnotherActiveAdmin(excludeUid: string): void {
  const other = loadTenantUsers().filter(
    (x) =>
      x.uid !== excludeUid &&
      x.roles.includes("admin") &&
      normalizeStatus(x.status) === "active",
  );
  if (other.length === 0) {
    throw new Error("系統至少需要一位在用的管理員，無法停權或降級最後一位");
  }
}

function normalizeRoles(roles: Role[]): Role[] {
  const set = new Set<Role>();
  for (const r of roles) {
    if (r === "admin" || r === "member") set.add(r);
  }
  // 有 admin 時仍保留 member 標籤，方便顯示「也是會員」
  if (set.has("admin") && !set.has("member")) set.add("member");
  if (!set.size) set.add("member");
  return [...set];
}

export type AdminUserListPage = {
  list: MemberAdminView[];
  page: number;
  pageSize: number;
  total: number;
  totalPages: number;
};

/** 分頁列表（page 從 1 起；query 可搜名稱 / Email / uid） */
export function listAdminViewsPage(
  page = 1,
  pageSize = 10,
  query = "",
): AdminUserListPage {
  const q = query.trim().toLowerCase();
  let all = listAdminViews();
  if (q) {
    all = all.filter((u) => {
      const name = u.display_name.toLowerCase();
      const email = u.email.toLowerCase();
      const uid = u.uid.toLowerCase();
      // 完整 uid 或前綴、名稱、email
      return (
        name.includes(q) ||
        email.includes(q) ||
        uid.includes(q) ||
        uid.replace(/^user_/, "").includes(q)
      );
    });
  }
  const total = all.length;
  const size = Math.max(1, Math.min(50, pageSize || 10));
  const totalPages = Math.max(1, Math.ceil(total / size));
  const p = Math.max(1, Math.min(totalPages, page || 1));
  const start = (p - 1) * size;
  return {
    list: all.slice(start, start + size),
    page: p,
    pageSize: size,
    total,
    totalPages,
  };
}

/** 同步舊 single-user credentials 進租戶表（一次性相容） */
export function migrateLegacyCredentials(): void {
  const list = loadTenantUsers();
  const legacy = readJson<{ email: string; password: string } | null>(
    KEYS.memberCredentials,
    null,
  );
  const profile = readJson<Partial<Member> | null>(KEYS.memberProfile, null);
  if (!legacy?.email) return;
  const email = legacy.email.toLowerCase();
  const existing = list.find((u) => u.email.toLowerCase() === email);
  if (existing) {
    // 沿用密碼／驗證狀態
    upsertTenantUser({
      ...existing,
      password: legacy.password || existing.password,
      display_name: profile?.display_name || existing.display_name,
      bio: profile?.bio ?? existing.bio,
      timezone: profile?.timezone || existing.timezone,
      notify_email: profile?.notify_email ?? existing.notify_email,
      email_verified:
        profile?.email_verified !== undefined
          ? profile.email_verified === true
          : existing.email_verified,
      email_verified_at:
        profile?.email_verified_at !== undefined
          ? profile.email_verified_at
          : existing.email_verified_at,
    });
  }
}
