
import { scrypt, randomBytes } from "crypto";
import { promisify } from "util";

const BASE_URL = "http://localhost:5001";
let cookie = "";

async function request(method: string, path: string, body?: any) {
    const headers: any = { "Content-Type": "application/json" };
    if (cookie) headers["Cookie"] = cookie;

    const res = await fetch(`${BASE_URL}${path}`, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined,
    });

    // Capture cookie
    const setCookie = res.headers.get("set-cookie");
    if (setCookie) {
        cookie = setCookie.split(";")[0];
    }

    const text = await res.text();
    // console.log("RAW RESPONSE:", text); // Uncomment if needed
    try {
        const data = JSON.parse(text);
        if (res.status >= 400 && data.error === "Failed to update task") {
            console.log("DEBUG ERROR RESPONSE:", text);
        }
        return { status: res.status, data };
    } catch {
        return { status: res.status, data: text };
    }
}

async function requestWithRetry(method: string, path: string, body?: any, retries = 5, delay = 2000) {
    for (let i = 0; i < retries; i++) {
        try {
            return await request(method, path, body);
        } catch (err: any) {
            if (i === retries - 1) throw err;
            if (err.cause && (err.cause.code === 'ECONNRESET' || err.cause.code === 'ECONNREFUSED')) {
                console.log(`Connection failed (${err.cause.code}). Retrying in ${delay}ms...`);
                await new Promise(r => setTimeout(r, delay));
            } else {
                throw err;
            }
        }
    }
    throw new Error("Request failed after retries");
}

