/* ========================================================================
   roles-data.jsx — Roles, members, capability model, nav
   Loaded AFTER data.jsx so it can extend window.MILO_DATA.
   Audit is deferred (design delta §4.1) — see roles-audit-deferred.jsx.
   ======================================================================== */

/* ---------- Role definitions (highest → lowest privilege) ---------- */
const ROLES = {
  owner: {
    key: "owner", label: "Account Owner", rank: 2, tone: "role-owner", icon: "shield-check",
    short: "Full access, plus permanent ownership of the account.",
    long: "Complete access to every area of the platform, including user management, billing, SIM management and your organisation's identity details. Ownership can only be transferred by Meaningful Planet support."
  },
  admin: {
    key: "admin", label: "Admin", rank: 1, tone: "role-admin", icon: "sliders-horizontal",
    short: "Full platform access, including user management, billing and SIMs.",
    long: "Full platform access, including user management, billing, payments and SIM management. Cannot modify the Account Owner, and cannot change your organisation's identity details."
  },
  analyst: {
    key: "analyst", label: "Financial Analyst", rank: 0, tone: "role-analyst", icon: "chart-column",
    short: "Views and exports usage, spend, invoice and emissions data.",
    long: "Can view and export usage, spend, invoice and emissions. It cannot make any changes."
  }
};
const ROLE_ORDER = ["owner", "admin", "analyst"];

/* ---------- Capabilities per role ---------- */
/* org.config  = the Organisation page and its billing/payments zone (AO + Admin)
   org.identity = organisation identity fields — name, address, contact,
                  integrations. Account Owner only. The single place the
                  read-only pattern is used (design delta §3, §6.1). */
const CAPS = {
  owner:   ["users.view", "users.invite", "users.records.manage", "billing.invoices", "billing.config", "rules.manage", "tags.manage", "alerts.config", "sim.manage", "sim.order", "data.view", "data.export", "org.config", "org.identity"],
  admin:   ["users.view", "users.invite", "users.records.manage", "billing.invoices", "billing.config", "rules.manage", "tags.manage", "alerts.config", "sim.manage", "sim.order", "data.view", "data.export", "org.config"],
  analyst: ["billing.invoices", "data.view", "data.export"]
};
function roleCan(roleKey, cap) {
  if (!cap) return true;
  return (CAPS[roleKey] || []).indexOf(cap) !== -1;
}

/* ---------- Who can manage whom ---------- */
function canManageMember(actorRole, target, actorId) {
  if (!target || target.role === "owner") return false;      // Owner is always protected
  if (actorId != null && target.id === actorId) return false; // Nobody manages their own access
  return actorRole === "owner" || actorRole === "admin";
}
/* Roles an actor may assign (at invite time or when changing a role) */
function assignableRoles(actorRole) {
  if (actorRole === "owner" || actorRole === "admin") return ["admin", "analyst"];
  return [];
}
/* Why management is unavailable — used for the read-only explanation */
function restrictionNote(actorRole, target, actorId) {
  if (!target) return null;
  if (actorId != null && target.id === actorId && target.role !== "owner") {
    return "You can't change your own role or remove your own access. Ask another Admin or your Account Owner if this needs to change.";
  }
  if (target.role === "owner") {
    return actorRole === "owner"
      ? "Your own account ownership cannot be changed in the platform. Contact Meaningful Planet support to transfer ownership."
      : "The Account Owner cannot be edited or removed in the platform. Contact Meaningful Planet support if ownership needs to be transferred.";
  }
  return null;
}

