"use client";

// ============================================================
// Profile — header card + 6 tabs:
//   Personal Info · KYC · Security · Notifications · Privacy · Devices
//
// Personal / KYC / Notifications / Privacy are served by the be-crm profile
// module (via useProfileData → live data door, or the local mock when the Shell
// isn't configured). Security & Devices are Shell-owned and stay on the mock.
// ============================================================

import { useEffect, useRef, useState } from "react";
import {
  user, loginActivity, passwordStrength,
} from "./account-data";
import {
  useProfileData,
  type ProfileData, type UiAccount, type UiKycSlot, type ContactKind, type KycKey, type NotifChannel, type UploadFile,
} from "@/lib/profile-api";
import {
  Avatar, Btn, Field, Icon, Modal, OtpField, PageHead, Pill, SegTabs,
  StatusDot, Toggle, useToast,
} from "./ui";

const TABS = [
  { value: "personal", label: "Personal Info", icon: "user" },
  { value: "kyc", label: "KYC", icon: "shield-check" },
  { value: "security", label: "Security", icon: "lock" },
  { value: "notifications", label: "Notifications", icon: "bell" },
  { value: "privacy", label: "Privacy & Consent", icon: "privacy" },
  { value: "devices", label: "Devices", icon: "devices" },
];

/** Read a picked File into the base64 shape a data-door command carries. */
function readFileAsUpload(file: File): Promise<UploadFile> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const base64 = String(reader.result).split(",")[1] ?? "";
      resolve({ fileName: file.name, contentType: file.type || "application/octet-stream", base64 });
    };
    reader.onerror = () => reject(reader.error ?? new Error("could not read file"));
    reader.readAsDataURL(file);
  });
}

/** Turn a thrown data-door error into a short, readable toast message. */
function errText(e: unknown): string {
  const m = e instanceof Error ? e.message : String(e);
  return m.replace(/^\d+\s*[-:]\s*/, "").slice(0, 160) || "Something went wrong.";
}

export function Profile() {
  const p = useProfileData();
  const [tab, setTab] = useState("personal");

  return (
    <div className="view">
      <PageHead
        eyebrow="My Account"
        title="Profile"
        subtitle="Manage your identity, verification, security and preferences."
        icon="user"
        actions={<Pill tone={p.account.status === "active" ? "green" : "red"}><StatusDot status={p.account.status === "active" ? "online" : "offline"} /> Account {p.account.status}</Pill>}
      />

      {p.error && (
        <div className="callout tone-red" style={{ marginBottom: 16 }}>
          <Icon name="alert" size={18} />
          <div><strong>Couldn&apos;t load your profile.</strong><span>{p.error}</span></div>
        </div>
      )}

      <ProfileHeaderCard account={p.account} />

      <SegTabs tabs={TABS} value={tab} onChange={setTab} />

      <div className="view-body">
        {tab === "personal" && <PersonalInfoTab p={p} />}
        {tab === "kyc" && <KycTab p={p} />}
        {tab === "security" && <SecurityTab />}
        {tab === "notifications" && <NotificationsTab p={p} />}
        {tab === "privacy" && <PrivacyTab p={p} />}
        {tab === "devices" && <DevicesTab />}
      </div>
    </div>
  );
}

/* ============================================================ */
/* Header card                                                  */
/* ============================================================ */

function ProfileHeaderCard({ account }: { account: UiAccount }) {
  const { push } = useToast();
  const locale = [account.sector, account.pocket].filter(Boolean).join(" · ") || "Location on file";
  return (
    <div className="card profile-hero">
      <div className="profile-hero-bg" />
      <div className="profile-hero-main">
        <div className="profile-hero-avatar">
          <Avatar initials={account.initials} gradient={account.avatarGradient} size={86} square />
          <button className="profile-hero-cam" aria-label="Change photo" onClick={() => push({ tone: "info", title: "Photo upload", desc: "Choose a JPG or PNG up to 5 MB." })}>
            <Icon name="camera" size={14} />
          </button>
        </div>
        <div className="profile-hero-id">
          <div className="profile-hero-name">
            {account.name}
            <Icon name="check-circle" size={18} className="verified-badge" />
          </div>
          <div className="profile-hero-role">{account.role} · Member since {account.memberSince}</div>
          <div className="profile-hero-chips">
            <span className="hero-chip"><Icon name="pin" size={13} /> {locale}</span>
            {account.category && <span className="hero-chip"><Icon name="owners" size={13} /> {account.category}</span>}
            {account.plot && <span className="hero-chip accent"><Icon name="check" size={13} /> House {account.plot}</span>}
          </div>
        </div>
      </div>
      <div className="profile-hero-stats">
        <HeroStat value={account.category ?? "—"} label="Category" />
        <HeroStat value={account.size ?? "—"} label="House size" />
        <HeroStat value={`${account.kycCompletionPct}%`} label="KYC complete" accent="var(--orange)" />
      </div>
    </div>
  );
}
function HeroStat({ value, label, accent }: { value: string; label: string; accent?: string }) {
  return (
    <div className="hero-stat">
      <div className="hero-stat-v" style={accent ? { color: accent } : undefined}>{value}</div>
      <div className="hero-stat-l">{label}</div>
    </div>
  );
}