async function run() {
    console.log("🚀 Starting Gamification E2E Test");

    // 1. Enable Registration (Just in case)
    // We can't easily do this via API without admin. 
    // Let's assume registration is open or we use the Setup flow if needed.
    // Actually, let's try to register. If 403, we try to login as admin?
    const username = `test_gamer_${Date.now()}`;
    const password = "password123";
    const email = `${username}@example.com`;

    console.log(`\n👤 Registering User: ${username}`);
    let res = await requestWithRetry("POST", "/api/register", {
        username,
        password,
        email,
        showOnLeaderboard: true
    });

    if (res.status === 403) {
        console.log("Registration disabled. Trying Setup...");
        // Try setup? Or maybe just login as admin? 
        // Let's assume we can create a user via raw storage import if API fails, 
        // but mixing contexts is messy.
        console.error("❌ Registration disabled and no admin handling in script. Aborting.");
        process.exit(1);
    }

    if (res.status !== 200 && res.status !== 201) {
        // Maybe already logged in or error
        console.error("❌ Registration failed:", res.data);
        // Try login
        res = await requestWithRetry("POST", "/api/login", { username, password });
        if (res.status !== 200) {
            console.error("❌ Login failed:", res.data);
            process.exit(1);
        }
    }
    const userId = res.data.id;
    console.log("✅ User Logged In. ID:", userId);

    // 2. Create a Task & Complete it for XP
    console.log("\n📝 Creating Task...");
    res = await request("POST", "/api/tasks", { title: "XP Grind Task", dueDate: null });

    if (res.status !== 201) {
        console.error("❌ Task Creation Failed:", res.status, JSON.stringify(res.data, null, 2));
    }
    const taskId = res.data?.id;
    console.log("Task Created:", taskId);

    if (taskId) {
        console.log("✅ Completing Task...");
        res = await request("PATCH", `/api/tasks/${taskId}`, { status: "done" });
        if (res.status !== 200) {
            console.error("❌ Task Completion Failed:", res.status, JSON.stringify(res.data, null, 2));
        }
        console.log("Task Update Result:", res.data.status);
    }

    // 3. Verify XP (via Leaderboard or Profile?)
    // We don't have a direct 'get me' endpoint that shows XP easily, 
    // but /api/user/history should show the event!

    console.log("\n📜 Checking History for XP Gain...");
    res = await request("GET", "/api/user/history");

    if (res.status !== 200) {
        console.error("❌ History Fetch Failed:", res.status, JSON.stringify(res.data, null, 2));
    }

    const history = Array.isArray(res.data) ? res.data : [];
    console.log("History Events:", history.length);
    const taskEvent = history.find((e: any) => e.source === 'task_completion');

    if (taskEvent) {
        console.log(`✅ Found Task Completion Event: +${taskEvent.amount} XP`);
    } else {
        console.error("❌ No Task Completion Event found!");
    }

    const clearEvent = history.find((e: any) => e.source === 'daily_clear_bonus');
    if (clearEvent) {
        console.log(`✅ Found Daily Clear Bonus: +${clearEvent.amount} XP`);
    } else {
        console.log("ℹ️ No Daily Clear Bonus (Normal if other tasks exist)");
    }

    // 4. Create a Reward (System allows user creation for now?)
    console.log("\n🎁 Creating Custom Reward...");
    res = await request("POST", "/api/rewards", {
        title: "Test Reward",
        description: "E2E Test Reward",
        cost: 10,
        type: "virtual",
        icon: "gift"
    });
    const rewardId = res.data.id;
    console.log("Reward Created:", rewardId);

    // 5. Verify Leaderboard
    console.log("\n🏆 Verifying Leaderboard...");
    res = await request("GET", "/api/leaderboard");
    const leaderboard = Array.isArray(res.data) ? res.data : [];
    const me = leaderboard.find((u: any) => u.username === username);

    if (me) {
        if (me.xp > 0) {
            console.log(`✅ Leaderboard verified: User ${username} has ${me.xp} XP`);
        } else {
            console.error(`❌ Leaderboard Error: User has ${me.xp} XP (expected > 0)`);
        }
    } else {
        console.error("❌ Leaderboard Error: User not found in leaderboard");
        // Check privacy settings? Default is showOnLeaderboard=false
        // Oops, default is false in schema? Let's check schema/storage.
    }

    // 6. Purchase Reward
    console.log("\n💰 Purchasing Reward...");
    res = await request("POST", "/api/rewards/purchase", { rewardId: rewardId });
    if (res.status === 200) {
        console.log("✅ Purchase Successful");
    } else {
        console.error("❌ Purchase Failed:", res.data);
    }

    // 6. Verify Inventory
    console.log("\n🎒 Checking Inventory...");
    res = await request("GET", "/api/user/inventory");
    const inventory = Array.isArray(res.data) ? res.data : [];
    const item = inventory.find((i: any) => i.rewardId === rewardId);
    if (item) {
        console.log("✅ Reward found in Inventory!");
        console.log("Inventory Item:", item);
    } else {
        console.error("❌ Reward NOT found in Inventory.");
        console.log("Full Inventory Response:", JSON.stringify(inventory, null, 2));
    }

    // 7. Verify Privacy: User 2 should NOT see User 1's custom reward
    console.log("\n🕵️ Checking Privacy...");
    const user2 = `test_gamer_2_${Date.now()}`;
    await request("POST", "/api/logout"); // Logout User 1

    console.log(`Registering User 2: ${user2}`);
    res = await requestWithRetry("POST", "/api/register", { username: user2, password: "password123", email: `${user2}@example.com` });

    if (res.status === 201) {
        console.log("User 2 Logged In");
        res = await request("GET", "/api/rewards");
        const rewards = Array.isArray(res.data) ? res.data : [];
        const privateReward = rewards.find((r: any) => r.id === rewardId);

        if (privateReward) {
            console.error("❌ PRIVACY FAIL: User 2 can see User 1's custom reward!");
            console.error("Reward:", privateReward);
        } else {
            console.log("✅ PRIVACY SUCCESS: User 2 cannot see private reward.");
        }
    } else {
        console.warn("⚠️ Could not register User 2, skipping privacy check.");
    }

    console.log("\n✅ Test Complete.");
}

run().catch(console.error);
