/* ========================================================================
   users-store.jsx — the writable users store

   Before this file, every user-mutating action in the prototype was stubbed
   independently: create, CSV import, edit, delete, and appending a newly
   bought SIM to a person all had nowhere to write, so each one silently did
   nothing. This is the single place that owns those writes.

   It mutates window.MILO_DATA.USERS in place, so every existing read path
   (DD.USERS.find, u.sim_ids, the pickers, the tables) keeps working without
   modification. A delta of the changes is persisted and replayed on load, so
   a refresh keeps what the demo did without serialising the whole estate.
   ======================================================================== */
const KEY = "mp_users_v1";
const UST = window.MILO_DATA;

/* The delta: what this demo session has changed, on top of the seed. */
let DELTA = { created: [], edits: {}, deleted: [], deletedEmails: [], attached: {}, detached: {}, seq: 0 };
let subs = [];
let version = 0;
const notify = () => { version += 1; subs.forEach((f) => f()); };

function save() {
  try { localStorage.setItem(KEY, JSON.stringify(DELTA)); } catch (e) {}
}

const initialsOf = (n) => {
  const p = String(n).trim().split(/\s+/);
  if (!p[0]) return "?";
  return ((p[0][0] || "") + (p.length > 1 ? p[p.length - 1][0] : "")).toUpperCase();
};

/* The seed email of every user, captured before any delta is applied. A
   deletion has to hold against the admin merge even if the address was
   edited first, so both the seed and current values are recorded. */
const SEED_EMAIL = {};
UST.USERS.forEach((u) => { SEED_EMAIL[u.id] = String(u.email || "").toLowerCase(); });

/* ---------- Applying the delta to the live arrays ---------- */
function apply() {
  const U = UST.USERS;
  DELTA.created.forEach((c) => {
    if (U.some((u) => u.id === c.id)) return;
    U.push({ id: c.id, full_name: c.full_name, email: c.email, initials: initialsOf(c.full_name), sim_ids: [] });
  });
  Object.keys(DELTA.edits).forEach((id) => {
    const u = U.find((x) => String(x.id) === String(id));
    if (!u) return;
    const e = DELTA.edits[id];
    if (e.full_name) { u.full_name = e.full_name; u.initials = initialsOf(e.full_name); }
    if (e.email) u.email = e.email;
  });
  Object.keys(DELTA.attached).forEach((id) => {
    const u = U.find((x) => String(x.id) === String(id));
    if (!u) return;
    DELTA.attached[id].forEach((sid) => { if (u.sim_ids.indexOf(sid) === -1) u.sim_ids.push(sid); });
  });
  Object.keys(DELTA.detached).forEach((id) => {
    const u = U.find((x) => String(x.id) === String(id));
    if (!u) return;
    u.sim_ids = u.sim_ids.filter((sid) => DELTA.detached[id].indexOf(sid) === -1);
  });
  DELTA.deleted.forEach((id) => {
    const i = U.findIndex((u) => String(u.id) === String(id));
    if (i === -1) return;
    /* Backfill the email for deltas saved before it was recorded, so an
       earlier deletion still holds against the admin merge. */
    [String(U[i].email || "").toLowerCase(), SEED_EMAIL[U[i].id]]
      .forEach((em) => { if (em && DELTA.deletedEmails.indexOf(em) === -1) DELTA.deletedEmails.push(em); });
    U.splice(i, 1);
  });
}

(function load() {
  try {
    const raw = localStorage.getItem(KEY);
    if (raw) {
      const p = JSON.parse(raw);
      DELTA = {
        created: p.created || [], edits: p.edits || {}, deleted: p.deleted || [],
        deletedEmails: p.deletedEmails || [], attached: p.attached || {},
        detached: p.detached || {}, seq: p.seq || 0
      };
    }
  } catch (e) {}
  apply();
})();

/* ---------- Writes ---------- */

/* A new user. Standard user, no platform access, no SIMs — assigning one is
   a separate journey by design. */
function createUser(fullName, email) {
  DELTA.seq += 1;
  const id = 9000 + DELTA.seq;
  const rec = { id, full_name: String(fullName).trim(), email: String(email).trim().toLowerCase() };
  DELTA.created.push(rec);
  UST.USERS.push({ id, full_name: rec.full_name, email: rec.email, initials: initialsOf(rec.full_name), sim_ids: [] });
  save(); notify();
  return UST.USERS[UST.USERS.length - 1];
}