/* ============================================================ */
/* Tab: Personal Info — verify-to-reveal + change email/mobile  */
/* ============================================================ */

function PersonalInfoTab({ p }: { p: ProfileData }) {
  const { push } = useToast();
  const { account } = p;
  const verified = account.revealed;
  const [house, setHouse] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const [change, setChange] = useState<null | ContactKind>(null);

  async function verify() {
    if (!house.trim()) return;
    setBusy(true); setErr("");
    try {
      const ok = await p.reveal(house.trim());
      if (ok) push({ tone: "success", title: "Verified", desc: "Your full details are now visible and editable." });
      else setErr("That doesn't match the house number on file.");
    } catch (e) { setErr(errText(e)); }
    finally { setBusy(false); }
  }

  const contactRows: { key: ContactKind; label: string; value: string }[] = [
    { key: "email", label: "Email", value: account.email },
    { key: "mobile", label: "Mobile", value: account.mobile },
    { key: "whatsapp", label: "WhatsApp", value: account.whatsapp },
  ];

  return (
    <div className="grid-2">
      <div className="card">
        <div className="card-head">
          <h3>Contact details</h3>
          {verified
            ? <Pill tone="green"><Icon name="eye" size={13} /> Revealed</Pill>
            : <Pill tone="orange"><Icon name="eye-off" size={13} /> Masked</Pill>}
        </div>

        {!verified && (
          <div className="reveal-gate">
            <div className="reveal-gate-ic"><Icon name="lock" size={20} /></div>
            <div className="reveal-gate-txt">
              <strong>Verify to reveal &amp; edit</strong>
              <span>Sensitive details are masked. Enter your house number to unmask and edit them.</span>
            </div>
            <div className="reveal-gate-form">
              <input className="ds-input" placeholder="House number" value={house} maxLength={16}
                onChange={(e) => { setHouse(e.target.value); setErr(""); }}
                onKeyDown={(e) => e.key === "Enter" && verify()} />
              <Btn icon="check" onClick={verify} disabled={busy || !house.trim()}>{busy ? "Verifying…" : "Reveal"}</Btn>
            </div>
            {err && <div className="ds-field-err"><Icon name="alert" size={12} /> {err}</div>}
          </div>
        )}

        <dl className="kv-list">
          {contactRows.map((f) => (
            <div className="kv" key={f.key}>
              <dt>{f.label}</dt>
              <dd className={verified ? "kv-edit" : "masked"}>
                {f.value}
                {verified && (
                  <button className="kv-edit-btn" aria-label={`Edit ${f.label}`} onClick={() => setChange(f.key)}><Icon name="edit" size={13} /></button>
                )}
              </dd>
            </div>
          ))}
        </dl>

        <div className="card-actions">
          <Btn variant="outline" icon="mail" onClick={() => setChange("email")} disabled={!verified}>Change email</Btn>
          <Btn variant="outline" icon="phone" onClick={() => setChange("mobile")} disabled={!verified}>Change mobile</Btn>
        </div>
        {!verified && <p className="muted-note"><Icon name="info" size={13} /> Reveal your details first to edit contact information.</p>}
      </div>

      <div className="card">
        <div className="card-head">
          <h3>Account &amp; identity</h3>
          {verified
            ? <Pill tone="green"><Icon name="eye" size={13} /> Revealed</Pill>
            : <Pill tone="muted"><Icon name="lock" size={12} /> Protected</Pill>}
        </div>
        <dl className="kv-list">
          <div className="kv"><dt>Full name</dt><dd>{account.name}</dd></div>
          <div className="kv"><dt>Role</dt><dd>{account.role}</dd></div>
          {account.allotmentNo && <div className="kv"><dt>Allotment no.</dt><dd>{account.allotmentNo}</dd></div>}
          <div className="kv"><dt>Member since</dt><dd>{account.memberSince}</dd></div>
          <div className="kv"><dt>Account status</dt><dd><Pill tone={account.status === "active" ? "green" : "red"}><StatusDot status={account.status === "active" ? "online" : "offline"} /> {account.status === "active" ? "Active" : "Suspended"}</Pill></dd></div>
          <div className="kv">
            <dt>PAN</dt>
            <dd className={verified ? "kv-edit" : "masked"}>{account.pan}{verified && <span className="kv-lock"><Icon name="lock" size={12} /> via KYC</span>}</dd>
          </div>
          <div className="kv">
            <dt>Aadhaar</dt>
            <dd className={verified ? "kv-edit" : "masked"}>{account.aadhaar}{verified && <span className="kv-lock"><Icon name="lock" size={12} /> via KYC</span>}</dd>
          </div>
        </dl>
        <p className="muted-note"><Icon name="info" size={13} /> PAN &amp; Aadhaar are managed under the KYC tab and can&apos;t be edited here.</p>
      </div>

      <ChangeContactModal p={p} kind={change} onClose={() => setChange(null)} />
    </div>
  );
}

