// numbers.jsx — Phone number provisioning + owned numbers (live data)

const PREFIX_OPTIONS = ["any", "800", "833", "844", "855", "866", "877", "888"];

// Telephone keypad letter → digit mapping (E.161 / ITU-T)
const KEYPAD = {
  A:"2",B:"2",C:"2",
  D:"3",E:"3",F:"3",
  G:"4",H:"4",I:"4",
  J:"5",K:"5",L:"5",
  M:"6",N:"6",O:"6",
  P:"7",Q:"7",R:"7",S:"7",
  T:"8",U:"8",V:"8",
  W:"9",X:"9",Y:"9",Z:"9",
};

// Translate an arbitrary user query (letters, digits, dashes, spaces) into the
// digit string that would be dialed on a phone keypad. Non-keypad characters
// are dropped. Returns "" for empty input.
function toKeypadDigits(s) {
  if (!s) return "";
  let out = "";
  for (const raw of s.toUpperCase()) {
    if (raw >= "0" && raw <= "9") out += raw;
    else if (KEYPAD[raw]) out += KEYPAD[raw];
  }
  return out;
}

function Numbers({ workspace, onOpen }) {
  const wid = workspace.id;
  const [mode, setMode] = React.useState("quick"); // quick | search | browse
  const [q, setQ] = React.useState("");
  const [sort, setSort] = React.useState({ key: "calls", dir: "desc" });

  const owned = useApi(
    () => API.numbers.list(wid, { q: q.trim() || undefined, sort: sort.key, dir: sort.dir }),
    [wid, q, sort.key, sort.dir]
  );

  async function claim(inv) {
    const ok = await window.confirmDialog({
      title: "Claim number",
      message: `Claim ${fmtNumber(inv.number)} for $2/mo? It will be billed to this workspace immediately.`,
      confirmLabel: "Claim",
    });
    if (!ok) return;
    try {
      await API.numbers.create(wid, { inventoryId: inv.id });
      owned.reload();
      window.toast.success(`${fmtNumber(inv.number)} claimed.`);
    } catch (err) {
      window.toast.error(err.message || "Could not claim number");
    }
  }

  async function release(n) {
    const ok = await window.confirmDialog({
      title: "Release number",
      message: `Release ${n.friendlyName || fmtNumber(n.number)}? It returns to the available inventory.`,
      confirmLabel: "Release",
      danger: true,
    });
    if (!ok) return;
    try {
      await API.numbers.delete(wid, n.id);
      owned.reload();
      window.toast.success("Number released");
    } catch (err) {
      window.toast.error(err.message);
    }
  }

  const toggleSort = (key) => setSort((s) => s.key === key ? { key, dir: s.dir === "asc" ? "desc" : "asc" } : { key, dir: "desc" });
  const SortH = ({ k, children, align = "left", style }) => (
    <th style={{textAlign: align, cursor:"pointer", ...style}} onClick={() => toggleSort(k)}>
      <span style={{display:"inline-flex", alignItems:"center", gap:4}}>
        {children}
        {sort.key === k && <span style={{fontSize:9, color:"var(--ink-3)"}}>{sort.dir === "asc" ? "▲" : "▼"}</span>}
      </span>
    </th>
  );

  const ownedRows = owned.data || [];

  return (
    <div className="page-inner wide">
      <div className="row between" style={{alignItems:"flex-start", marginBottom:24}}>
        <div>
          <div className="eyebrow">Tracking</div>
          <h1 className="page-title" style={{marginTop:6}}>Phone numbers</h1>
          <div className="page-sub">Toll-free inventory · provision in seconds · $2/mo per number</div>
        </div>
      </div>

      {/* Get a number card */}
      <div className="card" style={{marginBottom:24, overflow:"hidden"}}>
        <div style={{padding:"24px 28px", background:"linear-gradient(180deg, color-mix(in oklab, var(--accent) 5%, var(--panel)) 0%, var(--panel) 100%)", borderBottom:".5px solid var(--line)"}}>
          <div className="row between" style={{alignItems:"flex-start"}}>
            <div>
              <h2 className="serif" style={{fontSize:26, marginBottom:4}}>Get a new number</h2>
              <div style={{fontSize:13.5, color:"var(--ink-3)"}}>Instant provisioning · $2/mo · no commitment</div>
            </div>
            <div className="seg">
              <button className={mode === "quick" ? "on" : ""} onClick={() => setMode("quick")}>Quick</button>
              <button className={mode === "search" ? "on" : ""} onClick={() => setMode("search")}>Vanity search</button>
              <button className={mode === "browse" ? "on" : ""} onClick={() => setMode("browse")}>Browse</button>
            </div>
          </div>
        </div>

        {mode === "quick" && <QuickPicker onClaim={claim} />}
        {mode === "search" && <VanitySearch onClaim={claim} />}
        {mode === "browse" && <BrowseNumbers onClaim={claim} />}
      </div>

      {/* Owned numbers */}
      <div className="card">
        <div className="card-h" style={{padding:"12px 16px", gap:12}}>
          <div style={{flex:1, display:"flex", alignItems:"center", gap:10, height:34, padding:"0 12px", background:"var(--bg-elev)", borderRadius:8, border:".5px solid var(--line-2)"}}>
            <I.search size={14} style={{color:"var(--ink-4)"}}/>
            <input
              value={q}
              onChange={(e) => setQ(e.target.value)}
              placeholder="Search your numbers by friendly name or digits…"
              style={{flex:1, border:0, outline:"none", background:"transparent", fontSize:13.5, color:"var(--ink)"}}
            />
            {q && <button className="icon-btn" style={{width:22, height:22}} onClick={() => setQ("")}><I.x size={12}/></button>}
          </div>
          <div className="row g8">
            <button className="btn btn-secondary btn-sm"><I.dl size={13}/> Export</button>
          </div>
        </div>
        <table className="table">
          <thead>
            <tr>
              <SortH k="friendly_name">Friendly name</SortH>
              <th>Number</th>
              <th>Forwards to</th>
              <th>Campaign</th>
              <SortH k="calls" align="right">Calls</SortH>
              <SortH k="ratio" align="right">Answer rate</SortH>
              <th>Status</th>
              <th style={{width:40}}></th>
            </tr>
          </thead>
          <tbody>
            {owned.loading && ownedRows.length === 0 && (
              <tr><td colSpan={8} style={{textAlign:"center", padding:"48px 0", color:"var(--ink-4)"}}>Loading…</td></tr>
            )}
            {owned.error && (
              <tr><td colSpan={8}><ErrorChip msg={owned.error.message}/></td></tr>
            )}
            {!owned.loading && ownedRows.length === 0 && (
              <tr><td colSpan={8} style={{textAlign:"center", padding:"48px 0", color:"var(--ink-4)"}}>
                {q ? <>No numbers match "<strong style={{color:"var(--ink-2)"}}>{q}</strong>"</> : "No numbers yet — claim one above"}
              </td></tr>
            )}
            {ownedRows.map((r) => {
              // Friendly name: trimmed value if user set one; otherwise the formatted number.
              const customName = r.friendlyName && r.friendlyName.trim() && !r.friendlyName.startsWith("+");
              const displayName = customName ? r.friendlyName : fmtNumber(r.number);
              const ratio = r.calls > 0 && r.answered != null
                ? Math.round((r.answered / r.calls) * 100) + "%"
                : "—";
              const fwdLabel = r.routingType === "ivr" ? "IVR menu"
                : r.routingType === "ring_group" ? "Ring group"
                : r.routingType === "voicemail" ? "Voicemail"
                : r.routingDestination
                  ? fmtNumber(r.routingDestination) || r.routingDestination
                  : "Not configured";
              return (
                <tr key={r.id} style={{cursor:"pointer"}} onClick={() => onOpen && onOpen(r.id)}>
                  <td style={{color:"var(--ink)", fontWeight:500}}>
                    {displayName}
                  </td>
                  <td className="num muted">{fmtNumber(r.number)}</td>
                  <td className="muted">{fwdLabel}</td>
                  <td className="muted" style={{fontSize:12.5}}>{r.campaignName || "—"}</td>
                  <td className="num" style={{textAlign:"right", color: r.calls > 0 ? "var(--ink)" : "var(--ink-4)"}}>{r.calls || 0}</td>
                  <td style={{textAlign:"right"}} className="num">{ratio}</td>
                  <td>
                    {r.status === "live"
                      ? <span className="pill good"><span className="dot"/>Live</span>
                      : r.status === "paused"
                        ? <span className="pill warn"><span className="dot"/>Paused</span>
                        : <span className="pill warn"><span className="dot"/>Setup needed</span>
                    }
                  </td>
                  <td onClick={(e) => e.stopPropagation()}>
                    <button className="icon-btn" title="Release" onClick={() => release(r)}>
                      <I.x size={14}/>
                    </button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
        {ownedRows.length > 0 && (
          <div style={{padding:"10px 16px", display:"flex", alignItems:"center", justifyContent:"space-between", borderTop:".5px solid var(--line)", fontSize:12, color:"var(--ink-4)"}}>
            <span>Showing {ownedRows.length} number{ownedRows.length === 1 ? "" : "s"}</span>
          </div>
        )}
      </div>
    </div>
  );
}

function QuickPicker({ onClaim }) {
  const sug = useApi(() => API.inventory.suggestions(6), []);
  const items = sug.data || [];
  return (
    <div className="card-b" style={{padding:"28px"}}>
      {sug.loading && <div style={{color:"var(--ink-4)", textAlign:"center", padding:20}}>Loading suggestions…</div>}
      {sug.error && <ErrorChip msg={sug.error.message}/>}
      {!sug.loading && items.length === 0 && (
        <div style={{color:"var(--ink-4)", textAlign:"center", padding:20}}>No numbers available right now.</div>
      )}
      {items.length > 0 && (
        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:12}}>
          {items.map((n) => (
            <div key={n.id} style={{padding:"18px 20px", borderRadius:"var(--radius-lg)", background:"var(--bg-elev)", border:".5px solid var(--line)", display:"flex", flexDirection:"column", gap:10}}>
              <div className="num serif" style={{fontSize:22, color:"var(--ink)", fontWeight:400}}>{fmtNumber(n.number)}</div>
              <div style={{fontSize:11.5, color:"var(--ink-4)"}}>
                {n.vanityPattern ? `Vanity · ${n.vanityPattern}` : n.isMemorable ? "Memorable digits" : `Toll-free · ${n.prefix}`}
              </div>
              <div className="row between" style={{marginTop:6}}>
                <span className="pill good"><span className="dot"/>Available</span>
                <button className="btn btn-primary btn-sm" onClick={() => onClaim(n)}>Claim</button>
              </div>
            </div>
          ))}
          <div style={{gridColumn:"1 / -1", textAlign:"center", marginTop:10}}>
            <button className="btn btn-ghost btn-sm" onClick={() => sug.reload()}>Refresh suggestions <I.chev size={12}/></button>
          </div>
        </div>
      )}
    </div>
  );
}

function VanitySearch({ onClaim }) {
  const [vanity, setVanity] = React.useState("");
  const [prefix, setPrefix] = React.useState("any");
  const [debounced, setDebounced] = React.useState("");
  const inputRef = React.useRef();

  // Debounce the typed query by 300ms — feels live but avoids per-keystroke fetches
  React.useEffect(() => {
    const t = setTimeout(() => setDebounced(vanity.trim()), 300);
    return () => clearTimeout(t);
  }, [vanity]);

  // Translate letters → keypad digits, then strip everything else
  const queryDigits = toKeypadDigits(debounced);
  const liveDigits = toKeypadDigits(vanity); // for the inline hint while typing

  // Minimum 2 digits before hitting the API
  const effectiveQuery = queryDigits.length >= 2 ? queryDigits : "";
  const hasLetters = /[A-Za-z]/.test(vanity);

  const results = useApi(
    () => effectiveQuery
      ? API.inventory.search({ q: effectiveQuery, prefix, limit: 24 })
      : Promise.resolve({ data: [] }),
    [effectiveQuery, prefix]
  );

  // Distinguish "user is still typing" from "results are loading"
  const stillTyping = toKeypadDigits(vanity.trim()) !== queryDigits;
  const loading = !!effectiveQuery && (results.loading || stillTyping);
  const items = effectiveQuery ? (results.data || []) : [];

  return (
    <div className="card-b" style={{padding:"32px 28px"}}>
      <div className="vanity-hero">
        <div className="vanity-search">
          <I.search size={18} className="vanity-search__icon"/>
          <input
            ref={inputRef}
            autoFocus
            placeholder="Search vanity — FLOWERS, CARE, 247-BOOK…"
            value={vanity}
            onChange={(e) => setVanity(e.target.value)}
            onKeyDown={(e) => { if (e.key === "Escape") setVanity(""); }}
          />
          <div className="vanity-search__action">
            {loading
              ? <Spinner size={16}/>
              : vanity
                ? <button type="button" className="icon-btn" style={{width:26, height:26}}
                    onClick={() => { setVanity(""); inputRef.current?.focus(); }} title="Clear (Esc)">
                    <I.x size={14}/>
                  </button>
                : null}
          </div>
        </div>

        <div className="vanity-prefix">
          {PREFIX_OPTIONS.map((p) => (
            <button
              key={p}
              type="button"
              className={"vanity-prefix__chip" + (prefix === p ? " on" : "")}
              onClick={() => setPrefix(p)}
            >
              {p === "any" ? "Any" : p}
            </button>
          ))}
        </div>

        {/* Live keypad translation hint — only when letters are typed AND
            we have enough digits to actually search */}
        {hasLetters && liveDigits.length >= 2 && (
          <div style={{fontSize:12, color:"var(--ink-4)", marginTop:6}}>
            Searching for <span className="num" style={{color:"var(--ink-2)", fontWeight:500}}>{liveDigits}</span>
          </div>
        )}
      </div>

      <div style={{marginTop:24}}>
        {!vanity.trim() && (
          <div style={{color:"var(--ink-4)", textAlign:"center", padding:20, fontSize:13}}>
            Type a word, digits, or both — letters convert via the phone keypad. <br/>
            <span className="num">CARE</span> → <span className="num">2273</span> · <span className="num">247-BOOK</span> → <span className="num">2472665</span>
          </div>
        )}
        {vanity.trim() && !effectiveQuery && (
          <div style={{color:"var(--ink-4)", textAlign:"center", padding:20, fontSize:13}}>
            Keep typing — at least 2 keypad characters.
          </div>
        )}
        {effectiveQuery && !loading && items.length === 0 && (
          <div style={{color:"var(--ink-4)", textAlign:"center", padding:20, fontSize:13}}>
            No matches for <span className="num" style={{color:"var(--ink-2)"}}>{effectiveQuery}</span>{hasLetters ? <> ({vanity.trim()})</> : null}.
          </div>
        )}
        {items.length > 0 && (
          <div className="col g8" style={{opacity: loading ? 0.55 : 1, transition:"opacity .15s"}}>
            {items.map((r) => (
              <div key={r.id} className="row between" style={{padding:"12px 16px", borderRadius:"var(--radius)", background:"var(--bg-elev)", border:".5px solid var(--line)"}}>
                <div className="row g16">
                  <div className="num serif" style={{fontSize:20, color:"var(--ink)"}}>
                    <VanityHighlight e164={r.number} query={vanity}/>
                  </div>
                  {r.vanityPattern && <span className="pill info">{r.vanityPattern}</span>}
                </div>
                <div className="row g8">
                  <span className="pill good"><span className="dot"/>Available</span>
                  <button className="btn btn-primary btn-sm" onClick={() => onClaim(r)}>Claim</button>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function Spinner({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" style={{animation:"ct-spin 0.8s linear infinite"}}>
      <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" strokeWidth="2.5" strokeDasharray="40 60" strokeLinecap="round"/>
    </svg>
  );
}

// Render a phone number with the matched vanity-search substring highlighted.
//   e.g. e164="+18334227315", query="CARE" → "(833) 4CA-RE15"
//        with "CA" and "RE" rendered in the accent color.
//   For all-digit queries it just highlights the matching digits.
function VanityHighlight({ e164, query }) {
  if (!e164) return null;

  // Per-character map of the query: keep what the user TYPED (letters
  // preserved, dashes/spaces dropped) along with the keypad digit each
  // character corresponds to. Digits map to themselves.
  const queryChars = [];
  for (const raw of (query || "").toUpperCase()) {
    if (raw >= "0" && raw <= "9") queryChars.push({ char: raw, digit: raw });
    else if (KEYPAD[raw]) queryChars.push({ char: raw, digit: KEYPAD[raw] });
  }
  const queryDigits = queryChars.map((c) => c.digit).join("");

  // Pull out the 10-digit national portion of the E.164 (strip "+1")
  const m = String(e164).match(/^\+1(\d{10})$/);
  if (!m) return <>{fmtNumber(e164)}</>;
  const national = m[1];

  const matchStart = queryDigits.length >= 2 ? national.indexOf(queryDigits) : -1;
  const matchEnd = matchStart >= 0 ? matchStart + queryDigits.length : -1;

  // Walk the formatted string "(NPA) NXX-XXXX" and emit each char,
  // substituting matched digits with the user's typed character (in accent).
  const formatted = "(" + national.slice(0, 3) + ") " + national.slice(3, 6) + "-" + national.slice(6);
  const out = [];
  let nationalIdx = 0;
  for (let i = 0; i < formatted.length; i++) {
    const ch = formatted[i];
    const isDigit = ch >= "0" && ch <= "9";
    if (isDigit) {
      const inMatch = matchStart >= 0 && nationalIdx >= matchStart && nationalIdx < matchEnd;
      if (inMatch) {
        const glyph = queryChars[nationalIdx - matchStart].char;
        out.push(
          <span key={i} style={{color:"var(--accent)", fontWeight:600}}>{glyph}</span>
        );
      } else {
        out.push(<span key={i}>{ch}</span>);
      }
      nationalIdx++;
    } else {
      out.push(<span key={i}>{ch}</span>);
    }
  }
  return <>{out}</>;
}

function BrowseNumbers({ onClaim }) {
  // "all" → no prefix filter, all toll-free prefixes mixed together
  const [prefix, setPrefix] = React.useState("all");
  const [contains, setContains] = React.useState("");
  const [page, setPage] = React.useState(1);

  const browse = useApi(
    () => API.inventory.browse({
      prefix: prefix === "all" ? undefined : prefix,
      contains: contains.trim() || undefined,
      page,
      limit: 12,
    }),
    [prefix, contains, page]
  );

  const items = browse.data || [];
  const meta = browse.meta || {};

  return (
    <div className="card-b" style={{padding:"28px"}}>
      <div className="row g12" style={{marginBottom:20, alignItems:"flex-end"}}>
        <div className="field"><label>Prefix</label>
          <div className="seg">
            {["all","800","833","844","855","866","877","888"].map((p) => (
              <button
                key={p}
                className={p === prefix ? "on" : ""}
                onClick={() => { setPrefix(p); setPage(1); }}
              >
                {p === "all" ? "All" : p}
              </button>
            ))}
          </div>
        </div>
        <div className="field" style={{flex:1}}><label>Contains digits</label>
          <input className="input" placeholder="e.g. 1234" value={contains} onChange={(e) => { setContains(e.target.value); setPage(1); }}/>
        </div>
      </div>
      {browse.loading && items.length === 0 && (
        <div style={{color:"var(--ink-4)", textAlign:"center", padding:20}}>Loading…</div>
      )}
      {!browse.loading && items.length === 0 && (
        <div style={{color:"var(--ink-4)", textAlign:"center", padding:20}}>No numbers found.</div>
      )}
      {items.length > 0 && (
        <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:10}}>
          {items.map((n) => (
            <div key={n.id} style={{padding:"14px", borderRadius:"var(--radius)", background:"var(--bg-elev)", border:".5px solid var(--line)", display:"flex", flexDirection:"column", gap:8}}>
              <div className="num" style={{fontSize:15, color:"var(--ink)", fontWeight:500}}>{fmtNumber(n.number)}</div>
              <div className="row between">
                <span style={{fontSize:11, color:"var(--ink-4)"}}>{n.vanityPattern || "$2/mo"}</span>
                <button className="btn btn-ghost btn-sm" style={{color:"var(--accent)"}} onClick={() => onClaim(n)}>
                  Claim <I.chev size={12}/>
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
      {meta.total != null && (
        <div style={{textAlign:"center", marginTop:16, fontSize:12, color:"var(--ink-4)"}}>
          Page {page} · {meta.total.toLocaleString()} total
          {" · "}
          {page > 1 && <button className="btn btn-ghost btn-sm" onClick={() => setPage((p) => Math.max(1, p - 1))}>Prev</button>}
          {meta.hasMore && <button className="btn btn-ghost btn-sm" onClick={() => setPage((p) => p + 1)}>Next</button>}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { Numbers, QuickPicker, VanitySearch, BrowseNumbers, Spinner, VanityHighlight, toKeypadDigits });
