import { and, desc, eq, isNull } from "drizzle-orm";
import { canManage, createCommentRequestSchema, markViewedRequestSchema, setReactionRequestSchema, } from "@familyalbum/shared";
import { ApiError, 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 { comments, families, mediaViews, reactions } from "../db/schema.js";
import { users } from "../../../core/db/schema.js";
export function registerSocialRoutes(app) {
    const { authenticate } = buildAuthHooks(app);
    const { requireFamilyMember } = buildFamilyAuthHooks(app);
    /** Réglage famille : réactions ou commentaires coupés → 403 (bloqué côté
     * serveur, pas seulement côté client). */
    function assertFamilySocialEnabled(ctx, kind) {
        const [family] = db
            .select({
            reactionsEnabled: families.reactionsEnabled,
            commentsEnabled: families.commentsEnabled,
        })
            .from(families)
            .where(eq(families.id, ctx.familyId))
            .limit(1)
            .all();
        if (!family)
            throw notFound();
        const enabled = kind === "reactions" ? family.reactionsEnabled : family.commentsEnabled;
        if (!enabled) {
            // Code distinct par type — le client traduit (social_disabled_reactions
            // / social_disabled_comments).
            throw new ApiError(403, kind === "reactions" ? "social_disabled_reactions" : "social_disabled_comments", kind === "reactions"
                ? "Les réactions sont désactivées pour cette famille"
                : "Les commentaires sont désactivés pour cette famille");
        }
    }
    /* ------------------------------------------------------------------ */
    /* Commentaires                                                       */
    /* ------------------------------------------------------------------ */
    app.post("/media/:mediaId/comments", {
        schema: { body: jsonSchema(createCommentRequestSchema) },
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId", minPermission: "full" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        const { body } = request.body;
        assertFamilySocialEnabled(ctx, "comments");
        const c = db
            .insert(comments)
            .values({
            familyId: ctx.familyId,
            mediaId: params.mediaId,
            authorId: ctx.userId,
            body,
        })
            .returning()
            .all()[0];
        // Nom de l'auteur (jointure users) : l'insertion optimiste côté client
        // affiche le commentaire immédiatement — un nom vide faisait apparaître
        // le nouveau commentaire sans auteur jusqu'au rechargement.
        const [author] = db
            .select({ displayName: users.displayName })
            .from(users)
            .where(eq(users.id, ctx.userId))
            .limit(1)
            .all();
        reply.status(201).send({
            id: c.id,
            mediaId: c.mediaId,
            authorId: c.authorId,
            authorName: author?.displayName ?? "",
            body: c.body,
            createdAt: c.createdAt.toISOString(),
        });
    });
    app.get("/media/:mediaId/comments", {
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId", minPermission: "full" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        assertFamilySocialEnabled(ctx, "comments");
        const rows = db
            .select({
            id: comments.id,
            mediaId: comments.mediaId,
            authorId: comments.authorId,
            authorName: users.displayName,
            body: comments.body,
            createdAt: comments.createdAt,
        })
            .from(comments)
            .innerJoin(users, eq(users.id, comments.authorId))
            .where(and(eq(comments.mediaId, params.mediaId), isNull(comments.deletedAt)))
            .orderBy(desc(comments.createdAt))
            .all();
        reply.send(rows.map((c) => ({
            id: c.id,
            mediaId: c.mediaId,
            authorId: c.authorId,
            authorName: c.authorName,
            body: c.body,
            createdAt: c.createdAt.toISOString(),
        })));
    });
    app.delete("/media/:mediaId/comments/:commentId", {
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        const [c] = db
            .select()
            .from(comments)
            .where(and(eq(comments.id, params.commentId), eq(comments.mediaId, params.mediaId)))
            .limit(1)
            .all();
        if (!c)
            throw notFound();
        // auteur OU gestionnaire de la famille (admin/coadmin)
        if (c.authorId !== ctx.userId && !canManage(ctx.role)) {
            throw forbidden("Seul l'auteur ou un gestionnaire peut supprimer");
        }
        db.update(comments)
            .set({ deletedAt: new Date() })
            .where(eq(comments.id, c.id))
            .run();
        reply.send({ ok: true });
    });
    /* ------------------------------------------------------------------ */
    /* Réactions                                                          */
    /* ------------------------------------------------------------------ */
    app.post("/media/:mediaId/reactions", {
        schema: { body: jsonSchema(setReactionRequestSchema) },
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId", minPermission: "full" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        const { emoji, remove } = request.body;
        assertFamilySocialEnabled(ctx, "reactions");
        if (remove) {
            db.delete(reactions)
                .where(and(eq(reactions.mediaId, params.mediaId), eq(reactions.userId, ctx.userId)))
                .run();
        }
        else {
            db.insert(reactions)
                .values({ mediaId: params.mediaId, userId: ctx.userId, emoji })
                .onConflictDoUpdate({
                target: [reactions.mediaId, reactions.userId],
                set: { emoji },
            })
                .run();
        }
        reply.send({ ok: true });
    });
    /* ------------------------------------------------------------------ */
    /* Vus par (accusés de lecture)                                       */
    /* ------------------------------------------------------------------ */
    app.post("/media/:mediaId/viewed", {
        schema: { body: jsonSchema(markViewedRequestSchema) },
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        db.insert(mediaViews)
            .values({ mediaId: params.mediaId, userId: ctx.userId })
            .onConflictDoNothing()
            .run();
        reply.send({ ok: true });
    });
    /* ------------------------------------------------------------------ */
    /* Détail social d'un média (commentaires + réactions + vus par)      */
    /* ------------------------------------------------------------------ */
    app.get("/media/:mediaId/social", {
        preHandler: [
            authenticate,
            requireFamilyMember({ mediaParam: "mediaId", minPermission: "full" }),
        ],
    }, async (request, reply) => {
        const ctx = request.familyContext;
        const params = request.params;
        // Réglage famille : quand réactions ET commentaires sont coupés, le
        // détail social n'a plus rien à servir. Si un seul est coupé, l'autre
        // reste visible (le POST correspondant est déjà bloqué).
        const [fam] = db
            .select({
            reactionsEnabled: families.reactionsEnabled,
            commentsEnabled: families.commentsEnabled,
        })
            .from(families)
            .where(eq(families.id, ctx.familyId))
            .limit(1)
            .all();
        if (fam && !fam.reactionsEnabled && !fam.commentsEnabled) {
            throw new ApiError(403, "social_disabled_all", "Les réactions et commentaires sont désactivés pour cette famille");
        }
        const commentRows = db
            .select({
            id: comments.id,
            mediaId: comments.mediaId,
            authorId: comments.authorId,
            authorName: users.displayName,
            body: comments.body,
            createdAt: comments.createdAt,
        })
            .from(comments)
            .innerJoin(users, eq(users.id, comments.authorId))
            .where(and(eq(comments.mediaId, params.mediaId), isNull(comments.deletedAt)))
            .orderBy(desc(comments.createdAt))
            .all();
        const reactionRows = db
            .select()
            .from(reactions)
            .where(eq(reactions.mediaId, params.mediaId))
            .all();
        const viewerRows = db
            .select({
            userId: mediaViews.userId,
            displayName: users.displayName,
            firstViewedAt: mediaViews.firstViewedAt,
        })
            .from(mediaViews)
            .innerJoin(users, eq(users.id, mediaViews.userId))
            .where(eq(mediaViews.mediaId, params.mediaId))
            .orderBy(mediaViews.firstViewedAt)
            .all();
        const reactionsMap = {};
        for (const r of reactionRows) {
            (reactionsMap[r.emoji] ??= []).push(r.userId);
        }
        reply.send({
            comments: commentRows.map((c) => ({
                id: c.id,
                mediaId: c.mediaId,
                authorId: c.authorId,
                authorName: c.authorName,
                body: c.body,
                createdAt: c.createdAt.toISOString(),
            })),
            reactions: reactionsMap,
            viewers: viewerRows.map((v) => ({
                userId: v.userId,
                displayName: v.displayName,
                firstViewedAt: v.firstViewedAt.toISOString(),
            })),
        });
    });
}
//# sourceMappingURL=social.js.map