const CONTACT_LABEL: Record<ContactKind, string> = { email: "email", mobile: "mobile", whatsapp: "WhatsApp number" };

// OTP-to-registered-mobile flow for changing a contact field. The one-time code
// is always sent to the registered mobile (never the new destination), then the
// new value is persisted only after the code verifies.
function ChangeContactModal({ p, kind, onClose }: { p: ProfileData; kind: null | ContactKind; onClose: () => void }) {
  const { push } = useToast();
  const [step, setStep] = useState<"enter" | "otp">("enter");
  const [val, setVal] = useState("");
  const [otp, setOtp] = useState("");
  const [challengeId, setChallengeId] = useState("");
  const [busy, setBusy] = useState(false);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (kind) { setStep("enter"); setVal(""); setOtp(""); setChallengeId(""); setBusy(false); }
  }, [kind]);

  function close() { setStep("enter"); setVal(""); setOtp(""); onClose(); }

  const isEmail = kind === "email";
  const label = kind ? CONTACT_LABEL[kind] : "";

  async function sendCode() {
    if (!kind) return;
    setBusy(true);
    try {
      const id = await p.requestContactChangeOtp(kind, val.trim());
      setChallengeId(id); setStep("otp");
      push({ tone: "info", title: "Code sent", desc: "A one-time code was sent to your registered mobile." });
    } catch (e) { push({ tone: "error", title: "Couldn't send code", desc: errText(e) }); }
    finally { setBusy(false); }
  }
  async function confirm() {
    if (!kind) return;
    setBusy(true);
    try {
      await p.changeContact(kind, val.trim(), challengeId, otp);
      push({ tone: "success", title: `${label[0].toUpperCase()}${label.slice(1)} updated`, desc: "Your change has been confirmed." });
      close();
    } catch (e) { push({ tone: "error", title: "Couldn't confirm", desc: errText(e) }); }
    finally { setBusy(false); }
  }

  return (
    <Modal
      open={kind != null} onClose={close}
      icon={isEmail ? "mail" : kind === "whatsapp" ? "chat" : "phone"}
      title={`Change ${label}`}
      subtitle={step === "enter" ? "We'll send a code to your registered mobile to confirm." : "Enter the 6-digit code sent to your registered mobile."}
      footer={
        step === "enter"
          ? <><Btn variant="ghost" onClick={close}>Cancel</Btn><Btn icon="send" disabled={busy || !val.trim()} onClick={sendCode}>{busy ? "Sending…" : "Send code"}</Btn></>
          : <><Btn variant="ghost" onClick={() => setStep("enter")}>Back</Btn><Btn icon="check" disabled={busy || otp.length < 6} onClick={confirm}>{busy ? "Confirming…" : "Confirm"}</Btn></>
      }
    >
      {step === "enter" ? (
        <Field label={`New ${label}`} hint="A one-time code is always sent to your existing registered mobile — never to the new destination.">
          <input className="ds-input" type={isEmail ? "email" : "tel"} placeholder={isEmail ? "you@example.com" : "+91 90000 00000"} value={val} onChange={(e) => setVal(e.target.value)} />
        </Field>
      ) : (
        <div className="otp-wrap">
          <OtpField value={otp} onChange={setOtp} />
          <button className="link-inline" onClick={sendCode} disabled={busy}>Resend code</button>
        </div>
      )}
    </Modal>
  );
}

/* ============================================================ */
/* Tab: KYC — inline upload, ID≠address, photo mandatory, %     */
/* ============================================================ */

