/**
 * Google Calendar / Tasks / People (Contacts) two-way sync helpers.
 *
 * Used by MailxService to push local edits to Google and pull server
 * changes into the local cache. All functions take a `getToken` function
 * and a fetch implementation so they stay platform-agnostic (Node uses
 * global `fetch` on Node 18+; browsers use window.fetch).
 *
 * Error handling: throws on network / HTTP errors. Caller catches and
 * either retries via the store_sync drainer or surfaces to the UI.
 */
export class GoogleHttpError extends Error {
    status;
    statusText;
    body;
    constructor(status, statusText, body) {
        super(`Google API ${status} ${statusText}: ${body.slice(0, 300)}`);
        this.status = status;
        this.statusText = statusText;
        this.body = body;
    }
}
async function googleFetch(tokenProvider, url, init = {}) {
    const token = await tokenProvider();
    const headers = new Headers(init.headers || {});
    headers.set("Authorization", `Bearer ${token}`);
    if (init.body && !headers.has("Content-Type"))
        headers.set("Content-Type", "application/json");
    const res = await fetch(url, { ...init, headers });
    if (!res.ok) {
        const body = await res.text().catch(() => "");
        throw new GoogleHttpError(res.status, res.statusText, body);
    }
    return res;
}
export async function listCalendarEvents(tokenProvider, fromMs, toMs, calendarId = "primary") {
    const from = new Date(fromMs).toISOString();
    const to = new Date(toMs).toISOString();
    const url = `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events`
        + `?timeMin=${encodeURIComponent(from)}&timeMax=${encodeURIComponent(to)}`
        + `&singleEvents=true&orderBy=startTime&maxResults=250`;
    const all = [];
    let pageToken;
    do {
        const pagedUrl = pageToken ? `${url}&pageToken=${encodeURIComponent(pageToken)}` : url;
        const res = await googleFetch(tokenProvider, pagedUrl);
        const data = await res.json();
        for (const ev of (data.items || []))
            all.push(ev);
        pageToken = data.nextPageToken;
    } while (pageToken);
    return all;
}
export async function listCalendars(tokenProvider) {
    const url = "https://www.googleapis.com/calendar/v3/users/me/calendarList?minAccessRole=reader&maxResults=250";
    const out = [];
    let pageToken;
    do {
        const pagedUrl = pageToken ? `${url}&pageToken=${encodeURIComponent(pageToken)}` : url;
        const res = await googleFetch(tokenProvider, pagedUrl);
        const data = await res.json();
        for (const item of (data.items || [])) {
            const dr = Array.isArray(item.defaultReminders)
                ? item.defaultReminders
                    .filter((r) => (r.method === "popup" || !r.method) && Number.isFinite(r.minutes))
                    .map((r) => r.minutes)
                : [];
            out.push({
                id: item.id,
                summary: item.summary || item.id,
                selected: !!item.selected,
                primary: !!item.primary,
                backgroundColor: item.backgroundColor || "",
                defaultReminderMinutes: dr,
            });
        }
        pageToken = data.nextPageToken;
    } while (pageToken);
    return out;
}
export async function createCalendarEvent(tokenProvider, event, calendarId = "primary") {
    const url = `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events`;
    const res = await googleFetch(tokenProvider, url, { method: "POST", body: JSON.stringify(event) });
    return res.json();
}
export async function updateCalendarEvent(tokenProvider, eventId, event, calendarId = "primary") {
    const url = `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(eventId)}`;
    const res = await googleFetch(tokenProvider, url, { method: "PATCH", body: JSON.stringify(event) });
    return res.json();
}
export async function deleteCalendarEvent(tokenProvider, eventId, calendarId = "primary") {
    const url = `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(eventId)}`;
    await googleFetch(tokenProvider, url, { method: "DELETE" });
}
export async function listTaskLists(tokenProvider) {
    const url = "https://tasks.googleapis.com/tasks/v1/users/@me/lists?maxResults=100";
    const res = await googleFetch(tokenProvider, url);
    const data = await res.json();
    return (data.items || []).map((it) => ({ id: it.id, title: it.title || it.id }));
}
export async function listTasks(tokenProvider, listId = "@default", showCompleted = false) {
    const url = `https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(listId)}/tasks`
        + `?showCompleted=${showCompleted}&maxResults=100`;
    const res = await googleFetch(tokenProvider, url);
    const data = await res.json();
    return data.items || [];
}
export async function createTask(tokenProvider, task, listId = "@default") {
    const url = `https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(listId)}/tasks`;
    const res = await googleFetch(tokenProvider, url, { method: "POST", body: JSON.stringify(task) });
    return res.json();
}
export async function updateTask(tokenProvider, taskId, task, listId = "@default") {
    const url = `https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(listId)}/tasks/${encodeURIComponent(taskId)}`;
    const res = await googleFetch(tokenProvider, url, { method: "PATCH", body: JSON.stringify(task) });
    return res.json();
}
export async function deleteTask(tokenProvider, taskId, listId = "@default") {
    const url = `https://tasks.googleapis.com/tasks/v1/lists/${encodeURIComponent(listId)}/tasks/${encodeURIComponent(taskId)}`;
    await googleFetch(tokenProvider, url, { method: "DELETE" });
}
// ── Contacts (People API) ──
export async function createContact(tokenProvider, person) {
    const url = `https://people.googleapis.com/v1/people:createContact`;
    const res = await googleFetch(tokenProvider, url, { method: "POST", body: JSON.stringify(person) });
    return res.json();
}
export async function updateContact(tokenProvider, resourceName, updatePersonFields, person) {
    const url = `https://people.googleapis.com/v1/${resourceName}:updateContact`
        + `?updatePersonFields=${encodeURIComponent(updatePersonFields)}`;
    const res = await googleFetch(tokenProvider, url, { method: "PATCH", body: JSON.stringify(person) });
    return res.json();
}
export async function deleteContact(tokenProvider, resourceName) {
    const url = `https://people.googleapis.com/v1/${resourceName}:deleteContact`;
    await googleFetch(tokenProvider, url, { method: "DELETE" });
}
// ── Conversion helpers ──
/** Parse a Google all-day `date` ("YYYY-MM-DD") as LOCAL midnight.
 *  `Date.parse` treats a date-only string as UTC, so an all-day event
 *  renders a day early for any user west of UTC — a June 14 birthday
 *  showed as June 13 in US Eastern (Bob 2026-05-16). Build the Date from
 *  explicit Y/M/D components so midnight is local. */
