import { describe, it, expect, beforeEach, afterEach } from "vitest";
import mysql from "mysql2/promise";

const shouldRun = process.env.RUN_DB_TESTS === "true";
const maybeDescribe = shouldRun ? describe : describe.skip;

const readEnv = (key: string): string => {
  const value = process.env[key];
  if (!value) {
    throw new Error(`Missing env var: ${key}`);
  }
  return value;
};

maybeDescribe("challenge pack routes", () => {
  let pool: mysql.Pool;

  beforeEach(async () => {
    const host = readEnv("DB_HOST");
    const port = Number(process.env.DB_PORT ?? "3306");
    const user = readEnv("DB_USER");
    const database = readEnv("DB_NAME");
    const password = readEnv("DB_PASSWORD");

    pool = mysql.createPool({
      host,
      port,
      user,
      password,
      database,
      connectionLimit: 1,
    });
  });

  afterEach(async () => {
    await pool.end();
  });

  it("crée une entrée challenge pack pour un utilisateur", async () => {
    const email = `challenge-${Date.now()}@test.local`;
    const [r] = await pool.execute(
      "INSERT INTO users (email, display_name, password_hash) VALUES (?, ?, ?)",
      [email, "Challenge Player", "dummy"],
    );
    const userId = (r as any).insertId;

    // Ajoute le pack de défis
    await pool.execute(
      "INSERT INTO user_challenge_pack (user_id, purchased_at) VALUES (?, NOW())",
      [userId],
    );

    // Vérifie qu'on peut le retrouver
    const [rows] = await pool.execute(
      "SELECT user_id FROM user_challenge_pack WHERE user_id = ? LIMIT 1",
      [userId],
    );

    expect((rows as any[]).length).toBe(1);

    // Cleanup
    await pool.execute("DELETE FROM user_challenge_pack WHERE user_id = ?", [
      userId,
    ]);
    await pool.execute("DELETE FROM users WHERE id = ?", [userId]);
  });

  it("empêche un utilisateur de payer deux fois le challenge pack", async () => {
    const email = `challenge-duplicate-${Date.now()}@test.local`;
    const [r] = await pool.execute(
      "INSERT INTO users (email, display_name, password_hash) VALUES (?, ?, ?)",
      [email, "Duplicate Player", "dummy"],
    );
    const userId = (r as any).insertId;

    // Premier achat
    await pool.execute(
      "INSERT INTO user_challenge_pack (user_id, purchased_at) VALUES (?, NOW())",
      [userId],
    );

    // Tentative du deuxième achat (user_id est PRIMARY KEY → UNIQUE)
    try {
      await pool.execute(
        "INSERT INTO user_challenge_pack (user_id, purchased_at) VALUES (?, NOW())",
        [userId],
      );
      expect.fail("Deuxième insertion devrait échouer");
    } catch (err) {
      expect((err as any).message).toContain("Duplicate");
    }

    // Cleanup
    await pool.execute("DELETE FROM user_challenge_pack WHERE user_id = ?", [
      userId,
    ]);
    await pool.execute("DELETE FROM users WHERE id = ?", [userId]);
  });

  it("enregistre et lit la progression quotidienne (daily_progress)", async () => {
    const email = `challenge-progress-${Date.now()}@test.local`;
    const [r] = await pool.execute(
      "INSERT INTO users (email, display_name, password_hash) VALUES (?, ?, ?)",
      [email, "Progress Tester", "dummy"],
    );
    const userId = (r as any).insertId;

    // Enregistre une progression
    await pool.execute(
      "INSERT INTO daily_progress (user_id, completed, updated_at) VALUES (?, ?, NOW())",
      [userId, JSON.stringify([1, 2, 3])],
    );

    // Vérifie
    const [rows] = await pool.execute(
      "SELECT completed FROM daily_progress WHERE user_id = ?",
      [userId],
    );

    const completed = (rows as any[])[0].completed;
    expect(completed).toEqual([1, 2, 3]);

    // Cleanup
    await pool.execute("DELETE FROM daily_progress WHERE user_id = ?", [
      userId,
    ]);
    await pool.execute("DELETE FROM users WHERE id = ?", [userId]);
  });

  it("enregistre la progression hebdomadaire (weekly_progress)", async () => {
    const email = `challenge-weekly-${Date.now()}@test.local`;
    const [r] = await pool.execute(
      "INSERT INTO users (email, display_name, password_hash) VALUES (?, ?, ?)",
      [email, "Weekly Tester", "dummy"],
    );
    const userId = (r as any).insertId;
    const weekKey = "2026-W06";

    // Enregistre une progression hebdo
    await pool.execute(
      "INSERT INTO weekly_progress (user_id, week_key, completed_days, reward_claimed, updated_at) VALUES (?, ?, ?, 0, NOW())",
      [
        userId,
        weekKey,
        JSON.stringify([true, true, false, false, false, false, false]),
      ],
    );

    // Vérifie
    const [rows] = await pool.execute(
      "SELECT completed_days, reward_claimed FROM weekly_progress WHERE user_id = ? AND week_key = ?",
      [userId, weekKey],
    );

    const row = (rows as any[])[0];
    const days = row.completed_days;
    expect(days.filter(Boolean).length).toBe(2);
    expect(row.reward_claimed).toBe(0);

    // Cleanup
    await pool.execute(
      "DELETE FROM weekly_progress WHERE user_id = ? AND week_key = ?",
      [userId, weekKey],
    );
    await pool.execute("DELETE FROM users WHERE id = ?", [userId]);
  });

  it("gère les récompenses hebdomadaires (weekly_rewards)", async () => {
    const weekKey = `t-${String(Date.now()).slice(-7)}`;

    // Crée une récompense hebdo
    await pool.execute(
      "INSERT INTO weekly_rewards (week_key, avatar_id) VALUES (?, ?)",
      [weekKey, 1],
    );

    // Vérifie
    const [rows] = await pool.execute(
      "SELECT avatar_id FROM weekly_rewards WHERE week_key = ?",
      [weekKey],
    );

    expect((rows as any[]).length).toBe(1);
    expect((rows as any[])[0].avatar_id).toBe(1);

    // Cleanup
    await pool.execute("DELETE FROM weekly_rewards WHERE week_key = ?", [
      weekKey,
    ]);
  });

  it("crée des défis quotidiens (daily_challenges)", async () => {
    const dateKey = "2099-12-31"; // Date future pour ne pas interférer

    // Insère un défi quotidien
    await pool.execute(
      "INSERT INTO daily_challenges (date_key, difficulty, level_index) VALUES (?, ?, ?)",
      [dateKey, "hard", 42],
    );

    // Vérifie
    const [rows] = await pool.execute(
      "SELECT difficulty, level_index FROM daily_challenges WHERE date_key = ?",
      [dateKey],
    );

    const row = (rows as any[])[0];
    expect(row.difficulty).toBe("hard");
    expect(row.level_index).toBe(42);

    // Cleanup
    await pool.execute("DELETE FROM daily_challenges WHERE date_key = ?", [
      dateKey,
    ]);
  });

  it("empêche les doublons de défis quotidiens par date", async () => {
    const dateKey = "2099-12-30";

    await pool.execute(
      "INSERT INTO daily_challenges (date_key, difficulty, level_index) VALUES (?, ?, ?)",
      [dateKey, "easy", 1],
    );

    // Tentative de doublon (date_key est PK)
    try {
      await pool.execute(
        "INSERT INTO daily_challenges (date_key, difficulty, level_index) VALUES (?, ?, ?)",
        [dateKey, "medium", 5],
      );
      expect.fail("Deuxième insertion devrait échouer");
    } catch (err) {
      expect((err as any).message).toContain("Duplicate");
    }

    // Cleanup
    await pool.execute("DELETE FROM daily_challenges WHERE date_key = ?", [
      dateKey,
    ]);
  });
});