function KycTab({ p }: { p: ProfileData }) {
  const { push } = useToast();
  const { slots, docTypes, completionPct } = p.kyc;
  // Doc type chosen for a slot but not yet uploaded (be-crm only persists it on upload).
  const [pending, setPending] = useState<Partial<Record<KycKey, string>>>({});

  const docIdFor = (key: KycKey) => pending[key] ?? slots.find((s) => s.key === key)?.docId ?? null;
  const idDoc = docIdFor("id");
  const addrDoc = docIdFor("address");

  function options(key: KycKey) {
    const want = key === "id" ? ["id", "any"] : key === "address" ? ["address", "any"] : [];
    return docTypes.filter((d) => want.includes(d.use));
  }
  function pickDoc(key: KycKey, docId: string) {
    if (key === "id" && docId === addrDoc) { push({ tone: "error", title: "Pick a different document", desc: "Identity and address proofs must be two different documents." }); return; }
    if (key === "address" && docId === idDoc) { push({ tone: "error", title: "Pick a different document", desc: "Address and identity proofs must be two different documents." }); return; }
    setPending((s) => ({ ...s, [key]: docId }));
  }
  async function upload(key: KycKey, file: File) {
    const docId = key === "photo" ? null : docIdFor(key);
    if (key !== "photo" && !docId) { push({ tone: "error", title: "Select a document type first" }); return; }
    try {
      await p.uploadKyc(key, docId, await readFileAsUpload(file));
      setPending((s) => { const n = { ...s }; delete n[key]; return n; });
      push({ tone: "success", title: "Uploaded", desc: "Your document is queued for review." });
    } catch (e) { push({ tone: "error", title: "Upload failed", desc: errText(e) }); }
  }
  async function remove(key: KycKey) {
    try { await p.removeKyc(key); push({ tone: "info", title: "Removed" }); }
    catch (e) { push({ tone: "error", title: "Couldn't remove", desc: errText(e) }); }
  }

  const photoDone = (slots.find((s) => s.key === "photo")?.status ?? "missing") !== "missing";
  const docLabel = (id: string | null) => docTypes.find((d) => d.id === id)?.label ?? "Not chosen";

  return (
    <div className="grid-side">
      <div className="card">
        <div className="card-head">
          <h3>Verification documents</h3>
          <Pill tone="orange">{completionPct}% complete</Pill>
        </div>

        <div className="kyc-progress">
          <div className="kyc-progress-bar"><span style={{ width: `${completionPct}%` }} /></div>
          <div className="kyc-progress-legend">
            {slots.map((s) => (
              <span key={s.key} className={`kyc-leg ${s.status}`}>
                <Icon name={s.status === "missing" ? "x" : s.status === "verified" ? "check-circle" : "clock"} size={13} /> {s.title}
              </span>
            ))}
          </div>
        </div>

        {!photoDone && <div className="callout tone-red"><Icon name="alert" size={18} /><div><strong>Live photo is mandatory.</strong><span>KYC cannot be completed without a verified photo.</span></div></div>}

        <div className="kyc-slots">
          {slots.map((slot) => (
            <KycSlotRow key={slot.key} slot={slot} docId={docIdFor(slot.key)} docTypes={docTypes} options={options(slot.key)}
              onPick={(d) => pickDoc(slot.key, d)} onUpload={(f) => upload(slot.key, f)} onRemove={() => remove(slot.key)} />
          ))}
        </div>
      </div>

      <div className="card card-muted">
        <div className="card-head"><h3>Rules</h3></div>
        <ul className="rule-mini">
          <li><Icon name="check" size={14} /> A live photo / selfie is <strong>mandatory</strong>.</li>
          <li><Icon name="check" size={14} /> Provide <strong>one ID</strong> and <strong>one address</strong> proof.</li>
          <li><Icon name="check" size={14} /> ID and address must be <strong>two different documents</strong>.</li>
          <li><Icon name="check" size={14} /> Accepted: PDF, JPG, PNG · size limits per document.</li>
          <li><Icon name="check" size={14} /> Review completes within ~4 business hours.</li>
        </ul>
        <div className="kyc-doc-pairs">
          <div className="muted-note"><Icon name="info" size={13} /> Current selection</div>
          <div className="pair-row"><span>Identity</span><Pill tone="blue">{docLabel(idDoc)}</Pill></div>
          <div className="pair-row"><span>Address</span><Pill tone="purple">{docLabel(addrDoc)}</Pill></div>
        </div>
      </div>
    </div>
  );
}