/* ---------- Organisation members ---------- */
const MEMBERS = [
  { id: 1,  name: "Lewis Mangrove",  email: "lewis@acmeco.co.uk",   initials: "LM", role: "owner",   status: "active",  joined: "2024-03-04", last_active: "2026-08-03T09:12:00Z" },
  { id: 2,  name: "Priya Shah",      email: "priya@acmeco.co.uk",   initials: "PS", role: "admin",   status: "active",  joined: "2024-05-19", last_active: "2026-08-03T08:41:00Z" },
  { id: 3,  name: "Sarah Chen",      email: "sarah@acmeco.co.uk",   initials: "SC", role: "admin",   status: "active",  joined: "2024-06-02", last_active: "2026-08-01T16:20:00Z" },
  { id: 4,  name: "Tom Williams",    email: "tom@acmeco.co.uk",     initials: "TW", role: "admin",   status: "active",  joined: "2024-09-11", last_active: "2026-08-03T07:55:00Z" },
  { id: 5,  name: "Maya Rossi",      email: "maya@acmeco.co.uk",    initials: "MR", role: "admin",   status: "active",  joined: "2025-01-27", last_active: "2026-07-30T11:02:00Z" },
  { id: 6,  name: "Daniel O'Connor", email: "daniel@acmeco.co.uk",  initials: "DO", role: "admin",   status: "pending", invited: "2026-08-06T02:15:00Z", invited_by: "Lewis Mangrove" },
  { id: 7,  name: "Iris Lin",        email: "iris@acmeco.co.uk",    initials: "IL", role: "analyst", status: "active",  joined: "2025-03-14", last_active: "2026-08-02T18:33:00Z" },
  { id: 8,  name: "Jay Patel",       email: "jay@acmeco.co.uk",     initials: "JP", role: "analyst", status: "active",  joined: "2025-04-08", last_active: "2026-07-28T09:48:00Z" },
  { id: 9,  name: "Helena Brandt",   email: "helena@acmeco.co.uk",  initials: "HB", role: "analyst", status: "active",  joined: "2025-06-23", last_active: "2026-06-19T14:07:00Z" },
  { id: 10, name: "Marcus Webb",     email: "marcus@acmeco.co.uk",  initials: "MW", role: "analyst", status: "pending", invited: "2026-08-03T09:00:00Z", invited_by: "Tom Williams" },
  { id: 11, name: "Ade Okonjo",      email: "ade@acmeco.co.uk",     initials: "AO", role: "analyst", status: "active",  joined: "2025-11-05", last_active: "2026-08-03T06:30:00Z" },
  { id: 12, name: "Konstantinos Papadimitriou-Wainwright", email: "konstantinos.papadimitriou-wainwright@acme-industries-holdings.co.uk", initials: "KP", role: "analyst", status: "active", joined: "2026-02-17", last_active: "2026-07-31T10:14:00Z" }
];

/* Personas offered in the "Viewing as" switcher */
const PERSONA_IDS = [1, 2, 4, 7];

/* ---------- Navigation, generated from the signed-in role ---------- */
const ROLE_NAV = [
  { path: "/dashboard",     label: "Overview",              icon: "chart-column",      group: "Manage",  cap: null },
  { path: "/users",         label: "User Management",       icon: "users-round",       group: "Manage",  cap: null },
  { path: "/sims",          label: "SIM Management",        icon: "card-sim",          group: "Manage",  cap: null },
  { path: "/alerts",        label: "Alerts",                icon: "bell",              group: "Manage",  cap: "alerts.config" },
  { path: "/co2",           label: "Environmental Impact",  icon: "leaf",              group: "Manage",  cap: null },
  { path: "/subscriptions", label: "Subscription & Billing",icon: "wallet",            group: "Account", cap: "billing.invoices" },
  { path: "/support",       label: "Support & Help",        icon: "life-buoy",         group: "Account", cap: null },
  { path: "/settings",      label: "Settings",              icon: "settings",          group: "Account", cap: null }
];

/* Route → required capability. Missing route = allowed. */
const ROUTE_CAPS = {
  /* Out of demo scope: no role holds this, so /rules always denies. */
  "/rules": "rules.demo",
  "/alerts": "alerts.config",
  "/settings/users": "users.view",
  "/settings/organisation": "org.config",
  "/sims/travel/new": "sim.order",
  "/sims/assign-csv": "sim.manage",
  "/users/import": "users.records.manage"
};

