// Exercise 3: data fetching — fix the stale-request race condition
//
// `UserProfile` fetches a user by id whenever `userId` changes, using
// `fakeFetchUser` (simulates network latency with setTimeout — a SLOWER
// earlier request can resolve AFTER a faster later one). As written, it
// commits whatever resolves last in real time, not whatever was requested
// last — a classic race condition. Fix it with an `ignore` flag.
//
// TODO 1: implement fakeFetchUser(id, delayMs) — resolves { id, name } after delayMs
// TODO 2: add an `ignore` flag to the effect; only call setUser if !ignore;
//         set ignore = true in the cleanup function

import { useEffect, useState } from "react";

interface User {
  id: number;
  name: string;
}

// TODO 1: return a Promise<User> that resolves after delayMs milliseconds
// with { id, name: `User ${id}` }.
function fakeFetchUser(id: number, delayMs: number): Promise<User> {
  return new Promise((resolve) => {
    // TODO: setTimeout(() => resolve({ id, name: `User ${id}` }), delayMs);
  });
}

function UserProfile({ userId, delayMs }: { userId: number; delayMs: number }) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    // TODO 2: declare `let ignore = false;`, check `if (!ignore)` before
    // setUser, and return `() => { ignore = true; }` as cleanup.
    fakeFetchUser(userId, delayMs).then((data) => {
      setUser(data); // BUG: no staleness guard — a slow earlier request can overwrite a fast later one
    });
  }, [userId, delayMs]);

  return <p>{user ? user.name : "Loading..."}</p>;
}

export { UserProfile, fakeFetchUser };

// --- self-check: do not modify below this line ---
// Simulates: request userId=1 with a 100ms delay, then immediately request
// userId=2 with a 10ms delay. Without a staleness guard, request 1's
// result (slower) would overwrite request 2's (faster, and CORRECT/latest)
// when it finally resolves. This harness models the guarded version.
async function simulateRaceGuarded(): Promise<number> {
  let committed = -1;
  let ignore1 = false;

  fakeFetchUser(1, 100).then((data) => {
    if (!ignore1) committed = data.id; // guard checked
  });

  ignore1 = true; // React calls this effect's cleanup when userId switches to 2
  const data2 = await fakeFetchUser(2, 10);
  committed = data2.id;

  await new Promise((r) => setTimeout(r, 150)); // let request 1 resolve too, it must NOT overwrite
  return committed;
}

simulateRaceGuarded().then((finalId) => {
  console.log(finalId === 2 ? "PASS (stale request correctly ignored)" : "FAIL (stale request overwrote latest)");
});

// Verify visually: mount <UserProfile userId={1} delayMs={2000} />, then
// quickly change userId to 2 with a short delay before the first request
// resolves. With the ignore-flag fix, the screen should show "User 2" and
// STAY on "User 2" even after the slow request for user 1 finishes.