function KycSlotRow({ slot, docId, docTypes, options, onPick, onUpload, onRemove }: {
  slot: UiKycSlot; docId: string | null; docTypes: { id: string; label: string; hint: string; formats: string; maxMb: number }[];
  options: { id: string; label: string }[]; onPick: (d: string) => void; onUpload: (f: File) => void; onRemove: () => void;
}) {
  const fileRef = useRef<HTMLInputElement>(null);
  const tone = slot.status === "verified" ? "green" : slot.status === "uploaded" ? "blue" : slot.status === "rejected" ? "red" : "muted";
  const docMeta = docTypes.find((d) => d.id === docId);
  const accept = slot.key === "photo" ? "image/*" : "application/pdf,image/jpeg,image/png";
  return (
    <div className={`kyc-slot status-${slot.status}`}>
      <div className="kyc-slot-ic"><Icon name={slot.key === "photo" ? "camera" : slot.key === "id" ? "user" : "pin"} size={18} /></div>
      <div className="kyc-slot-main">
        <div className="kyc-slot-top">
          <span className="kyc-slot-title">{slot.title}{slot.required && <i className="req">*</i>}</span>
          <Pill tone={tone}>{slot.status === "missing" ? "Missing" : slot.status === "uploaded" ? "Under review" : slot.status === "verified" ? "Verified" : "Rejected"}</Pill>
        </div>

        {slot.key !== "photo" && (
          <select className="ds-select" value={docId ?? ""} onChange={(e) => onPick(e.target.value)}>
            <option value="" disabled>Select document type…</option>
            {options.map((o) => <option key={o.id} value={o.id}>{o.label}</option>)}
          </select>
        )}

        {docMeta && <div className="kyc-slot-hint">{docMeta.hint ? `${docMeta.hint} · ` : ""}{docMeta.formats} · ≤ {docMeta.maxMb} MB</div>}
        {slot.status === "rejected" && slot.note && <div className="ds-field-err"><Icon name="alert" size={12} /> {slot.note}</div>}

        <input ref={fileRef} type="file" accept={accept} hidden
          onChange={(e) => { const f = e.target.files?.[0]; if (f) onUpload(f); e.target.value = ""; }} />

        {slot.fileName ? (
          <div className="kyc-file">
            <Icon name="paperclip" size={14} />
            <span className="kyc-file-name">{slot.fileName}</span>
            {slot.note && slot.status !== "rejected" && <span className="kyc-file-note">{slot.note}</span>}
            <button className="ds-iconbtn sm" aria-label="Remove" onClick={onRemove}><Icon name="trash" size={14} /></button>
          </div>
        ) : (
          <button className="kyc-drop" onClick={() => fileRef.current?.click()}>
            <Icon name="upload" size={16} /> Click to upload {slot.key === "photo" ? "a photo" : "document"}
          </button>
        )}
      </div>
    </div>
  );
}

/* ============================================================ */
/* Tab: Security  (Shell-owned — local mock)                    */
/* ============================================================ */

function SecurityTab() {
  const { push } = useToast();
  const [pw, setPw] = useState("");
  const [twoFA, setTwoFA] = useState(false);
  const [authApp, setAuthApp] = useState(false);
  const [alerts, setAlerts] = useState(true);
  const [twoFaModal, setTwoFaModal] = useState(false);
  const strength = passwordStrength(pw);

  return (
    <div className="grid-2">
      <div className="card">
        <div className="card-head"><h3>Password</h3><Pill tone="muted">Updated 5 days ago</Pill></div>
        <Field label="New password" hint="Min 8 chars · upper & lower · a number · a special character · no common patterns.">
          <input className="ds-input" type="password" placeholder="••••••••" value={pw} onChange={(e) => setPw(e.target.value)} />
        </Field>
        {pw && (
          <div className="pw-meter">
            <div className="pw-bars">{[0, 1, 2, 3].map((i) => <span key={i} className={i < strength.score ? `lvl lvl-${strength.label.toLowerCase()}` : "lvl"} />)}</div>
            <div className="pw-label">{strength.label}</div>
          </div>
        )}
        <ul className="pw-checks">
          {strength.checks.map((c) => <li key={c.label} className={c.ok ? "ok" : ""}><Icon name={c.ok ? "check-circle" : "x"} size={14} /> {c.label}</li>)}
        </ul>
        <div className="card-actions"><Btn icon="key" disabled={strength.score < 3} onClick={() => { push({ tone: "success", title: "Password updated" }); setPw(""); }}>Update password</Btn></div>
      </div>

      <div className="card">
        <div className="card-head"><h3>Two-factor &amp; sign-in</h3></div>
        <SettingRow icon="shield-check" title="Two-factor authentication" desc="Requires your password and a one-time code to enable.">
          <Toggle checked={twoFA} onChange={(v) => { if (v) setTwoFaModal(true); else { setTwoFA(false); setAuthApp(false); push({ tone: "info", title: "Two-factor disabled" }); } }} label="2FA" />
        </SettingRow>
        <SettingRow icon="key" title="Authenticator app" desc={authApp ? "Connected · codes from your app" : "Use an app like Google Authenticator"}>
          <Toggle checked={authApp} disabled={!twoFA} onChange={(v) => { setAuthApp(v); push({ tone: v ? "success" : "info", title: v ? "Authenticator linked" : "Authenticator removed" }); }} label="Authenticator" />
        </SettingRow>
        <SettingRow icon="bell" title="Login alerts" desc="Notify me when a new device signs in.">
          <Toggle checked={alerts} onChange={(v) => { setAlerts(v); push({ tone: "info", title: v ? "Login alerts on" : "Login alerts off" }); }} label="Login alerts" />
        </SettingRow>
        {!twoFA && <p className="muted-note"><Icon name="info" size={13} /> Enable two-factor authentication to link an authenticator app.</p>}
      </div>

      <TwoFaModal open={twoFaModal} onClose={() => setTwoFaModal(false)} onDone={() => { setTwoFA(true); setTwoFaModal(false); push({ tone: "success", title: "Two-factor enabled", desc: "Your account is now protected with 2FA." }); }} />
    </div>
  );
}