/* Settings tabs, filtered by capability */
const SETTINGS_TABS = [
  { key: "profile",       label: "Profile",  path: "/settings",         cap: null,
    title: "Profile",  subtitle: "Your account details." },
  { key: "security",      label: "Security", path: "/settings/security", cap: null,
    title: "Security", subtitle: "Manage your password and two-factor authentication." },
  { key: "users",         label: "Admin Management", path: "/settings/users",         cap: "users.view",
    title: "Admin Management", subtitle: "Invite and manage the people who work on your account, and provide them with access levels that matches their responsibilities." },
  { key: "organisation",  label: "Organisation",    path: "/settings/organisation",  cap: "org.config",
    title: "Organisation", subtitle: "Billing, payments and the business details that appear on your invoices and contracts." }
];

/* ---------- Role comparison ---------- */
const PERMISSION_GROUPS = [
  {
    title: "User management",
    rows: [
      { label: "Invite users",               owner: true, admin: true,  analyst: false },
      { label: "Change roles",               owner: true, admin: true,  analyst: false },
      { label: "Remove users",               owner: true, admin: true,  analyst: false },
      { label: "Manage other Admins",        owner: true, admin: true,  analyst: false }
    ]
  },
  {
    title: "Billing",
    rows: [
      { label: "View invoices and spend",    owner: true, admin: true, analyst: true },
      { label: "Billing configuration",      owner: true, admin: true, analyst: false },
      { label: "Payment methods",            owner: true, admin: true, analyst: false }
    ]
  },
  {
    title: "Organisation identity",
    rows: [
      { label: "Organisation name",          owner: true, admin: { no: "View only" }, analyst: false },
      { label: "Registered address",         owner: true, admin: { no: "View only" }, analyst: false },
      { label: "Contact details",            owner: true, admin: { no: "View only" }, analyst: false },
      { label: "Integrations",               owner: true, admin: { no: "View only" }, analyst: false }
    ]
  },
  {
    title: "SIM management",
    rows: [
      { label: "Edit IMEI details",              owner: true, admin: true, analyst: false },
      { label: "Suspend and resume SIMs",        owner: true, admin: true, analyst: false },
      { label: "Pause and resume SIMs",          owner: true, admin: true, analyst: false },
      { label: "Block and unblock SIMs",         owner: true, admin: true, analyst: false },
      { label: "Assign and reassign SIM users",  owner: true, admin: true, analyst: false },
      { label: "Transfer a number",              owner: true, admin: true, analyst: false },
      { label: "Manage per-SIM services",        owner: true, admin: true, analyst: false },
      { label: "Order SIM and travel plans",     owner: true, admin: true, analyst: false }
    ]
  },
  {
    title: "Data pools",
    rows: [
      { label: "View pool usage",  owner: true, admin: true, analyst: true },
      { label: "Rename a pool",    owner: true, admin: true, analyst: false }
    ]
  },
  {
    title: "Data access",
    rows: [
      { label: "Usage",     owner: true, admin: true, analyst: true },
      { label: "Spend",     owner: true, admin: true, analyst: true },
      { label: "Invoices",  owner: true, admin: true, analyst: true },
      { label: "Emissions", owner: true, admin: true, analyst: true },
      { label: "Exports",   owner: true, admin: true, analyst: true }
    ]
  }
];

/* ---------- Restriction patterns — the three-route convention (§3) ---------- */
const RESTRICTION_PATTERNS = [
  { key: "hidden", icon: "eye-off", title: "Hidden",
    when: "The role could never perform the action.",
    what: "The control isn't rendered at all.",
    eg: "A Financial Analyst never sees Rules, Alerts, or any SIM state control." },
  { key: "readonly", icon: "lock", title: "Read-only",
    when: "The information is useful, but this role can't change it.",
    what: "A lock replaces the pencil, with a note naming who can change it.",
    eg: "An Admin viewing organisation identity fields — the only place this pattern is used." },
  { key: "denied", icon: "shield-x", title: "Permission denied",
    when: "The role reaches a restricted page by URL, bookmark or external link.",
    what: "A full-page error with no technical detail.",
    eg: "A Financial Analyst opening a saved link to Rules." }
];

