import { z } from "zod";
/* ------------------------------------------------------------------ */
/* Primitives                                                          */
/* ------------------------------------------------------------------ */
export const idSchema = z.string().uuid();
export const emailSchema = z.string().email().max(320);
export const emojiSchema = z.string().min(1).max(16);
/* ------------------------------------------------------------------ */
/* Rôles & permissions                                                 */
/* ------------------------------------------------------------------ */
/** Rôle dans la famille — source unique consommée par serveur et mobile. */
export const roleSchema = z.enum(["admin", "coadmin", "viewer"]);
/**
 * Droits d'un viewer : la visu est toujours acquise (« view »), les options
 * + Réactions / + Téléchargement sont cumulables (choix multiple).
 * `full` = les deux options (ancien statut plein). Réglé uniquement sur les
 * viewers — les gestionnaires sont toujours en `full` implicitement.
 */
export const permissionSchema = z.enum(["full", "download", "reactions", "view"]);
const ROLE_RANK = { viewer: 0, coadmin: 1, admin: 2 };
/** `true` si `r` vaut au moins `min` dans la hiérarchie viewer < coadmin < admin. */
export const hasRole = (r, min) => ROLE_RANK[r] >= ROLE_RANK[min];
/** admin OU coadmin — gère la famille (réglages, invitations, médias…). */
export const canManage = (r) => hasRole(r, "coadmin");
/**
 * Peut télécharger / partager un média. Les gestionnaires oui, les viewers
 * uniquement si l'option « + Téléchargement » est active (`download`/`full`).
 */
export const canDownload = (r, p) => canManage(r) || p === "download" || p === "full";
/**
 * Peut réagir / commenter. Les gestionnaires oui, les viewers uniquement si
 * l'option « + Réactions » est active (`reactions`/`full`).
 */
