// Contacts bridge CORE — search the Mac's address book (Contacts.app) via AppleScript. Local, no cloud.
// Read-only for now (creating address-book entries can come later). Same engine-osascript pattern as Mail.

import { execFile } from "node:child_process";
const FS = String.fromCharCode(31), RS = String.fromCharCode(30);

function osa(script: string): Promise<{ ok: boolean; out: string; err?: string }> {
  return new Promise((resolve) => {
    execFile("osascript", ["-e", script], { maxBuffer: 4 * 1024 * 1024, timeout: 25_000 }, (e, stdout, stderr) => {
      if (e) {
        const err = (stderr || String(e)).trim();
        const perm = err.includes("-1743") || /not allowed|not authori[sz]ed/i.test(err);
        resolve({ ok: false, out: "", err: perm
          ? "Contacts automation not authorized yet — accept the prompt (or System Settings → Privacy & Security → Automation → DewLink → Contacts), then retry."
          : err });
      } else resolve({ ok: true, out: stdout });
    });
  });
}
const asStr = (s: string) => '"' + String(s ?? "").replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';

function parseContacts(out: string) {
  const clean = (s: string) => { const t = (s || "").trim(); return t && t !== "missing value" ? t : null; };
  return out.split(RS).filter((x) => x.trim().length > 0).map((row) => {
    const [name, email, phone, org] = row.split(FS);
    return { name: (name || "").trim(), email: clean(email), phone: clean(phone), org: clean(org) };
  });
}

/// List address-book people (name + first email/phone/org). Read-only.
export async function contactsList(a?: { limit?: number }) {
  const limit = Math.min(a?.limit ?? 50, 200);
  const r = await osa(`tell application "Contacts"
    set ppl to people
    set n to count of ppl
    if n > ${limit} then set n to ${limit}
    set out to ""
    repeat with i from 1 to n
      set p to item i of ppl
      set em to ""
      try
        set em to (value of first email of p)
      end try
      set ph to ""
      try
        set ph to (value of first phone of p)
      end try
      set org to ""
      try
        set org to (organization of p)
      end try
      set out to out & (name of p) & "${FS}" & em & "${FS}" & ph & "${FS}" & org & "${RS}"
    end repeat
    return out
  end tell`);
  if (!r.ok) return { ok: false, error: r.err };
  const contacts = parseContacts(r.out);
  return { ok: true, count: contacts.length, contacts };
}

/// Search the address book by name; returns name + first email + first phone. Read-only.
export async function contactsSearch(a: { query: string; limit?: number }) {
  if (!a.query) return { ok: false, error: "missing 'query'" };
  const limit = Math.min(a.limit ?? 20, 100);
  const r = await osa(`tell application "Contacts"
    set ppl to (people whose name contains ${asStr(a.query)})
    set n to count of ppl
    if n > ${limit} then set n to ${limit}
    set out to ""
    repeat with i from 1 to n
      set p to item i of ppl
      set em to ""
      try
        set em to (value of first email of p)
      end try
      set ph to ""
      try
        set ph to (value of first phone of p)
      end try
      set org to ""
      try
        set org to (organization of p)
      end try
      set out to out & (name of p) & "${FS}" & em & "${FS}" & ph & "${FS}" & org & "${RS}"
    end repeat
    return out
  end tell`);
  if (!r.ok) return { ok: false, error: r.err };
  const contacts = parseContacts(r.out);
  return { ok: true, count: contacts.length, query: a.query, contacts };
}