/* ---------- Rules (new page — demonstrates operational config) ---------- */
const RULES = [
  { id: 1, name: "Suspend at 120% of plan",   trigger: "Data usage exceeds 120% of plan allowance", action: "Suspend service", scope: "All SIMs",        enabled: true,  last_fired: "2026-07-22" },
  { id: 2, name: "Notify at 80%",             trigger: "Data usage reaches 80% of plan allowance",  action: "Email account admins", scope: "All SIMs",   enabled: true,  last_fired: "2026-08-02" },
  { id: 3, name: "Block premium rate calls",  trigger: "Outbound call to premium rate number",      action: "Bar service",     scope: "Tag: Field team",  enabled: true,  last_fired: "2026-06-11" },
  { id: 4, name: "Pause dormant IoT SIMs",    trigger: "No data session for 60 days",               action: "Pause SIM",       scope: "Tag: IoT sensors", enabled: false, last_fired: "—" },
  { id: 5, name: "Roaming spend cap",         trigger: "Roaming spend exceeds £40 in a period",     action: "Suspend service", scope: "All SIMs",         enabled: true,  last_fired: "2026-05-30" }
];

/* ---------- Formatting helpers ---------- */
const NOW = new Date("2026-08-06T10:30:00Z");
const INVITE_TTL_H = 48;   /* invitations expire 48h after sending */
const EMAIL_TTL_H  = 24;   /* email-change links expire 24h after request */

/* Invitation expiry, relative to NOW */
function inviteExpiry(m) {
  if (!m || m.status !== "pending" || !m.invited) return null;
  const sent = new Date(m.invited);
  const expires = new Date(sent.getTime() + INVITE_TTL_H * 3600000);
  const msLeft = expires - NOW;
  const hoursLeft = Math.floor(msLeft / 3600000);
  return {
    sent, expires, expired: msLeft <= 0, hoursLeft,
    label: msLeft <= 0
      ? "Expired " + relative(-msLeft)
      : hoursLeft < 1 ? "Expires in under an hour"
      : "Expires in " + hoursLeft + (hoursLeft === 1 ? " hour" : " hours")
  };
}
function relative(ms) {
  const h = Math.floor(ms / 3600000);
  if (h < 1) return "just now";
  if (h < 24) return h + (h === 1 ? " hour ago" : " hours ago");
  const dd = Math.round(h / 24);
  return dd === 1 ? "yesterday" : dd + " days ago";
}
function fmtDate(iso) {
  if (!iso) return "—";
  return new Date(iso).toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" });
}
function fmtLastActive(iso) {
  if (!iso) return "—";
  const d = new Date(iso);
  const mins = Math.round((NOW - d) / 60000);
  if (mins < 60) return mins <= 1 ? "Just now" : mins + " minutes ago";
  const hrs = Math.round(mins / 60);
  if (hrs < 24) return hrs + (hrs === 1 ? " hour ago" : " hours ago");
  const days = Math.round(hrs / 24);
  if (days === 1) return "Yesterday";
  if (days < 31) return days + " days ago";
  const months = Math.round(days / 30.4);
  if (months < 12) return months === 1 ? "1 month ago" : months + " months ago";
  const years = Math.round(months / 12);
  return years === 1 ? "1 year ago" : years + " years ago";
}
function fmtUtc(iso) {
  const d = new Date(iso);
  return d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) + " " +
         d.toISOString().slice(11, 16) + " UTC";
}
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i;

const TWEAK_DEFAULTS_ROLES = /*EDITMODE-BEGIN*/{
  "fail_mode": "none",
  "travel_purchase": "works",
  "bulk_fail": "none",
  "batch_fail": "none",
  "pool_usage_demo": "live",
  "row_style": "zebra",
  "density": "comfortable"
} /*EDITMODE-END*/;

Object.assign(window, {
  TWEAK_DEFAULTS_ROLES,
  ROLES, ROLE_ORDER, CAPS, roleCan, canManageMember, assignableRoles, restrictionNote,
  MEMBERS, PERSONA_IDS, ROLE_NAV, ROUTE_CAPS, SETTINGS_TABS, PERMISSION_GROUPS,
  RULES, RESTRICTION_PATTERNS, inviteExpiry, INVITE_TTL_H, EMAIL_TTL_H, fmtDate, fmtLastActive, fmtUtc, EMAIL_RE
});
