// iMessage/SMS bridge CORE — READ the Mac's Messages history from ~/Library/Messages/chat.db. Local,
// no cloud. Read-only (sending via AppleScript is unreliable on recent macOS, so omitted for now).
//
// Requires Full Disk Access for DewLink (System Settings → Privacy & Security → Full Disk Access →
// DewLink) — there's no prompt API for FDA, so we surface a clear instruction when the DB can't open.

import { DatabaseSync } from "node:sqlite";
import os from "node:os";
import path from "node:path";

const CHAT_DB = path.join(process.env.HOME || os.homedir(), "Library", "Messages", "chat.db");
// Apple Core Data epoch (2001-01-01) in unix seconds; message.date is ns since then on modern macOS.
const APPLE_EPOCH = 978307200;
const appleToISO = (d: number) => {
  if (!d) return null;
  const secs = d > 1e12 ? d / 1e9 : d; // ns (modern) vs legacy seconds
  return new Date((secs + APPLE_EPOCH) * 1000).toISOString();
};

function open(): DatabaseSync | { err: string } {
  try { return new DatabaseSync(CHAT_DB, { readOnly: true } as any); }
  catch (e: any) {
    const msg = String(e?.message ?? e);
    if (/authorization denied|operation not permitted|unable to open/i.test(msg)) {
      return { err: "Messages DB not accessible — grant DewLink Full Disk Access (System Settings → Privacy & Security → Full Disk Access → DewLink), then retry." };
    }
    return { err: msg };
  }
}

/// Recent conversations (1:1 + group) by last activity. Read-only.
export function imessageChats(a?: { limit?: number }) {
  const d = open(); if ("err" in d) return { ok: false, error: d.err };
  try {
    const limit = Math.min(a?.limit ?? 25, 100);
    const rows = d.prepare(
      `SELECT c.ROWID AS chat_id,
              COALESCE(NULLIF(c.display_name,''), c.chat_identifier) AS name,
              c.chat_identifier AS identifier,
              MAX(m.date) AS last_raw,
              SUM(CASE WHEN m.is_read=0 AND m.is_from_me=0 THEN 1 ELSE 0 END) AS unread
       FROM chat c
       JOIN chat_message_join cmj ON cmj.chat_id = c.ROWID
       JOIN message m ON m.ROWID = cmj.message_id
       GROUP BY c.ROWID ORDER BY MAX(m.date) DESC LIMIT ?`).all(limit) as any[];
    const chats = rows.map((r) => ({
      chat_id: r.chat_id, name: r.name, identifier: r.identifier,
      last_message_at: appleToISO(Number(r.last_raw)), unread: Number(r.unread) || 0,
    }));
    return { ok: true, count: chats.length, chats };
  } catch (e: any) { return { ok: false, error: String(e?.message ?? e) }; }
  finally { try { (d as DatabaseSync).close(); } catch {} }
}

/// Messages in a conversation (chat_id from imessage_chats), oldest→newest. Read-only.
export function imessageMessages(a: { chat_id: number; limit?: number }) {
  const d = open(); if ("err" in d) return { ok: false, error: d.err };
  try {
    const limit = Math.min(a.limit ?? 50, 500);
    const rows = d.prepare(
      `SELECT m.text, m.is_from_me, m.date,
              COALESCE(h.id,'') AS handle,
              (m.attributedBody IS NOT NULL AND m.text IS NULL) AS body_in_attr
       FROM message m
       JOIN chat_message_join cmj ON cmj.message_id = m.ROWID
       LEFT JOIN handle h ON h.ROWID = m.handle_id
       WHERE cmj.chat_id = ? ORDER BY m.date ASC LIMIT ?`).all(a.chat_id, limit) as any[];
    const messages = rows.map((r) => ({
      direction: r.is_from_me ? "OUT" : "IN",
      sender: r.is_from_me ? "You" : (r.handle || "Unknown"),
      text: r.text || (r.body_in_attr ? "[rich message]" : ""),
      timestamp: appleToISO(Number(r.date)),
    }));
    return { ok: true, count: messages.length, chat_id: a.chat_id, messages };
  } catch (e: any) { return { ok: false, error: String(e?.message ?? e) }; }
  finally { try { (d as DatabaseSync).close(); } catch {} }
}

/// Search message text across all conversations. Read-only.
export function imessageSearch(a: { query: string; limit?: number }) {
  if (!a.query) return { ok: false, error: "missing 'query'" };
  const d = open(); if ("err" in d) return { ok: false, error: d.err };
  try {
    const limit = Math.min(a.limit ?? 30, 200);
    const rows = d.prepare(
      `SELECT m.text, m.is_from_me, m.date, COALESCE(h.id,'') AS handle, cmj.chat_id AS chat_id
       FROM message m
       JOIN chat_message_join cmj ON cmj.message_id = m.ROWID
       LEFT JOIN handle h ON h.ROWID = m.handle_id
       WHERE m.text LIKE ? ORDER BY m.date DESC LIMIT ?`).all(`%${a.query}%`, limit) as any[];
    const matches = rows.map((r) => ({
      chat_id: r.chat_id, direction: r.is_from_me ? "OUT" : "IN",
      sender: r.is_from_me ? "You" : (r.handle || "Unknown"),
      text: r.text || "", timestamp: appleToISO(Number(r.date)),
    }));
    return { ok: true, count: matches.length, query: a.query, matches };
  } catch (e: any) { return { ok: false, error: String(e?.message ?? e) }; }
  finally { try { (d as DatabaseSync).close(); } catch {} }
}