function TwoFaModal({ open, onClose, onDone }: { open: boolean; onClose: () => void; onDone: () => void }) {
  const { push } = useToast();
  const [step, setStep] = useState<"password" | "otp">("password");
  const [pw, setPw] = useState("");
  const [otp, setOtp] = useState("");
  function close() { setStep("password"); setPw(""); setOtp(""); onClose(); }
  return (
    <Modal open={open} onClose={close} icon="shield-check" title="Enable two-factor authentication"
      subtitle={step === "password" ? "Confirm your password to continue." : `Enter the code sent to ${user.mobile.masked}.`}
      footer={step === "password"
        ? <><Btn variant="ghost" onClick={close}>Cancel</Btn><Btn icon="arrow" iconRight="arrow" disabled={pw.length < 4} onClick={() => { setStep("otp"); push({ tone: "info", title: "Code sent", desc: "OTP sent to your registered mobile." }); }}>Continue</Btn></>
        : <><Btn variant="ghost" onClick={() => setStep("password")}>Back</Btn><Btn icon="check" disabled={otp.length < 6} onClick={onDone}>Enable 2FA</Btn></>}
    >
      {step === "password" ? (
        <Field label="Current password"><input className="ds-input" type="password" placeholder="••••••••" value={pw} onChange={(e) => setPw(e.target.value)} /></Field>
      ) : (
        <div className="otp-wrap"><OtpField value={otp} onChange={setOtp} /><span className="muted-note">Both password and OTP are required to turn 2FA on.</span></div>
      )}
    </Modal>
  );
}

function SettingRow({ icon, title, desc, children }: { icon: string; title: string; desc: string; children: React.ReactNode }) {
  return (
    <div className="setting-row">
      <span className="setting-ic"><Icon name={icon} size={18} /></span>
      <div className="setting-txt"><div className="setting-title">{title}</div><div className="setting-desc">{desc}</div></div>
      <div className="setting-ctl">{children}</div>
    </div>
  );
}

/* ============================================================ */
/* Tab: Notifications                                           */
/* ============================================================ */

function NotificationsTab({ p }: { p: ProfileData }) {
  const { push } = useToast();
  const { prefs, timezone, timezoneOptions, windows, destinations } = p.notifications;
  const [addOpen, setAddOpen] = useState(false);
  const [verifyId, setVerifyId] = useState<string | null>(null);
  const channels: { key: NotifChannel; label: string; icon: string }[] = [
    { key: "email", label: "Email", icon: "mail" },
    { key: "sms", label: "SMS", icon: "phone" },
    { key: "whatsapp", label: "WhatsApp", icon: "chat" },
    { key: "push", label: "Push", icon: "bell" },
  ];

  async function toggle(cat: (typeof prefs)[number], ch: NotifChannel) {
    if (cat.locked) { push({ tone: "info", title: "Required notifications", desc: "Security alerts can't be turned off." }); return; }
    try { await p.setPreference(cat.id, ch, !cat[ch]); }
    catch (e) { push({ tone: "error", title: "Couldn't save", desc: errText(e) }); }
  }
  async function toggleWindow(w: (typeof windows)[number]) {
    try { await p.setContactWindow(w.label, { startTime: w.startTime, endTime: w.endTime, enabled: !w.enabled }); }
    catch (e) { push({ tone: "error", title: "Couldn't save", desc: errText(e) }); }
  }
  async function changeTz(tz: string) {
    try { await p.setTimezone(tz); } catch (e) { push({ tone: "error", title: "Couldn't save", desc: errText(e) }); }
  }

  return (
    <div className="grid-side">
      <div className="card card-pad-0">
        <div className="card-head pad"><h3>Per-channel matrix</h3><span className="muted-note">Choose how each category reaches you</span></div>
        <div className="notif-table">
          <div className="notif-row notif-head">
            <span>Category</span>
            {channels.map((c) => <span key={c.key} className="notif-ch"><Icon name={c.icon} size={14} /> {c.label}</span>)}
          </div>
          {prefs.map((cat) => (
            <div className="notif-row" key={cat.id}>
              <span className="notif-cat">
                <span className="notif-cat-name">{cat.label}{cat.locked && <Icon name="lock" size={12} className="lock-ic" />}</span>
                <span className="notif-cat-desc">{cat.desc}</span>
              </span>
              {channels.map((c) => (
                <span key={c.key} className="notif-ch">
                  <Toggle checked={Boolean(cat[c.key])} disabled={cat.locked} onChange={() => toggle(cat, c.key)} label={`${cat.label} ${c.label}`} />
                </span>
              ))}
            </div>
          ))}
        </div>
      </div>

      <div className="card-stack">
        <div className="card">
          <div className="card-head"><h3>Contact-time window</h3></div>
          <Field label="Timezone">
            <select className="ds-select" value={timezone} onChange={(e) => changeTz(e.target.value)}>
              {timezoneOptions.map((t) => <option key={t} value={t}>{t}</option>)}
            </select>
          </Field>
          <div className="time-windows">
            {windows.map((t) => (
              <button key={t.label} className={`time-win ${t.enabled ? "on" : ""}`} onClick={() => toggleWindow(t)}>
                <Icon name={t.enabled ? "check-circle" : "clock"} size={15} />
                <span className="tw-label">{t.label}</span>
                <span className="tw-range">{t.range}</span>
              </button>
            ))}
          </div>
          <p className="muted-note"><Icon name="info" size={13} /> Callbacks &amp; proactive messages only arrive inside enabled windows, in your timezone.</p>
        </div>

        <div className="card">
          <div className="card-head"><h3>Destinations</h3></div>
          <div className="dest-list">
            {destinations.length === 0 && <p className="muted-note"><Icon name="info" size={13} /> No extra destinations yet.</p>}
            {destinations.map((d) => (
              <div className="dest-row" key={d.id}>
                <span className="dest-ic"><Icon name={d.channel === "email" ? "mail" : d.channel === "sms" ? "phone" : "chat"} size={15} /></span>
                <div className="dest-txt"><div className="dest-label">{d.label}{d.primary && <Pill tone="orange">Primary</Pill>}</div><div className="dest-val">{d.value}</div></div>
                {d.verified ? <Pill tone="green"><Icon name="check" size={12} /> Verified</Pill> : <Btn variant="outline" size="sm" onClick={() => setVerifyId(d.id)}>Verify</Btn>}
              </div>
            ))}
          </div>
          <Btn variant="ghost" icon="plus" onClick={() => setAddOpen(true)}>Add destination</Btn>
        </div>
      </div>

      <AddDestinationModal p={p} open={addOpen} onClose={() => setAddOpen(false)} />
      <VerifyDestinationModal p={p} id={verifyId} onClose={() => setVerifyId(null)} />
    </div>
  );
}

