import type { FastifyInstance } from "fastify";
import type { Pool } from "mysql2/promise";
import type { RouteContext } from "./types.js";
import Stripe from "stripe";

const packIdSchema = {
  type: "string" as const,
  minLength: 1,
  maxLength: 50,
};

// Packs cash définis côté serveur (prix en EUR)
const CASH_PACKS: Record<
  string,
  {
    name: string;
    price: number;
    items: { hints: number; undos: number; replays: number };
    bonusPoints: number;
  }
> = {
  silver: {
    name: "Pack Silver",
    price: 1.99,
    items: { hints: 6, undos: 4, replays: 1 },
    bonusPoints: 40,
  },
  gold: {
    name: "Pack Gold",
    price: 3.99,
    items: { hints: 12, undos: 8, replays: 2 },
    bonusPoints: 90,
  },
  platinum: {
    name: "Pack Platinum",
    price: 6.99,
    items: { hints: 24, undos: 16, replays: 4 },
    bonusPoints: 180,
  },
};

const getPackById = async (db: Pool, packId: string) => {
  const [rows] = await db.execute(
    `SELECT pack_id, name, price, is_active
     FROM avatar_packs WHERE pack_id = ? AND is_active = 1 LIMIT 1`,
    [packId],
  );
  if (!Array.isArray(rows) || rows.length === 0) return null;
  const row = rows[0] as Record<string, unknown>;
  return {
    packId: row.pack_id as string,
    name: row.name as string,
    price: Number(row.price),
    isActive: Boolean(row.is_active),
  };
};

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 getBallSkinPackById = async (db: Pool, packId: string) => {
  const [rows] = await db.execute(
    `SELECT pack_id, name, 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;
  const row = rows[0] as Record<string, unknown>;
  const skinIds = parseSkinIds(row.skin_ids);
  return {
    packId: row.pack_id as string,
    name: row.name as string,
    price: Number(row.price),
    skinIds,
    skinCount: Number(row.skin_count) || skinIds.length,
    isActive: Boolean(row.is_active),
  };
};

const userOwnsPack = async (db: Pool, userId: number, packId: string) => {
  const [rows] = await db.execute(
    "SELECT 1 FROM user_avatar_packs WHERE user_id = ? AND pack_id = ? LIMIT 1",
    [userId, packId],
  );
  return Array.isArray(rows) && rows.length > 0;
};

const userOwnsBallSkinPack = 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;
};

const userOwnsChallengePack = async (db: Pool, userId: number) => {
  const [rows] = await db.execute(
    "SELECT 1 FROM user_challenge_pack WHERE user_id = ? LIMIT 1",
    [userId],
  );
  return Array.isArray(rows) && rows.length > 0;
};

const isChallengePackEnabled = async (db: Pool): Promise<boolean> => {
  const [rows] = await db.execute(
    "SELECT config_value FROM app_config WHERE config_key = 'challenge_pack_enabled' LIMIT 1",
  );
  const row = Array.isArray(rows)
    ? (rows[0] as { config_value?: unknown })
    : undefined;
  const value =
    typeof row?.config_value === "string"
      ? row.config_value.trim().toLowerCase()
      : "";
  return value === "1" || value === "true";
};

const userOwnsVipNoAds = async (db: Pool, userId: number) => {
  const [rows] = await db.execute(
    `SELECT vip_no_ads, vip_expires_at FROM users WHERE id = ?`,
    [userId],
  );
  if (!Array.isArray(rows) || rows.length === 0) return false;
  const row = rows[0] as {
    vip_no_ads?: number | boolean;
    vip_expires_at?: Date | string | null;
  };
  if (!row.vip_no_ads) return false;
  // Si pas de date d'expiration ou date future => VIP actif
  if (!row.vip_expires_at) return true;
  const expiresAt = new Date(row.vip_expires_at);
  return expiresAt > new Date();
};

export const registerPaymentsRoutes = async (
  app: FastifyInstance,
  ctx: RouteContext,
) => {
  const { db, config, requireAuth } = ctx;
  const stripeSecretKey = config.payments?.stripeSecretKey;
  const stripeWebhookSecret = config.payments?.stripeWebhookSecret;
  const stripe = stripeSecretKey
    ? new Stripe(stripeSecretKey, { apiVersion: "2026-01-28.clover" })
    : null;

  app.post(
    "/payments/stripe/checkout-session",
    {
      preHandler: requireAuth,
      schema: {
        body: {
          type: "object",
          required: ["packId"],
          additionalProperties: false,
          properties: { packId: packIdSchema },
        },
      },
    },
    async (request, reply) => {
      if (!stripe) {
        reply.code(503);
        return { error: "Stripe non configuré" };
      }
      const body = request.body as { packId?: string } | undefined;
      const packId = body?.packId?.trim();
      const userId = request.user?.sub;
      if (!userId || !packId) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      const pack = await getPackById(db, packId);
      if (!pack) {
        reply.code(404);
        return { error: "Pack introuvable" };
      }
      const alreadyOwned = await userOwnsPack(db, userId, packId);
      if (alreadyOwned) {
        reply.code(400);
        return { error: "Vous possédez déjà ce pack" };
      }
      const amountCents = Math.round(pack.price * 100);
      if (!Number.isFinite(amountCents) || amountCents <= 0) {
        reply.code(400);
        return { error: "Prix invalide" };
      }
      const appUrl = config.appUrl ?? "http://localhost:5173";
      const successUrl =
        config.payments?.stripeSuccessUrl ??
        `${appUrl}/#/shop?payment=success&session_id={CHECKOUT_SESSION_ID}`;
      const cancelUrl =
        config.payments?.stripeCancelUrl ?? `${appUrl}/#/shop?payment=cancel`;

      const session = await stripe.checkout.sessions.create({
        mode: "payment",
        success_url: successUrl,
        cancel_url: cancelUrl,
        line_items: [
          {
            price_data: {
              currency: "eur",
              unit_amount: amountCents,
              product_data: { name: pack.name },
            },
            quantity: 1,
          },
        ],
        metadata: {
          userId: String(userId),
          packId,
          packType: "avatar",
        },
      });

      await db.execute(
        `INSERT INTO payment_transactions (user_id, pack_id, pack_type, provider, status, amount, currency, stripe_session_id)
         VALUES (?, ?, 'avatar', 'stripe', 'created', ?, 'EUR', ?)`,
        [userId, packId, pack.price, session.id],
      );

      return {
        sessionId: session.id,
        url: session.url,
      };
    },
  );

  // Endpoint pour les packs cash (hints, undos, replays)
  app.post(
    "/payments/stripe/checkout-cash",
    {
      preHandler: requireAuth,
      schema: {
        body: {
          type: "object",
          required: ["packId"],
          additionalProperties: false,
          properties: { packId: packIdSchema },
        },
      },
    },
    async (request, reply) => {
      if (!stripe) {
        reply.code(503);
        return { error: "Stripe non configuré" };
      }
      const body = request.body as { packId?: string } | undefined;
      const packId = body?.packId?.trim();
      const userId = request.user?.sub;
      if (!userId || !packId) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      const pack = CASH_PACKS[packId];
      if (!pack) {
        reply.code(404);
        return { error: "Pack introuvable" };
      }
      const amountCents = Math.round(pack.price * 100);
      if (!Number.isFinite(amountCents) || amountCents <= 0) {
        reply.code(400);
        return { error: "Prix invalide" };
      }
      const appUrl = config.appUrl ?? "http://localhost:5173";
      const successUrl =
        config.payments?.stripeSuccessUrl ??
        `${appUrl}/#/shop?payment=success&session_id={CHECKOUT_SESSION_ID}`;
      const cancelUrl =
        config.payments?.stripeCancelUrl ?? `${appUrl}/#/shop?payment=cancel`;

      const session = await stripe.checkout.sessions.create({
        mode: "payment",
        success_url: successUrl,
        cancel_url: cancelUrl,
        line_items: [
          {
            price_data: {
              currency: "eur",
              unit_amount: amountCents,
              product_data: { name: pack.name },
            },
            quantity: 1,
          },
        ],
        metadata: {
          userId: String(userId),
          packId,
          packType: "cash",
        },
      });

      await db.execute(
        `INSERT INTO payment_transactions (user_id, pack_id, pack_type, provider, status, amount, currency, stripe_session_id)
         VALUES (?, ?, 'cash', 'stripe', 'created', ?, 'EUR', ?)`,
        [userId, `cash_${packId}`, pack.price, session.id],
      );

      return {
        sessionId: session.id,
        url: session.url,
      };
    },
  );

  // Endpoint pour les packs de billes (skins)
  app.post(
    "/payments/stripe/checkout-ball-skins",
    {
      preHandler: requireAuth,
      schema: {
        body: {
          type: "object",
          required: ["packId"],
          additionalProperties: false,
          properties: { packId: packIdSchema },
        },
      },
    },
    async (request, reply) => {
      if (!stripe) {
        reply.code(503);
        return { error: "Stripe non configuré" };
      }
      const body = request.body as { packId?: string } | undefined;
      const packId = body?.packId?.trim();
      const userId = request.user?.sub;
      if (!userId || !packId) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      const pack = await getBallSkinPackById(db, packId);
      if (!pack) {
        reply.code(404);
        return { error: "Pack introuvable" };
      }
      const alreadyOwned = await userOwnsBallSkinPack(db, userId, packId);
      if (alreadyOwned) {
        reply.code(400);
        return { error: "Vous possédez déjà ce pack" };
      }
      const amountCents = Math.round(pack.price * 100);
      if (!Number.isFinite(amountCents) || amountCents <= 0) {
        reply.code(400);
        return { error: "Prix invalide" };
      }
      const appUrl = config.appUrl ?? "http://localhost:5173";
      const successUrl =
        config.payments?.stripeSuccessUrl ??
        `${appUrl}/#/shop?payment=success&session_id={CHECKOUT_SESSION_ID}`;
      const cancelUrl =
        config.payments?.stripeCancelUrl ?? `${appUrl}/#/shop?payment=cancel`;

      const session = await stripe.checkout.sessions.create({
        mode: "payment",
        success_url: successUrl,
        cancel_url: cancelUrl,
        line_items: [
          {
            price_data: {
              currency: "eur",
              unit_amount: amountCents,
              product_data: { name: pack.name },
            },
            quantity: 1,
          },
        ],
        metadata: {
          userId: String(userId),
          packId,
          packType: "ball_skin",
        },
      });

      await db.execute(
        `INSERT INTO payment_transactions (user_id, pack_id, pack_type, provider, status, amount, currency, stripe_session_id)
         VALUES (?, ?, 'ball_skin', 'stripe', 'created', ?, 'EUR', ?)`,
        [userId, packId, pack.price, session.id],
      );

      return {
        sessionId: session.id,
        url: session.url,
      };
    },
  );

  // Endpoint pour le pack de défis
  app.post(
    "/payments/stripe/checkout-challenge-pack",
    {
      preHandler: requireAuth,
    },
    async (request, reply) => {
      if (!stripe) {
        reply.code(503);
        return { error: "Stripe non configuré" };
      }
      const userId = request.user?.sub;
      if (!userId) {
        reply.code(400);
        return { error: "Utilisateur non authentifié" };
      }
      const enabled = await isChallengePackEnabled(db);
      if (!enabled) {
        reply.code(400);
        return { error: "Le pack défis n'est pas disponible actuellement" };
      }
      const alreadyOwned = await userOwnsChallengePack(db, userId);
      if (alreadyOwned) {
        reply.code(400);
        return { error: "Vous possédez déjà ce pack" };
      }
      const packName = "Pack Défis";
      const packPrice = 4.99;
      const amountCents = Math.round(packPrice * 100);
      const appUrl = config.appUrl ?? "http://localhost:5173";
      const successUrl =
        config.payments?.stripeSuccessUrl ??
        `${appUrl}/#/shop?payment=success&session_id={CHECKOUT_SESSION_ID}`;
      const cancelUrl =
        config.payments?.stripeCancelUrl ?? `${appUrl}/#/shop?payment=cancel`;

      const session = await stripe.checkout.sessions.create({
        mode: "payment",
        success_url: successUrl,
        cancel_url: cancelUrl,
        line_items: [
          {
            price_data: {
              currency: "eur",
              unit_amount: amountCents,
              product_data: { name: packName },
            },
            quantity: 1,
          },
        ],
        metadata: {
          userId: String(userId),
          packId: "challenge_pack",
          packType: "challenge",
        },
      });

      await db.execute(
        `INSERT INTO payment_transactions (user_id, pack_id, pack_type, provider, status, amount, currency, stripe_session_id)
         VALUES (?, ?, 'challenge', 'stripe', 'created', ?, 'EUR', ?)`,
        [userId, "challenge_pack", packPrice, session.id],
      );

      return {
        sessionId: session.id,
        url: session.url,
      };
    },
  );

  // Endpoint pour le pack VIP (no pubs + x2 rewards)
  app.post(
    "/payments/stripe/checkout-vip",
    {
      preHandler: requireAuth,
    },
    async (request, reply) => {
      if (!stripe) {
        reply.code(503);
        return { error: "Stripe non configuré" };
      }
      const userId = request.user?.sub;
      if (!userId) {
        reply.code(400);
        return { error: "Utilisateur non authentifié" };
      }
      const alreadyOwned = await userOwnsVipNoAds(db, userId);
      if (alreadyOwned) {
        reply.code(400);
        return { error: "Vous possédez déjà ce pack" };
      }

      const packId = "vip_no_ads";
      const packName = "Pack VIP (No pubs)";
      const packPrice = 9.99;
      const amountCents = Math.round(packPrice * 100);
      const appUrl = config.appUrl ?? "http://localhost:5173";
      const successUrl =
        config.payments?.stripeSuccessUrl ??
        `${appUrl}/#/shop?payment=success&session_id={CHECKOUT_SESSION_ID}`;
      const cancelUrl =
        config.payments?.stripeCancelUrl ?? `${appUrl}/#/shop?payment=cancel`;

      const session = await stripe.checkout.sessions.create({
        mode: "payment",
        success_url: successUrl,
        cancel_url: cancelUrl,
        line_items: [
          {
            price_data: {
              currency: "eur",
              unit_amount: amountCents,
              product_data: { name: packName },
            },
            quantity: 1,
          },
        ],
        metadata: {
          userId: String(userId),
          packId,
          packType: "vip",
        },
      });

      await db.execute(
        `INSERT INTO payment_transactions (user_id, pack_id, pack_type, provider, status, amount, currency, stripe_session_id)
         VALUES (?, ?, 'vip', 'stripe', 'created', ?, 'EUR', ?)`,
        [userId, packId, packPrice, session.id],
      );

      return {
        sessionId: session.id,
        url: session.url,
      };
    },
  );
};
