// a. Working with Higher Order Function in JavaScript
function higherOrderFunction(operation, a, b) {
    return operation(a, b);
}
 
function add(x, y) {
    return x + y;
}
 
console.log(higherOrderFunction(add, 5, 3)); // Output: 8
 
// b. Using Callback and Creating a Callback Hell Situation
function registerUser(callback) {
  setTimeout(() => {
    console.log("1. User registered");
    callback();
  }, 1000);
}

function sendWelcomeEmail(callback) {
  setTimeout(() => {
    console.log("2. Welcome email sent");
    callback();
  }, 1000);
}

function loginUser(callback) {
  setTimeout(() => {
    console.log("3. User logged in");
    callback();
  }, 1000);
}

function getUserData() {
  setTimeout(() => {
    console.log("4. Fetched user data");
  }, 1000);
}

// Nested callbacks (callback hell)
registerUser(() => {
  sendWelcomeEmail(() => {
    loginUser(() => {
      getUserData();
    });
  });
});

 
// c. Working with XHR : Response
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", true);
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log("XHR Response:", JSON.parse(xhr.responseText));
    }
};
xhr.send();
 
// d. Dealing with Callback Hell Using Promise
function registerUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("1. User registered");
      resolve();
    }, 1000);
  });
}

function sendWelcomeEmail() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("2. Welcome email sent");
      resolve();
    }, 1000);
  });
}

function loginUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("3. User logged in");
      resolve();
    }, 1000);
  });
}

function getUserData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("4. Fetched user data");
      resolve();
    }, 1000);
  });
}

// Chaining Promises
registerUser()
  .then(sendWelcomeEmail)
  .then(loginUser)
  .then(getUserData)
  .catch((err) => {
    console.error("Error:", err);
  });
 
// e. Dealing with Promise Chaining and Async/Await
function registerUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("1. User registered");
      resolve();
    }, 1000);
  });
}

function sendWelcomeEmail() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("2. Welcome email sent");
      resolve();
    }, 1000);
  });
}

function loginUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("3. User logged in");
      resolve();
    }, 1000);
  });
}

function getUserData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("4. Fetched user data");
      resolve();
    }, 1000);
  });
}

// Using async/await
async function processUserFlow() {
  try {
    await registerUser();
    await sendWelcomeEmail();
    await loginUser();
    await getUserData();
    console.log("All steps completed successfully.");
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

processUserFlow();