import type { FastifyInstance } from "fastify";
import type { Pool } from "mysql2/promise";
import type { RouteContext } from "./types.js";

const paramsUserIdSchema = {
  type: "object" as const,
  required: ["userId"],
  properties: { userId: { type: "string", pattern: "^[0-9]+$" } },
};

interface BallSkinPack {
  id: number;
  packId: string;
  name: string;
  description: string | null;
  price: number;
  skinIds: string[];
  skinCount: number;
  isActive: boolean;
}

const parseSkinIds = (value: unknown): string[] => {
  if (Array.isArray(value)) {
    return value.map((entry) => String(entry)).filter(Boolean);
  }
  if (typeof value === "string" && value.trim().length > 0) {
    try {
      const parsed = JSON.parse(value) as unknown;
      if (Array.isArray(parsed)) {
        return parsed.map((entry) => String(entry)).filter(Boolean);
      }
    } catch {
      return value
        .split(",")
        .map((entry) => entry.trim())
        .filter(Boolean);
    }
  }
  return [];
};

const mapPackRow = (row: Record<string, unknown>): BallSkinPack => {
  const skinIds = parseSkinIds(row.skin_ids);
  return {
    id: Number(row.id),
    packId: String(row.pack_id),
    name: String(row.name),
    description: typeof row.description === "string" ? row.description : null,
    price: Number(row.price),
    skinIds,
    skinCount:
      Number(row.skin_count) || (skinIds.length > 0 ? skinIds.length : 0),
    isActive: Boolean(row.is_active),
  };
};

const getAllActivePacks = async (db: Pool): Promise<BallSkinPack[]> => {
  const [rows] = await db.execute(
    `SELECT id, pack_id, name, description, price, skin_ids, skin_count, is_active
     FROM ball_skin_packs WHERE is_active = 1 ORDER BY id ASC`,
  );
  if (!Array.isArray(rows)) return [];
  return rows.map((row) => mapPackRow(row as Record<string, unknown>));
};

const getPackById = async (db: Pool, packId: string) => {
  const [rows] = await db.execute(
    `SELECT id, pack_id, name, description, price, skin_ids, skin_count, is_active
     FROM ball_skin_packs WHERE pack_id = ? AND is_active = 1 LIMIT 1`,
    [packId],
  );
  if (!Array.isArray(rows) || rows.length === 0) return null;
  return mapPackRow(rows[0] as Record<string, unknown>);
};

const userOwnsPack = async (db: Pool, userId: number, packId: string) => {
  const [rows] = await db.execute(
    "SELECT 1 FROM user_ball_skin_packs WHERE user_id = ? AND pack_id = ? LIMIT 1",
    [userId, packId],
  );
  return Array.isArray(rows) && rows.length > 0;
};

export const registerBallSkinPackRoutes = async (
  app: FastifyInstance,
  ctx: RouteContext,
) => {
  const { db, requireAuth } = ctx;

  app.get(
    "/ball-skins/user/:userId",
    { preHandler: requireAuth, schema: { params: paramsUserIdSchema } },
    async (request, reply) => {
      const params = request.params as { userId: string };
      const userId = Number(params.userId);
      const tokenUserId = request.user?.sub;
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      const [rows] = await db.execute(
        "SELECT skin_id FROM user_ball_skins WHERE user_id = ? AND skin_id IS NOT NULL AND skin_id <> '' ORDER BY unlocked_at DESC",
        [userId],
      );
      const skins = Array.isArray(rows)
        ? rows
            .map((row) => String((row as { skin_id: string }).skin_id))
            .filter((skinId) => skinId && skinId !== "null")
        : [];
      return { skins };
    },
  );

  app.get("/ball-skin-packs", { preHandler: requireAuth }, async (request) => {
    const userId = request.user?.sub;
    const packs = await getAllActivePacks(db);

    const packsWithOwnership = await Promise.all(
      packs.map(async (pack) => {
        const owned = userId
          ? await userOwnsPack(db, userId, pack.packId)
          : false;
        return {
          ...pack,
          priceDisplay: `${pack.price.toFixed(2)} €`,
          owned,
        };
      }),
    );

    return { packs: packsWithOwnership };
  });

  app.get(
    "/ball-skin-packs/:packId",
    { preHandler: requireAuth },
    async (request, reply) => {
      const packId = (request.params as { packId: string }).packId;
      const userId = request.user?.sub;

      const pack = await getPackById(db, packId);
      if (!pack) {
        reply.code(404);
        return { error: "Pack introuvable" };
      }

      const owned = userId ? await userOwnsPack(db, userId, packId) : false;
      return {
        pack: {
          ...pack,
          priceDisplay: `${pack.price.toFixed(2)} €`,
          owned,
        },
      };
    },
  );

  app.get(
    "/ball-skin-packs/user/:userId",
    { preHandler: requireAuth, schema: { params: paramsUserIdSchema } },
    async (request, reply) => {
      const params = request.params as { userId: string };
      const userId = Number(params.userId);
      if (!userId || Number.isNaN(userId)) {
        reply.code(400);
        return { error: "Utilisateur invalide" };
      }
      const [rows] = await db.execute(
        `SELECT p.pack_id, p.name, p.description, p.price, p.skin_ids, p.skin_count, u.purchased_at
         FROM user_ball_skin_packs u
         JOIN ball_skin_packs p ON p.pack_id = u.pack_id
         WHERE u.user_id = ?
         ORDER BY u.purchased_at DESC`,
        [userId],
      );
      const ownedPacks = Array.isArray(rows)
        ? rows.map((row) => {
            const pack = mapPackRow(row as Record<string, unknown>);
            return {
              ...pack,
              priceDisplay: `${pack.price.toFixed(2)} €`,
              owned: true,
              purchasedAt: String(
                (row as Record<string, unknown>).purchased_at,
              ),
            };
          })
        : [];

      return { ownedPacks };
    },
  );
};