/* Several at once, for the CSV import. Rows already on the list are skipped,
   which is what the import's warnings bucket promises. */
function createUsers(rows) {
  const made = [];
  rows.forEach((r) => {
    const email = String(r.email || "").trim().toLowerCase();
    if (!email) return;
    if (UST.USERS.some((u) => String(u.email).toLowerCase() === email)) return;
    made.push(createUser(r.full_name || r.name || email.split("@")[0], email));
  });
  return made;
}

function updateUser(id, fields) {
  const u = UST.USERS.find((x) => String(x.id) === String(id));
  if (!u) return null;
  const wasEmail = u.email;
  DELTA.edits[id] = Object.assign({}, DELTA.edits[id], fields);
  if (fields.full_name) { u.full_name = fields.full_name; u.initials = initialsOf(fields.full_name); }
  if (fields.email) u.email = fields.email;
  if (window.MP_SYNC_MEMBER) window.MP_SYNC_MEMBER(wasEmail, {
    name: fields.full_name, email: fields.email,
    initials: fields.full_name ? initialsOf(fields.full_name) : undefined
  });
  save(); notify();
  return u;
}

function deleteUser(id) {
  const i = UST.USERS.findIndex((u) => String(u.id) === String(id));
  if (i === -1) return false;
  if (DELTA.deleted.indexOf(id) === -1) DELTA.deleted.push(id);
  /* The email as well as the id: the user list merges platform admins in by
     email, so an id alone lets a deleted person reappear from that side. */
  [String(UST.USERS[i].email || "").toLowerCase(), SEED_EMAIL[UST.USERS[i].id]]
    .forEach((em) => { if (em && DELTA.deletedEmails.indexOf(em) === -1) DELTA.deletedEmails.push(em); });
  /* A user created in this session just disappears from the delta. */
  DELTA.created = DELTA.created.filter((c) => String(c.id) !== String(id));
  UST.USERS.splice(i, 1);
  save(); notify();
  return true;
}

/* The write that was missing behind "the new travel eSIM is not on the
   profile": a bought or assigned SIM has to land in the person's sim_ids. */
function attachSim(userId, simId) {
  const u = UST.USERS.find((x) => String(x.id) === String(userId));
  if (!u) return false;
  const sid = Number(simId);
  if (u.sim_ids.indexOf(sid) !== -1) return true;
  u.sim_ids.push(sid);
  DELTA.attached[userId] = (DELTA.attached[userId] || []).concat([sid]);
  if (DELTA.detached[userId]) DELTA.detached[userId] = DELTA.detached[userId].filter((x) => x !== sid);
  save(); notify();
  return true;
}

function detachSim(userId, simId) {
  const u = UST.USERS.find((x) => String(x.id) === String(userId));
  if (!u) return false;
  const sid = Number(simId);
  u.sim_ids = u.sim_ids.filter((x) => x !== sid);
  DELTA.detached[userId] = (DELTA.detached[userId] || []).concat([sid]);
  if (DELTA.attached[userId]) DELTA.attached[userId] = DELTA.attached[userId].filter((x) => x !== sid);
  save(); notify();
  return true;
}

const userOf = (simId) => UST.USERS.find((u) => u.sim_ids.indexOf(Number(simId)) !== -1) || null;
const emailTaken = (email, exceptId) => UST.USERS.some((u) =>
  String(u.email).toLowerCase() === String(email).trim().toLowerCase() && String(u.id) !== String(exceptId));

/* Components subscribe so a write anywhere re-renders every list showing it. */
function useUsers() {
  const [v, force] = React.useState(version);
  React.useEffect(() => {
    const f = () => force(version);
    subs.push(f);
    return () => { subs = subs.filter((x) => x !== f); };
  }, []);
  return v;
}

const deletedEmails = () => new Set(DELTA.deletedEmails);

window.MP_USERS = {
  createUser, createUsers, updateUser, deleteUser, deletedEmails,
  attachSim, detachSim, userOf, emailTaken, useUsers, initialsOf,
  all: () => UST.USERS.slice()
};
/* Called unconditionally by every list, so it must exist even if this file
   somehow loads late — a conditional hook call would change the hook count. */
window.MP_USE_USERS = useUsers;
