import { z } from "zod";
import { query } from "./db.js";
import { ApiError } from "./errors.js";
const putSchema = z
    .object({
    enabled: z.boolean(),
    muted_categories: z.array(z.string().min(1).max(80)).max(200),
    quiet_start: z.number().int().min(0).max(1439).nullable(),
    quiet_end: z.number().int().min(0).max(1439).nullable(),
    timezone: z.string().min(1).max(64).nullable(),
    updated_at: z.string().max(40).optional(),
})
    .refine((v) => (v.quiet_start === null) === (v.quiet_end === null), {
    message: "quiet_start and quiet_end come together",
})
    .refine((v) => v.quiet_start === null || v.timezone !== null, {
    message: "Quiet hours need a timezone",
});
function parse(schema, body) {
    const parsed = schema.safeParse(body);
    if (!parsed.success) {
        throw new ApiError(400, "invalid_body", parsed.error.issues[0]?.message ?? "Invalid body");
    }
    return parsed.data;
}
function doc(row) {
    if (!row) {
        return {
            enabled: true,
            muted_categories: [],
            quiet_start: null,
            quiet_end: null,
            timezone: null,
            updated_at: null,
        };
    }
    return {
        enabled: row.enabled,
        muted_categories: row.muted_categories,
        quiet_start: row.quiet_start,
        quiet_end: row.quiet_end,
        timezone: row.timezone,
        updated_at: row.updated_at,
    };
}
export async function getPrefs(claims) {
    const res = await query(`SELECT * FROM notify_prefs WHERE user_id = $1 AND app_id = $2`, [claims.userId, claims.appId]);
    return doc(res.rows[0]);
}
const MAX_CLOCK_SKEW_MS = 5 * 60_000;
export async function putPrefs(claims, body) {
    const input = parse(putSchema, body);
    const now = new Date();
    let updatedAt = input.updated_at ? new Date(input.updated_at) : now;
    if (Number.isNaN(updatedAt.getTime())) {
        throw new ApiError(400, "invalid_body", "updated_at must be a datetime");
    }
    if (updatedAt.getTime() > now.getTime() + MAX_CLOCK_SKEW_MS) {
        updatedAt = now;
    }
    const res = await query(`INSERT INTO notify_prefs
       (user_id, app_id, enabled, muted_categories, quiet_start, quiet_end, timezone, updated_at)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
     ON CONFLICT (user_id, app_id) DO UPDATE SET
       enabled = EXCLUDED.enabled,
       muted_categories = EXCLUDED.muted_categories,
       quiet_start = EXCLUDED.quiet_start,
       quiet_end = EXCLUDED.quiet_end,
       timezone = EXCLUDED.timezone,
       updated_at = EXCLUDED.updated_at
     WHERE notify_prefs.updated_at <= EXCLUDED.updated_at
     RETURNING *`, [
        claims.userId,
        claims.appId,
        input.enabled,
        input.muted_categories,
        input.quiet_start,
        input.quiet_end,
        input.timezone,
        updatedAt,
    ]);
    if (res.rows[0])
        return doc(res.rows[0]);
    return getPrefs(claims);
}
export async function loadPrefsMap(q, userIds, appId) {
    if (userIds.length === 0)
        return new Map();
    const res = await q.query(`SELECT * FROM notify_prefs WHERE app_id = $1 AND user_id = ANY($2::uuid[])`, [appId, userIds]);
    return new Map(res.rows.map((r) => [r.user_id, r]));
}