function AddDestinationModal({ p, open, onClose }: { p: ProfileData; open: boolean; onClose: () => void }) {
  const { push } = useToast();
  const [channel, setChannel] = useState<"email" | "sms" | "whatsapp">("email");
  const [label, setLabel] = useState("");
  const [value, setValue] = useState("");
  const [busy, setBusy] = useState(false);
  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (open) { setChannel("email"); setLabel(""); setValue(""); setBusy(false); }
  }, [open]);

  async function save() {
    setBusy(true);
    try {
      await p.addDestination(channel, label.trim() || channel, value.trim());
      push({ tone: "success", title: "Destination added", desc: "Verify it to start receiving messages there." });
      onClose();
    } catch (e) { push({ tone: "error", title: "Couldn't add", desc: errText(e) }); }
    finally { setBusy(false); }
  }
  const isEmail = channel === "email";
  return (
    <Modal open={open} onClose={onClose} icon="plus" title="Add a destination"
      subtitle="Add another address or number, then verify it with a one-time code."
      footer={<><Btn variant="ghost" onClick={onClose}>Cancel</Btn><Btn icon="check" disabled={busy || !value.trim()} onClick={save}>{busy ? "Adding…" : "Add destination"}</Btn></>}
    >
      <Field label="Channel">
        <select className="ds-select" value={channel} onChange={(e) => setChannel(e.target.value as "email" | "sms" | "whatsapp")}>
          <option value="email">Email</option>
          <option value="sms">SMS</option>
          <option value="whatsapp">WhatsApp</option>
        </select>
      </Field>
      <Field label="Label" hint="A name to recognise this destination.">
        <input className="ds-input" placeholder={isEmail ? "Work email" : "Alternate mobile"} value={label} onChange={(e) => setLabel(e.target.value)} />
      </Field>
      <Field label={isEmail ? "Email address" : "Phone number"}>
        <input className="ds-input" type={isEmail ? "email" : "tel"} placeholder={isEmail ? "you@example.com" : "+91 90000 00000"} value={value} onChange={(e) => setValue(e.target.value)} />
      </Field>
    </Modal>
  );
}

function VerifyDestinationModal({ p, id, onClose }: { p: ProfileData; id: string | null; onClose: () => void }) {
  const { push } = useToast();
  const [otp, setOtp] = useState("");
  const [challengeId, setChallengeId] = useState("");
  const [busy, setBusy] = useState(false);
  const [sent, setSent] = useState(false);

  useEffect(() => {
    if (!id) return;
    let cancelled = false;
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setOtp(""); setChallengeId(""); setSent(false); setBusy(true);
    p.requestDestinationOtp(id)
      .then((cid) => { if (!cancelled) { setChallengeId(cid); setSent(true); } })
      .catch((e) => { if (!cancelled) push({ tone: "error", title: "Couldn't send code", desc: errText(e) }); })
      .finally(() => { if (!cancelled) setBusy(false); });
    return () => { cancelled = true; };
  }, [id, p, push]);

  async function confirm() {
    if (!id) return;
    setBusy(true);
    try {
      await p.verifyDestination(id, challengeId, otp);
      push({ tone: "success", title: "Destination verified" });
      onClose();
    } catch (e) { push({ tone: "error", title: "Couldn't verify", desc: errText(e) }); }
    finally { setBusy(false); }
  }
  return (
    <Modal open={id != null} onClose={onClose} icon="shield-check" title="Verify destination"
      subtitle="Enter the 6-digit code sent to your registered mobile."
      footer={<><Btn variant="ghost" onClick={onClose}>Cancel</Btn><Btn icon="check" disabled={busy || otp.length < 6} onClick={confirm}>{busy ? "Verifying…" : "Verify"}</Btn></>}
    >
      <div className="otp-wrap">
        <OtpField value={otp} onChange={setOtp} />
        <span className="muted-note">{sent ? "A code was sent to your registered mobile." : "Sending a code…"}</span>
      </div>
    </Modal>
  );
}

