import { and, eq, inArray } from "drizzle-orm";
import { canManage, createFamilyRequestSchema, hasRole, updateMemberRequestSchema, updateNotificationSettingsRequestSchema, updateSocialSettingsRequestSchema, } from "@familyalbum/shared";
import { ApiError, badRequest, forbidden, notFound } from "../../../core/errors.js";
import { db } from "../../../core/db/client.js";
import { buildAuthHooks } from "../../../core/plugins/auth.js";
import { jsonSchema } from "../../../core/lib/json-schema.js";
import { buildFamilyAuthHooks } from "../plugins/auth.js";
import { families, familyMembers } from "../db/schema.js";
import { users } from "../../../core/db/schema.js";
export function registerFamilyRoutes(app) {
    const { authenticate } = buildAuthHooks(app);
    const { requireFamilyMember } = buildFamilyAuthHooks(app);
    app.get("/families", { preHandler: [authenticate] }, async (request, reply) => {
        const user = request.user;
        const memberships = db
            .select()
            .from(familyMembers)
            .where(eq(familyMembers.userId, user.sub))
            .all();
        const ids = memberships.map((m) => m.familyId);
        const fams = ids.length === 0 ? [] : db.select().from(families).where(inArray(families.id, ids)).all();
        reply.send(fams.map((f) => {
            const m = memberships.find((mm) => mm.familyId === f.id);
            return {
                id: f.id,
                name: f.name,
                createdAt: f.createdAt.toISOString(),
                role: m?.role ?? "viewer",
                permission: m?.permission ?? "full",
                reactionsEnabled: f.reactionsEnabled,
                commentsEnabled: f.commentsEnabled,
                createdBy: f.createdBy,
            };
        }));
    });
    /**
     * Création d'une famille par un utilisateur existant — remplace
     * fa_create_family (transaction : famille + appartenance admin).
     */
    app.post("/families", {
        schema: { body: jsonSchema(createFamilyRequestSchema) },
        preHandler: [authenticate],
    }, async (request, reply) => {
        const user = request.user;
        const { name } = request.body;
        const id = db.transaction((tx) => {
            const [family] = tx.insert(families).values({ name, createdBy: user.sub }).returning().all();
            if (!family)
                throw new ApiError(500, "family_create_failed", "Échec de création");
            tx.insert(familyMembers)
                .values({ familyId: family.id, userId: user.sub, role: "admin" })
                .run();
            return family.id;
        });
        reply.status(201).send({ id });
    });
    app.get("/families/:familyId", { preHandler: [authenticate, requireFamilyMember({ familyParam: "familyId" })] }, async (request, reply) => {
        const ctx = request.familyContext;
        const [family] = db
            .select()
            .from(families)
            .where(eq(families.id, ctx.familyId))
            .limit(1)
            .all();
        if (!family)
            throw notFound();
        reply.send({
            id: family.id,
            name: family.name,
            createdAt: family.createdAt.toISOString(),
            role: ctx.role,
            permission: ctx.permission,
            reactionsEnabled: family.reactionsEnabled,
            commentsEnabled: family.commentsEnabled,
            createdBy: family.createdBy,
        });
    });
    app.get("/families/:familyId/members", { preHandler: [authenticate, requireFamilyMember({ familyParam: "familyId" })] }, async (request, reply) => {
        const ctx = request.familyContext;
        const members = db
            .select({
            userId: familyMembers.userId,
            role: familyMembers.role,
            permission: familyMembers.permission,
            joinedAt: familyMembers.joinedAt,
            displayName: users.displayName,
        })
            .from(familyMembers)
            .innerJoin(users, eq(users.id, familyMembers.userId))
            .where(eq(familyMembers.familyId, ctx.familyId))
            .all();
        const [ownerRow] = db
            .select({ createdBy: families.createdBy })
            .from(families)
            .where(eq(families.id, ctx.familyId))
            .limit(1)
            .all();
        const ownerId = ownerRow?.createdBy ?? null;
        reply.send(members.map((m) => ({
            userId: m.userId,
            displayName: m.displayName,
            role: m.role,
            permission: m.permission,
            isOwner: m.userId === ownerId,
            joinedAt: m.joinedAt.toISOString(),
        })));
    });
    /* ------------------------------------------------------------------ */
    /* Gestion des membres (statut + rôle)                                 */
    /* ------------------------------------------------------------------ */
    /**
     * Modifie le rôle et/ou le statut d'un membre. Garde-fous :
     * - pas sa propre ligne (anti-auto-verrouillage) ;
     * - pas le propriétaire de la famille (families.created_by) ;
     * - un coadmin ne touche que des viewers, et ne promeut personne ;
     * - le rôle « admin » est réservé au créateur — jamais assignable ici ;
     * - `permission` est ignorée si le rôle cible est gestionnaire (toujours full).
     */
    app.patch("/families/:familyId/members/:userId", {
        schema: { body: jsonSchema(updateMemberRequestSchema) },
        preHandler: [
            authenticate,
            requireFamilyMember({ familyParam: "familyId", minRole: "coadmin" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        const { role, permission } = request.body;
        if (params.userId === ctx.userId) {
            throw badRequest("Vous ne pouvez pas modifier votre propre ligne");
        }
        if (role === "admin") {
            throw badRequest("Le rôle admin est réservé au créateur de la famille");
        }
        const [target] = db
            .select()
            .from(familyMembers)
            .where(and(eq(familyMembers.familyId, ctx.familyId), eq(familyMembers.userId, params.userId)))
            .limit(1)
            .all();
        const [family] = db
            .select()
            .from(families)
            .where(eq(families.id, ctx.familyId))
            .limit(1)
            .all();
        if (!target)
            throw notFound("Membre introuvable");
        if (family && target.userId === family.createdBy) {
            throw badRequest("Le propriétaire de la famille ne peut pas être modifié");
        }
        const promoting = role !== undefined && role !== target.role && hasRole(role, "coadmin");
        if (!hasRole(ctx.role, "admin")) {
            if (target.role !== "viewer") {
                throw forbidden("Un administrateur ne peut modifier que des viewers");
            }
            if (promoting) {
                throw forbidden("Seul un admin peut promouvoir un membre");
            }
        }
        const nextRole = role ?? target.role;
        const nextPermission = canManage(nextRole) ? "full" : permission ?? target.permission;
        const [m] = db
            .update(familyMembers)
            .set({ role: nextRole, permission: nextPermission })
            .where(and(eq(familyMembers.familyId, ctx.familyId), eq(familyMembers.userId, params.userId)))
            .returning()
            .all();
        if (!m)
            throw notFound();
        const [userRow] = db
            .select({ displayName: users.displayName })
            .from(users)
            .where(eq(users.id, params.userId))
            .limit(1)
            .all();
        reply.send({
            userId: m.userId,
            displayName: userRow?.displayName ?? "",
            role: m.role,
            permission: m.permission,
            isOwner: family ? m.userId === family.createdBy : false,
            joinedAt: m.joinedAt.toISOString(),
        });
    });
    /* ------------------------------------------------------------------ */
    /* Réglages famille (social)                                           */
    /* ------------------------------------------------------------------ */
    /**
     * Active/désactive les réactions et commentaires pour TOUTES les photos
     * de la famille. Appliqué côté serveur (les routes sociales le vérifient).
     */
    app.patch("/families/:familyId/social", {
        schema: { body: jsonSchema(updateSocialSettingsRequestSchema) },
        preHandler: [
            authenticate,
            requireFamilyMember({ familyParam: "familyId", minRole: "coadmin" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const { reactionsEnabled, commentsEnabled } = request.body;
        const [row] = db
            .update(families)
            .set({
            ...(reactionsEnabled !== undefined ? { reactionsEnabled } : {}),
            ...(commentsEnabled !== undefined ? { commentsEnabled } : {}),
        })
            .where(eq(families.id, ctx.familyId))
            .returning()
            .all();
        if (!row)
            throw notFound();
        reply.send({
            reactionsEnabled: row.reactionsEnabled,
            commentsEnabled: row.commentsEnabled,
        });
    });
    /* ------------------------------------------------------------------ */
    /* Préférences de notification (toujours la propre ligne du membre)    */
    /* ------------------------------------------------------------------ */
    app.get("/families/:familyId/notifications", {
        preHandler: [authenticate, requireFamilyMember({ familyParam: "familyId" })],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const [row] = db
            .select()
            .from(familyMembers)
            .where(and(eq(familyMembers.familyId, ctx.familyId), eq(familyMembers.userId, ctx.userId)))
            .limit(1)
            .all();
        if (!row)
            throw notFound();
        reply.send({
            newMedia: row.notifyNewMedia,
            comments: row.notifyComments,
        });
    });
    app.patch("/families/:familyId/notifications", {
        schema: { body: jsonSchema(updateNotificationSettingsRequestSchema) },
        preHandler: [authenticate, requireFamilyMember({ familyParam: "familyId" })],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const { newMedia, comments } = request.body;
        const [row] = db
            .update(familyMembers)
            .set({
            ...(newMedia !== undefined ? { notifyNewMedia: newMedia } : {}),
            ...(comments !== undefined ? { notifyComments: comments } : {}),
        })
            .where(and(eq(familyMembers.familyId, ctx.familyId), eq(familyMembers.userId, ctx.userId)))
            .returning()
            .all();
        if (!row)
            throw notFound();
        reply.send({
            newMedia: row.notifyNewMedia,
            comments: row.notifyComments,
        });
    });
}
//# sourceMappingURL=families.js.map