export const canSocial = (r, p) => canManage(r) || p === "reactions" || p === "full";
/* ------------------------------------------------------------------ */
/* Auth                                                               */
/* ------------------------------------------------------------------ */
export const authTokenPairSchema = z.object({
    accessToken: z.string(),
    refreshToken: z.string(),
});
export const userSchema = z.object({
    id: idSchema,
    email: emailSchema.nullable(),
    displayName: z.string(),
    createdAt: z.string(),
});
export const familySchema = z.object({
    id: idSchema,
    name: z.string().min(1).max(120),
    createdAt: z.string(),
    role: roleSchema,
    /** Statut du membre courant dans cette famille (réglé sur les viewers). */
    permission: permissionSchema,
    /** Réglages famille (posés par admin/coadmin) — réactions & commentaires. */
    reactionsEnabled: z.boolean(),
    commentsEnabled: z.boolean(),
    /** Créateur de la famille (permet de dériver isOwner côté client). */
    createdBy: idSchema.nullable(),
});
export const registerRequestSchema = z.object({
    email: emailSchema,
    password: z.string().min(8).max(200),
    displayName: z.string().min(1).max(100),
    familyName: z.string().min(1).max(120),
});
export const loginRequestSchema = z.object({
    email: emailSchema,
    password: z.string().max(200),
});
/* --- Mot de passe oublié (preuve par Login Flow v2 Nextcloud) --- */
export const forgotPasswordRequestSchema = z.object({
    email: emailSchema,
});
/** Renvoie l'URL du Nextcloud familial à prouver — 404 générique sinon. */
export const forgotPasswordResponseSchema = z.object({
    baseUrl: z.string().url(),
});
/** Étape 1 du relais : l'URL doit correspondre à la connexion stockée (anti-SSRF). */
export const forgotPasswordLoginFlowStartRequestSchema = z.object({
    email: emailSchema,
    baseUrl: z.string().url().regex(/^https:\/\//i, "HTTPS requis"),
});
/** Étape 2 du relais : à la validation, renvoie un jeton de reset à usage unique. */
export const forgotPasswordLoginFlowPollRequestSchema = z.object({
    email: emailSchema,
    baseUrl: z.string().url().regex(/^https:\/\//i, "HTTPS requis"),
    endpoint: z.string().url(),
    token: z.string().min(1),
});
export const resetPasswordRequestSchema = z.object({
    resetToken: z.string().min(1),
    password: z.string().min(8).max(200),
});
export const refreshRequestSchema = z.object({
    refreshToken: z.string(),
});
export const logoutRequestSchema = z.object({
    refreshToken: z.string(),
});
export const authResponseSchema = z.object({
    user: userSchema,
    families: z.array(familySchema),
    tokens: authTokenPairSchema,
});
export const meResponseSchema = z.object({
    user: userSchema,
    families: z.array(familySchema),
});
export const familyMemberSchema = z.object({
    userId: idSchema,
    displayName: z.string(),
    role: roleSchema,
    permission: permissionSchema,
    /** Le membre est le créateur de la famille (intouchable). */
    isOwner: z.boolean(),
    joinedAt: z.string(),
});
/** Corps de PATCH /families/:familyId/members/:userId. */
export const updateMemberRequestSchema = z.object({
    role: roleSchema.optional(),
    permission: permissionSchema.optional(),
});
/** Corps de PATCH /families/:familyId/social (réglages famille). */
export const updateSocialSettingsRequestSchema = z.object({
    reactionsEnabled: z.boolean().optional(),
    commentsEnabled: z.boolean().optional(),
});
/** Préférences de notification du membre courant. */
export const notificationSettingsSchema = z.object({
    newMedia: z.boolean(),
    comments: z.boolean(),
});
export const updateNotificationSettingsRequestSchema = z.object({
    newMedia: z.boolean().optional(),
    comments: z.boolean().optional(),
});
export const createFamilyRequestSchema = z.object({
    name: z.string().min(1).max(120),
});
/* ------------------------------------------------------------------ */
/* Invitations                                                        */
/* ------------------------------------------------------------------ */
export const invitationCodeSchema = z
    .string()
    .regex(/^[A-Za-z0-9]{4}-?[A-Za-z0-9]{4}$/, "Format invalide (8 caractères, ex: H4TK9RQ2)");
export const createInvitationRequestSchema = z.object({
    role: roleSchema.default("viewer"),
    maxUses: z.number().int().min(1).max(1000).default(1),
    expiresInDays: z.number().int().min(1).max(365).default(30),
});
export const invitationSchema = z.object({
    id: idSchema,
    familyId: idSchema,
    role: roleSchema,
    code: invitationCodeSchema,
    expiresAt: z.string(),
    maxUses: z.number().int(),
    usedCount: z.number().int(),
    revokedAt: z.string().nullable(),
});
export const joinRequestSchema = z.object({
    code: invitationCodeSchema,
    displayName: z.string().min(1).max(100),
});
export const joinResponseSchema = z.object({
    user: userSchema,
    family: familySchema,
    tokens: authTokenPairSchema,
});
/* ------------------------------------------------------------------ */
/* Nextcloud connection                                               */
/* ------------------------------------------------------------------ */
export const connectionStatusSchema = z.enum([
    "active",
    "needs_reauth",
    "error",
]);
export const nextcloudLoginV2StartSchema = z.object({
    baseUrl: z.string().url().regex(/^https:\/\//i, "HTTPS requis"),
});
export const nextcloudLoginV2ResponseSchema = z.object({
    login: z.string().url(),
    poll: z.object({
        token: z.string(),
        endpoint: z.string().url(),
    }),
});
export const nextcloudLoginV2PollSchema = z.object({
    server: z.string().url(),
    loginName: z.string(),
    appPassword: z.string(),
});
/** Relaie du poll : l'endpoint vient de la réponse start (même origine). */
export const nextcloudLoginV2PollRequestSchema = z.object({
    baseUrl: z.string().url().regex(/^https:\/\//i, "HTTPS requis"),
    endpoint: z.string().url(),
    token: z.string().min(1),
});
export const connectRequestSchema = z.object({
    baseUrl: z.string().url().regex(/^https?:\/\//i, "URL invalide"),
    loginName: z.string().min(1).max(190),
    appPassword: z.string().min(1),
    folderName: z.preprocess(
    // Champ « optionnel » : une chaîne vide (ou espaces) vaut non renseigné
    // et laissera le serveur choisir le dossier par défaut.
    (v) => (typeof v === "string" && v.trim() === "" ? undefined : v), z
        .string()
        .regex(/^[A-Za-z0-9._ -]+$/, "Caractères interdits dans le nom de dossier")
        .max(100)
        .optional()),
});
export const connectionSchema = z.object({
    id: idSchema,
    familyId: idSchema,
    baseUrl: z.string().url(),
    loginName: z.string(),
    status: connectionStatusSchema,
    rootFolder: z.string().nullable(), rootFolderFileId: z.number().int().nullable(),
    lastSyncAt: z.string().nullable(),
});
/** Credentials Nextcloud renvoyés à l'admin pour l'upload direct téléphone → serveur. */
export const nextcloudCredentialsResponseSchema = z.object({
    baseUrl: z.string().url(),
    loginName: z.string(),
    appPassword: z.string().min(1),
    rootFolder: z.string().nullable(),
});
export const probeRequestSchema = z.object({
    baseUrl: z.string().url().regex(/^https?:\/\//i, "URL invalide"),
    loginName: z.string().min(1).max(190),
    appPassword: z.string().min(1),
});
export const capabilitiesSchema = z.object({
    version: z.string(),
    product: z.string(),
    chunking: z.boolean(),
    bigFileChunking: z.boolean(),
    supportsSyncCollection: z.boolean(),
    webhookListeners: z.boolean(),
});
export const probeResponseSchema = z.object({
    server: z.object({
        baseUrl: z.string().url(),
        loginName: z.string(),
    }),
    capabilities: capabilitiesSchema,
});
export const nextcloudFolderSchema = z.object({
    name: z.string().min(1).max(100),
    fileId: z.number().int().positive(),
    path: z.string(),
});
export const connectResponseSchema = z.object({
    connection: connectionSchema,
    folder: nextcloudFolderSchema,
});
/* ------------------------------------------------------------------ */
/* Media                                                              */
/* ------------------------------------------------------------------ */
export const capturedAtSourceSchema = z.enum([
    "filename",
    "mtime",
    "exif",
    "client",
]);
export const mediaSchema = z.object({
    id: idSchema,
    familyId: idSchema,
    ncFileId: z.number().int(),
    path: z.string(),
    name: z.string(),
    etag: z.string(),
    size: z.number().int().nonnegative(),
    mime: z.string(),
    capturedAt: z.string(),
    capturedAtSource: capturedAtSourceSchema,
    width: z.number().int().positive().nullable(),
    height: z.number().int().positive().nullable(),
    durationMs: z.number().int().nonnegative().nullable(),
    hasPreview: z.boolean(),
    uploadedBy: idSchema.nullable(),
    caption: z.string().max(2000).nullable(),
    /** Jeton de cache-busting après édition photo (`&v=<updatedAt>`). */
    updatedAt: z.string(),
});
/** Corps de PATCH /media/:mediaId/caption — null ou chaîne vide = efface. */
export const mediaCaptionUpdateSchema = z.object({
    caption: z.string().max(2000).nullable(),
});
export const mediaListQuerySchema = z.object({
    before: z.string().datetime().optional(),
    limit: z.number().int().min(1).max(200).default(50),
});
export const mediaListResponseSchema = z.object({
    items: z.array(mediaSchema),
    nextCursor: z.string().datetime().nullable(),
});
/** Mois disponibles de la timeline (« 2026-08 ») + nombre de médias, tri DESC. */
export const mediaMonthsResponseSchema = z.array(z.object({ month: z.string().regex(/^\d{4}-\d{2}$/), count: z.number().int().positive() }));
export const mediaNotifyRequestSchema = z.object({
    paths: z.array(z.string().min(1).max(500)).max(50),
});
/* ------------------------------------------------------------------ */
/* Comments / reactions / views                                       */
/* ------------------------------------------------------------------ */
export const commentSchema = z.object({
    id: idSchema,
    mediaId: idSchema,
    authorId: idSchema,
    authorName: z.string(),
    body: z.string().min(1).max(2000),
    createdAt: z.string(),
});
export const createCommentRequestSchema = z.object({
    mediaId: idSchema.optional(),
    body: z.string().min(1).max(2000),
});
export const reactionSchema = z.object({
    mediaId: idSchema,
    userId: idSchema,
    emoji: emojiSchema,
});
export const setReactionRequestSchema = z.object({
    mediaId: idSchema.optional(),
    emoji: emojiSchema,
    remove: z.boolean().default(false),
});
export const markViewedRequestSchema = z.object({
    mediaId: idSchema.optional(),
});
export const viewerSchema = z.object({
    userId: idSchema,
    displayName: z.string(),
    firstViewedAt: z.string(),
});
export const mediaDetailSchema = mediaSchema.extend({
    comments: z.array(commentSchema),
    reactions: z.record(emojiSchema, z.array(idSchema)),
    viewers: z.array(viewerSchema),
});
/* ------------------------------------------------------------------ */
/* Albums                                                             */
/* ------------------------------------------------------------------ */
export const albumSchema = z.object({
    id: idSchema,
    familyId: idSchema,
    name: z.string().min(1).max(120),
    createdAt: z.string(),
    coverMediaId: idSchema.nullable(),
});
export const createAlbumRequestSchema = z.object({
    familyId: idSchema.optional(),
    name: z.string().min(1).max(120),
});
export const albumDetailSchema = albumSchema.extend({
    mediaIds: z.array(idSchema),
});
export const addMediaToAlbumRequestSchema = z.object({
    mediaIds: z.array(idSchema).min(1).max(500),
});
/* ------------------------------------------------------------------ */
/* Children & growth                                                  */
/* ------------------------------------------------------------------ */
export const childSchema = z.object({
    id: idSchema,
    familyId: idSchema,
    name: z.string().min(1).max(100),
    birthDate: z.string(),
    avatarMediaId: idSchema.nullable(),
});
export const createChildRequestSchema = z.object({
    familyId: idSchema.optional(),
    name: z.string().min(1).max(100),
    birthDate: z.string().date(),
});
export const updateChildRequestSchema = z.object({
    name: z.string().min(1).max(100).optional(),
    birthDate: z.string().date().optional(),
    avatarMediaId: idSchema.nullable().optional(),
});
export const growthEntrySchema = z.object({
    id: idSchema,
    childId: idSchema,
    measuredOn: z.string(),
    heightCm: z.number().positive().nullable(),
    weightKg: z.number().positive().nullable(),
    note: z.string().max(500).nullable(),
});
export const createGrowthEntryRequestSchema = z.object({
    childId: idSchema.optional(),
    measuredOn: z.string().date(),
    heightCm: z.number().positive().max(250).optional(),
    weightKg: z.number().positive().max(200).optional(),
    note: z.string().max(500).optional(),
});
export const milestoneSchema = z.object({
    id: idSchema,
    childId: idSchema,
    happenedOn: z.string(),
    title: z.string().min(1).max(200),
    note: z.string().max(1000).nullable(),
});
export const createMilestoneRequestSchema = z.object({
    childId: idSchema.optional(),
    happenedOn: z.string().date(),
    title: z.string().min(1).max(200),
    note: z.string().max(1000).optional(),
});
/* ------------------------------------------------------------------ */
/* Devices / push                                                     */
/* ------------------------------------------------------------------ */
export const registerDeviceRequestSchema = z.object({
    expoPushToken: z.string().min(1).max(300),
    platform: z.enum(["ios", "android"]).optional(),
    /** BCP 47 du device (ex. "fr", "pt-BR") — sert au push localisé. */
    locale: z.string().regex(/^[a-z]{2}([_-][A-Za-z0-9]{2,8})?$/).max(16).optional(),
});
/* ------------------------------------------------------------------ */
/* Errors                                                             */
/* ------------------------------------------------------------------ */
export * from "./uploader.js";
export * from "./age.js";
export const apiErrorSchema = z.object({
    error: z.object({
        code: z.string(),
        message: z.string(),
    }),
});
//# sourceMappingURL=index.js.map