/* ============================================================ */
/* Tab: Privacy & Consent                                       */
/* ============================================================ */

function PrivacyTab({ p }: { p: ProfileData }) {
  const { push } = useToast();

  async function toggle(gid: string, iid: string, locked: boolean, granted: boolean) {
    if (locked) { push({ tone: "info", title: "Required consent", desc: "Statutory consents can't be withdrawn while active." }); return; }
    try { await p.setConsent(gid, iid, !granted); }
    catch (e) { push({ tone: "error", title: "Couldn't save", desc: errText(e) }); }
  }

  return (
    <div className="consent-cols">
      {p.consent.map((g) => {
        const required = g.items.some((it) => it.locked);
        return (
          <div className="card" key={g.id}>
            <div className="card-head">
              <h3>{g.title}</h3>
              {required
                ? <Pill tone="red"><Icon name="lock" size={12} /> Required</Pill>
                : <Pill tone="muted">Optional</Pill>}
            </div>
            <div className="consent-list">
              {g.items.map((it) => (
                <div className={`consent-item ${it.locked ? "locked" : ""}`} key={it.id}>
                  <div className="consent-txt">
                    <div className="consent-label">{it.label}{it.locked && <Icon name="lock" size={12} className="lock-ic" />}</div>
                    {it.desc && <div className="consent-desc">{it.desc}</div>}
                  </div>
                  <Toggle checked={it.granted} disabled={it.locked} onChange={() => toggle(g.id, it.id, it.locked, it.granted)} label={it.label} />
                </div>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ============================================================ */
/* Tab: Devices  (Shell-owned — local mock)                     */
/* ============================================================ */

function DevicesTab() {
  const { push } = useToast();
  const [devices, setDevices] = useState(loginActivity);

  function signOut(id: string) {
    setDevices((d) => d.filter((x) => x.id !== id));
    push({ tone: "success", title: "Signed out", desc: "That device no longer has access." });
  }

  return (
    <div className="grid-side">
      <div className="card">
        <div className="card-head">
          <h3>Connected devices</h3>
          <Btn variant="outline" size="sm" icon="logout" onClick={() => { setDevices((d) => d.filter((x) => x.current)); push({ tone: "success", title: "Other devices signed out" }); }}>Sign out all others</Btn>
        </div>
        <div className="device-list">
          {devices.filter((d) => d.event !== "password-change").map((d) => (
            <div className={`device-row ${d.current ? "current" : ""}`} key={d.id}>
              <span className="device-ic"><Icon name={d.kind} size={20} /></span>
              <div className="device-txt">
                <div className="device-name">{d.device}{d.current && <Pill tone="green">This device</Pill>}{!d.trusted && <Pill tone="orange"><Icon name="alert" size={11} /> New</Pill>}</div>
                <div className="device-meta">{d.browser} · {d.os}</div>
                <div className="device-meta"><Icon name="pin" size={12} /> {d.location} · {d.ip} · {d.lastActive}</div>
              </div>
              {!d.current && <button className="ds-iconbtn" aria-label="Sign out" onClick={() => signOut(d.id)}><Icon name="logout" size={16} /></button>}
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <div className="card-head"><h3>Recent activity</h3></div>
        <div className="timeline">
          {loginActivity.map((a) => (
            <div className="tl-item" key={a.id}>
              <span className={`tl-dot ev-${a.event}`}><Icon name={a.event === "password-change" ? "key" : a.event === "new-device" ? "alert" : a.event === "2fa-enabled" ? "shield-check" : "check"} size={12} /></span>
              <div className="tl-body">
                <div className="tl-title">{labelForEvent(a.event)}</div>
                <div className="tl-meta">{a.device} · {a.location}</div>
                <div className="tl-time">{a.lastActive}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function labelForEvent(ev: string) {
  switch (ev) {
    case "password-change": return "Password changed";
    case "new-device": return "New device signed in";
    case "2fa-enabled": return "Two-factor enabled";
    case "sign-out": return "Signed out";
    default: return "Successful sign-in";
  }
}
