import fetch from 'node-fetch';

import { playAudioFile } from 'audic';
import open from 'open';
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import AnonymizeUAPlugin from 'puppeteer-extra-plugin-anonymize-ua';
import AdblockerPlugin from 'puppeteer-extra-plugin-adblocker';
import fs from 'fs';

const url = 'https://www.bestbuy.com/gateway/graphql';
// toggle browser for platform.
const browser_path = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe';
//const browser_path = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';

puppeteer.use(StealthPlugin());
puppeteer.use(AnonymizeUAPlugin());
puppeteer.use(
  AdblockerPlugin({
    blockTrackers: true,
  }
));

const headers = {
  'Host': 'www.bestbuy.com',
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0',
  'Accept': 'application/json',
  'Accept-Language': 'en-US,en;q=0.5',
  'Accept-Encoding': 'gzip, deflate, br, zstd',
  'Referer': 'https://www.bestbuy.com/site/nvidia-geforce-rtx-5090-32gb-gddr7-graphics-card-dark-gun-metal/6614151.p?skuId=6614151',
  'X-REQUEST-ID': 'add-on-selector',
  'X-CLIENT-ID': 'ATTACH-accessories-in-variations',
  'Content-Type': 'application/json',
  'Origin': 'https://www.bestbuy.com',
  'Connection': 'keep-alive',
};

const skuIds = ["6614151","6616096","6617487","6614119","6614120","6616095","6616092","6614122","6616093","6616090","6616091","6615930","6615929","6615931"]

const queries = skuIds.map(skuId => ({
  skuId,
  query: `
    query AOS_fetchButtonStateData($zip: String!, $store: String!) {
      productBySkuId(skuId: "${skuId}") {
        fulfillmentOptions(
          input: {
            buttonState: {context: PDP, destinationZipCode: $zip, storeId: $store},
            shipping: {destinationZipCode: $zip},
            inStorePickup: {storeId: $store}
          }
        ) {
          buttonStates {
            buttonState
            planButtonState
            displayText
            skuId
          }
        }
      }
    }
  `
}));

//CHANGE YOUR STORES HERE
const params = [
    { zip: "97504", store: "2517" },
  ];

let lines = 0;
let found = false;
let winningResult = null;

function getProductUrl(skuId) {
  return `https://www.bestbuy.com/site/${skuId}.p?skuId=${skuId}`;
}

async function addToCart(productUrl) {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null,
    ignoreDefaultArgs: ["--disable-extensions"],
    dumpio: true,
    args: ["--start-maximized", "--no-sandbox", "--disable-setuid-sandbox"],
    executablePath: browser_path,
  });
  const page = await browser.newPage();

  // Load cookies
  try{
    const cookies = JSON.parse(fs.readFileSync('./cookies.json', 'utf8'));
    await page.setCookie(...cookies);
  }catch (error){
    console.error('An error occurred:',error)
  }


  // Load local storage data
  try{
    const localStorageData = JSON.parse(fs.readFileSync('./localStorage.json', 'utf8'));
    await page.evaluateOnNewDocument((data) => {
      for (const key in data) {
        localStorage.setItem(key, data[key]);
      }
    }, localStorageData);
  }catch (error){
    console.error('An error occurred:',error)
  }

  await page.goto(productUrl);

  // Wait for the "Add to Cart" button to be available and click it
  try{
    await page.waitForSelector('.add-to-cart-button',{ timeout: 300000 });
    await page.click('.add-to-cart-button');
    console.log('Item added to cart');
  
    await page.waitForSelector('button.btn-primary[data-track="Checkout - Top"]',{ timeout: 300000 });
    await page.click('button.btn-primary[data-track="Checkout - Top"]');
    console.log('Checkout button clicked');
  }catch (error){
    console.error('An error occurred:',error)
  }
}

async function fetchGraphQL(zip, store, query) {
  const body = {
    query,
    variables: { zip, store },
    operationName: "AOS_fetchButtonStateData"
  };

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers,
      body: JSON.stringify(body)
    });
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return await response.json();
  } catch (err) {
    console.error(`Error fetching for zip ${zip}, store ${store} query ${query}:`, err);
    return null;
  }
}

async function pollStore(zip, store) {
  console.log(`Starting poll for zip ${zip} and store ${store}...`);
  const batchSize = 5;
  while (!found) {
    const queryBatches = [];
    for (let i = 0; i < queries.length; i += batchSize) {
        queryBatches.push(queries.slice(i, i + batchSize));
    }

    for (const batch of queryBatches) {
        const fetchPromises = batch.map(({ skuId, query }) => fetchGraphQL(zip, store, query).then(data => ({ skuId, data })));
        const results = await Promise.all(fetchPromises);

        for (const { skuId, data } of results) {
            if (
                data &&
                data.data &&
                data.data.productBySkuId &&
                data.data.productBySkuId.fulfillmentOptions &&
                data.data.productBySkuId.fulfillmentOptions.buttonStates &&
                data.data.productBySkuId.fulfillmentOptions.buttonStates.length > 0
            ) {
                const buttonState = data.data.productBySkuId.fulfillmentOptions.buttonStates[0].buttonState;
                console.log(`skuId: ${skuId} Store: ${store} Ship to: ${zip} -> buttonState: ${buttonState}`);
                if (buttonState !== "SOLD_OUT") {
                    found = true;
                    const productUrl = getProductUrl(skuId);
                    console.log(productUrl);
                    winningResult = { skuId, zip, store, buttonState, data };
                    const openPromise = open(productUrl);
                    const addToCartPromise = addToCart(productUrl);
                    await open('./alarm.wav');
                    await addToCartPromise;
                    return winningResult;
                }
            } else {
                console.log(`Incomplete data for zip ${zip}, store ${store}.`);
            }
            lines++;
            if (lines > 30) {
                lines = 0;
                process.stdout.write('\x1B[3J\x1B[2J\x1B[H');
            }
            await new Promise(resolve => setTimeout(resolve, 500));
        }
    }
}
return null;
}

async function runAllPollers() {
  const pollers = params.map(({ zip, store }) => pollStore(zip, store));
  const result = await Promise.race(pollers);
  console.log("Found a store with available button state:");
  console.log(JSON.stringify(result, null, 2));
}

runAllPollers();
