import type { FastifyInstance } from "fastify";
import { and, desc, eq, sql } from "drizzle-orm";
import { schema } from "@app/database";
import { idParamSchema } from "@app/validation";
import { errors, parse } from "../lib/errors.js";

/**
 * Community & creators (spec §35–38).
 * Rankinglistor bakom feature flag; ALDRIG ranking på vikt/kalorier (spec §38).
 */
export async function communityRoutes(app: FastifyInstance) {
  const auth = { preHandler: [app.authenticate] };

  app.get("/v1/creators/:id", auth, async (req) => {
    const { id } = parse(idParamSchema, req.params);
    const [stats] = await app.db
      .select()
      .from(schema.creatorStats)
      .where(eq(schema.creatorStats.userId, id))
      .limit(1);
    const [user] = await app.db
      .select({ displayName: schema.users.displayName })
      .from(schema.users)
      .where(eq(schema.users.id, id))
      .limit(1);
    if (!user) throw errors.notFound("Profilen finns inte.");
    if (stats?.visibility === "private" && id !== req.userId) {
      throw errors.forbidden("Profilen är privat.");
    }

    const recipes = await app.db
      .select({
        id: schema.recipes.id,
        titleSv: schema.recipes.titleSv,
        ratingAverage: schema.recipes.ratingAverage,
        cookCount: schema.recipes.cookCount,
        verificationStatus: schema.recipes.verificationStatus,
        imageUrls: schema.recipes.imageUrls,
      })
      .from(schema.recipes)
      .where(and(eq(schema.recipes.creatorUserId, id), eq(schema.recipes.status, "published")))
      .orderBy(desc(schema.recipes.cookCount))
      .limit(30);

    return {
      userId: id,
      displayName: user.displayName,
      level: stats?.level ?? "beginner",
      followers: stats?.followers ?? 0,
      publishedRecipes: stats?.publishedRecipes ?? recipes.length,
      totalCooks: stats?.totalCooks ?? 0,
      averageRating: stats?.averageRating ?? null,
      badges: stats?.badges ?? [],
      recipes,
    };
  });

  app.post("/v1/creators/:id/follow", auth, async (req) => {
    const { id } = parse(idParamSchema, req.params);
    if (id === req.userId) throw errors.badRequest("Du kan inte följa dig själv.");
    await app.db
      .insert(schema.creatorFollows)
      .values({ followerUserId: req.userId, creatorUserId: id })
      .onConflictDoNothing();
    await app.db
      .insert(schema.creatorStats)
      .values({ userId: id, followers: 1 })
      .onConflictDoUpdate({
        target: schema.creatorStats.userId,
        set: { followers: sql`${schema.creatorStats.followers} + 1`, updatedAt: new Date() },
      });
    return { ok: true };
  });

  app.delete("/v1/creators/:id/follow", auth, async (req) => {
    const { id } = parse(idParamSchema, req.params);
    await app.db
      .delete(schema.creatorFollows)
      .where(
        and(
          eq(schema.creatorFollows.followerUserId, req.userId),
          eq(schema.creatorFollows.creatorUserId, id),
        ),
      );
    await app.db
      .update(schema.creatorStats)
      .set({ followers: sql`GREATEST(${schema.creatorStats.followers} - 1, 0)` })
      .where(eq(schema.creatorStats.userId, id));
    return { ok: true };
  });

  /** Topplistor (spec §38) – kräver flagga + minst 5 betyg för att synas. */
  app.get("/v1/rankings/:kind", auth, async (req) => {
    if (!(await app.flags.isEnabled("creator_rankings", req.userId))) {
      return { enabled: false, entries: [] };
    }
    const kind = (req.params as { kind: string }).kind;
    const MIN_RATINGS = 5;

    if (kind === "most-cooked") {
      const rows = await app.db
        .select({
          id: schema.recipes.id,
          titleSv: schema.recipes.titleSv,
          value: schema.recipes.cookCount,
        })
        .from(schema.recipes)
        .where(eq(schema.recipes.status, "published"))
        .orderBy(desc(schema.recipes.cookCount))
        .limit(20);
      return { enabled: true, kind, entries: rows };
    }
    if (kind === "top-rated") {
      const rows = await app.db
        .select({
          id: schema.recipes.id,
          titleSv: schema.recipes.titleSv,
          value: schema.recipes.ratingAverage,
        })
        .from(schema.recipes)
        .where(
          and(
            eq(schema.recipes.status, "published"),
            sql`${schema.recipes.ratingCount} >= ${MIN_RATINGS}`,
          ),
        )
        .orderBy(desc(schema.recipes.ratingAverage))
        .limit(20);
      return { enabled: true, kind, entries: rows };
    }
    if (kind === "budget") {
      const rows = await app.db
        .select({
          id: schema.recipes.id,
          titleSv: schema.recipes.titleSv,
          value: schema.recipes.estimatedCostMinorPerPortion,
        })
        .from(schema.recipes)
        .where(
          and(
            eq(schema.recipes.status, "published"),
            sql`${schema.recipes.estimatedCostMinorPerPortion} IS NOT NULL`,
          ),
        )
        .orderBy(schema.recipes.estimatedCostMinorPerPortion)
        .limit(20);
      return { enabled: true, kind, entries: rows };
    }
    if (kind === "protein") {
      const rows = await app.db
        .select({
          id: schema.recipes.id,
          titleSv: schema.recipes.titleSv,
          value: sql<number>`(${schema.recipes.nutritionPerPortion}->>'proteinG')::float`,
        })
        .from(schema.recipes)
        .where(eq(schema.recipes.status, "published"))
        .orderBy(desc(sql`(${schema.recipes.nutritionPerPortion}->>'proteinG')::float`))
        .limit(20);
      return { enabled: true, kind, entries: rows };
    }
    // Spec §38: ingen ranking på vikt, viktnedgång, kalorier eller BMI.
    throw errors.badRequest("Okänd rankingtyp.");
  });
}