function dateOnlyToLocalMs(d) {
    const m = d.match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (!m)
        return Date.parse(d);
    return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])).getTime();
}
export function calendarEventToLocal(ev, accountId, calendarDefaultReminderMinutes = []) {
    const allDay = !!ev.start.date && !ev.start.dateTime;
    const startMs = allDay
        ? dateOnlyToLocalMs(ev.start.date)
        : Date.parse(ev.start.dateTime);
    const endMs = allDay
        ? dateOnlyToLocalMs(ev.end.date)
        : Date.parse(ev.end.dateTime);
    // Per-event reminder offsets. Google sends `useDefault: true` to mean
    // "apply the calendar's defaultReminders" (which we passed in from the
    // calendarList entry). When `useDefault: false`, the event's own
    // `overrides` array wins. We only fire `popup` reminders client-side
    // — email reminders are Google's job, sending a duplicate from mailx
    // would be redundant. Bob 2026-05-12: "use the reminder times in the
    // event (there can be multiple)".
    let reminderMinutes = [];
    if (ev.reminders?.useDefault) {
        reminderMinutes = calendarDefaultReminderMinutes.slice();
    }
    else if (Array.isArray(ev.reminders?.overrides)) {
        reminderMinutes = ev.reminders.overrides
            .filter(r => (r.method === "popup" || !r.method) && Number.isFinite(r.minutes))
            .map(r => r.minutes);
    }
    return {
        providerId: ev.id, accountId,
        title: ev.summary || "(no title)",
        startMs, endMs, allDay,
        location: ev.location || "", notes: ev.description || "",
        etag: ev.etag || "",
        recurringEventId: ev.recurringEventId,
        htmlLink: ev.htmlLink,
        reminderMinutes,
    };
}
export function localToCalendarEvent(local) {
    const toIso = (ms) => new Date(ms).toISOString();
    const toDate = (ms) => new Date(ms).toISOString().slice(0, 10);
    return {
        summary: local.title,
        start: local.allDay ? { date: toDate(local.startMs) } : { dateTime: toIso(local.startMs) },
        end: local.allDay ? { date: toDate(local.endMs) } : { dateTime: toIso(local.endMs) },
        location: local.location || undefined,
        description: local.notes || undefined,
    };
}
export function taskToLocal(t, accountId) {
    return {
        providerId: t.id, accountId, title: t.title || "",
        notes: t.notes || "",
        dueMs: t.due ? Date.parse(t.due) : undefined,
        completedMs: t.completed ? Date.parse(t.completed) : undefined,
        etag: t.etag || "",
    };
}
export function localToTask(local) {
    return {
        title: local.title,
        notes: local.notes || undefined,
        due: local.dueMs ? new Date(local.dueMs).toISOString() : undefined,
        completed: local.completedMs ? new Date(local.completedMs).toISOString() : undefined,
        status: local.completedMs ? "completed" : "needsAction",
    };
}
//# sourceMappingURL=google-sync.js.map