/**
 * HTTP client for the mac-sync server's `/my/*` surface.
 *
 * mac-sync owns the data (icloud.messages / contacts / prospect / calls /
 * outbox in Postgres), so this MCP talks to it directly — no quinn-api hop.
 * `/my/*` is gated by a single shared service token (`serviceTokenAuth`), so we
 * authenticate with one bearer token, not per-user SSO.
 */

// DO backend droplet (lilith-store-backend) — replaces dead homelan `black`.
// Override MAC_SYNC_BASE_URL to use the wg mesh IP (http://10.9.0.5:3201).
const BASE_URL = process.env['MAC_SYNC_BASE_URL'] ?? 'http://209.38.51.98:3201';
const TOKEN = process.env['MAC_SYNC_TOKEN'] ?? '';

interface Json {
  [k: string]: unknown;
}

/** Some `/my` routes wrap payloads in `{success, data}`, others return raw. */
function unwrap<T>(body: unknown): T {
  if (body && typeof body === 'object' && 'success' in body && 'data' in body) {
    return (body as { data: T }).data;
  }
  return body as T;
}

async function request<T>(path: string, init?: RequestInit): Promise<{ status: number; data: T }> {
  let res: Response;
  try {
    res = await fetch(`${BASE_URL}${path}`, {
      ...init,
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        Accept: 'application/json',
        ...(init?.body ? { 'Content-Type': 'application/json' } : {}),
        ...(init?.headers ?? {}),
      },
    });
  } catch (err) {
    throw new Error(`mac-sync ${init?.method ?? 'GET'} ${path} failed: ${String(err)}`);
  }
  const text = await res.text();
  const body = text ? (JSON.parse(text) as unknown) : null;
  if (!res.ok && res.status !== 404) {
    const msg = (body as Json | null)?.['error'] ?? res.statusText;
    throw new Error(`mac-sync ${path} → ${res.status}: ${JSON.stringify(msg)}`);
  }
  return { status: res.status, data: unwrap<T>(body) };
}

const qs = (params: Record<string, string | number | undefined>): string => {
  const u = new URLSearchParams();
  for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== '') u.set(k, String(v));
  const s = u.toString();
  return s ? `?${s}` : '';
};

// ---------------------------------------------------------------------------
// Messages / search (Handoff 01 consumer parity with quinn-m-mcp)
// ---------------------------------------------------------------------------

export async function searchMessages(query: string, mode = 'hybrid', limit = 20, offset = 0): Promise<unknown> {
  const { data } = await request(`/my/messages/search${qs({ q: query, mode, limit, offset })}`);
  return data;
}

export async function listConversations(limit = 50): Promise<unknown> {
  const { data } = await request(`/my/messages/conversations${qs({ limit })}`);
  return data;
}

export async function getThread(conversationId: string, limit = 50, since?: string): Promise<unknown> {
  const { data } = await request(
    `/my/messages/conversations/${encodeURIComponent(conversationId)}/messages${qs({ limit, since })}`,
  );
  return data;
}

export async function listContacts(deviceId?: string): Promise<unknown> {
  const { data } = await request(`/my/contacts${qs({ deviceId })}`);
  return data;
}

export async function searchContactsByName(name: string, limit = 10, deviceId?: string): Promise<unknown> {
  const { data } = await request(`/my/contacts/by-name${qs({ name, limit, deviceId })}`);
  return data;
}

// ---------------------------------------------------------------------------
// Prospects (Handoff 01)
// ---------------------------------------------------------------------------

export interface ProspectFilter {
  tier?: string;
  heat?: string;
  metro?: string;
  deviceId?: string;
  limit?: number;
}

export async function listProspects(filter: ProspectFilter = {}): Promise<unknown> {
  const { data } = await request(`/my/prospects${qs({ ...filter })}`);
  return data;
}

export async function getProspect(handle: string, deviceId: string): Promise<unknown | null> {
  const { status, data } = await request(`/my/prospects/${encodeURIComponent(handle)}${qs({ deviceId })}`);
  return status === 404 ? null : data;
}

// ---------------------------------------------------------------------------
// Calls (Handoff 02) — "did this lead also call?"
// ---------------------------------------------------------------------------

export interface CallsFilter {
  since?: string;
  direction?: string;
  callType?: string;
  deviceId?: string;
  limit?: number;
}

export async function recentCalls(filter: CallsFilter = {}): Promise<unknown> {
  const { data } = await request<{ calls: unknown[] }>(`/my/calls${qs({ ...filter })}`);
  return (data as { calls?: unknown[] })?.calls ?? data;
}

// ---------------------------------------------------------------------------
// Outbox (Handoff 03) — enqueue a paced, channel-smart reply + poll status
// ---------------------------------------------------------------------------

export interface EnqueueReplyInput {
  deviceId: string;
  recipient: string;
  body: string;
  channelPref?: 'auto' | 'imessage' | 'sms';
  recvAt?: number;
  notBefore?: string;
  dedupeKey?: string;
  markReadOnSend?: boolean;
}

export async function enqueueReply(input: EnqueueReplyInput): Promise<unknown> {
  const { data } = await request(`/my/outbox`, { method: 'POST', body: JSON.stringify(input) });
  return data;
}

export async function outboxStatus(id: string): Promise<unknown | null> {
  const { status, data } = await request(`/my/outbox/${encodeURIComponent(id)}`);
  return status === 404 ? null : data;
}

export async function outboxStats(deviceId: string): Promise<unknown> {
  const { data } = await request(`/my/outbox/stats${qs({ deviceId })}`);
  return data;
}

// ---------------------------------------------------------------------------
// Read-state (Handoff 04) — logical "mark conversation read" (never touches
// Apple's chat.db; macsync tracks handled/read in its own store).
// ---------------------------------------------------------------------------

export async function markConversationRead(
  deviceId: string,
  handle: string,
  readThrough?: string,
): Promise<unknown> {
  const { data } = await request(`/my/read`, {
    method: 'POST',
    body: JSON.stringify({ deviceId, handle, readThrough }),
  });
  return data;
}

export async function markAllRepliedRead(deviceId: string): Promise<unknown> {
  const { data } = await request(`/my/read/all-replied`, {
    method: 'POST',
    body: JSON.stringify({ deviceId }),
  });
  return data;
}

export async function conversationReadState(deviceId: string, handle: string): Promise<unknown> {
  const { data } = await request(`/my/read/${encodeURIComponent(handle)}${qs({ deviceId })}`);
  return data;
}
