<reponame>hykilpikonna/GraphicsCardScripts
import os
import re
import time

import requests
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

from bestbuy_utils import get_availability, avail_str, get_avail_v2
from utils import parse_retry, TelegramReporter, CSS

# Config
# Price increase ratio threshold (ignore everything higher than this ratio)
INCR_MAX = 0.2
ALERT_MODELS = ['3060 ti', '3070', '3070 ti', '3080']
ALERT_MIN_AVAILABLE = 2

# Telegram chat ID that receives update messages (could be a channel in @channel_id format)
# TG_RECEIVER = 1770239825
TG_RECEIVER = '@toronto_bestbuy_gpu'
# Telegram bot token
TG_TOKEN = os.environ['TG_TOKEN']
# Alert receiver telegram chat ID
ALERT_RECEIVER = -1001655384423

# Constants
MODELS = [
    ['3060 ti', 400,  132],
    ['3070 ti', 600,  167],
    ['3080 ti', 1200, 233],
    ['3050',    250,  73],
    ['3060',    330,  98],
    ['3070',    500,  154],
    ['3080',    700,  204],
    ['3090',    1500, 236],
]
USD_TO_CAD = 1.27
AVAIL_TABLE: dict[str, bool] = {}
IGNORED = []
TITLE_SHORTEN = re.compile('(rtx|nvidia|geforce|edition|gddr[56]x*|video|card)', flags=re.IGNORECASE)
TG = TelegramReporter(TG_TOKEN, TG_RECEIVER, ALERT_RECEIVER)


def shorten_title(title: str):
    short_title = TITLE_SHORTEN.sub('', title)
    while '  ' in short_title:
        short_title = short_title.replace('  ', ' ')
    return short_title.strip()


def parse_page(browser: Chrome):
    become_available = []

    # Parse page
    for item in browser.find_elements(By.CLASS_NAME, 'x-productListItem'):
        title = item.find_element(CSS, 'div[data-automation="productItemName"]').get_attribute('innerHTML')
        title = shorten_title(title)

        # Ignored
        if title in IGNORED:
            continue

        # Check availability
        avail = item.find_elements(CSS, 'div[data-automation="store-availability-messages"] span[data-automation="store-availability-checkmark"]')

        # Not available, check if it was previously available
        if len(avail) == 0:
            if title in AVAIL_TABLE:
                TG.send(f'Sold out: `{title}`')
                del AVAIL_TABLE[title]
            continue

        # Already sent availability message
        if title in AVAIL_TABLE:
            continue

        # Get price
        price = item.find_element(CSS, 'div[data-automation="product-pricing"] > span > div').get_attribute('innerHTML')
        price = round(float(price.replace(',', '')[1:]))
        price_usd = price / USD_TO_CAD

        # Find model
        lower = title.lower()
        model = [c for c in MODELS if c[0] in lower]
        if not model:
            continue
        model = model[0]

        # Calculate price increase
        price_incr = (price_usd - model[1]) / model[1]
        value = model[2] / price_usd * 100

        # Check incr threshold
        if price_incr > INCR_MAX:
            print(' '.join(title.split()[:2]), f'is in stock but price increase {price_incr * 100:.0f}% '
                                               f'is larger than defined threshold.')
            IGNORED.append(title)
            continue

        # Find link
        link = item.find_element(CSS, 'a').get_attribute('href')

        # Available and meets threshold criteria, notify user
        AVAIL_TABLE[title] = True
        msg = TG.send(f'{model[0].upper()} Became Available!\n'
                      f'\n'
                      f'${price:.0f} | {price_incr * 100:.0f}% Incr | Value: {value:.0f}\n'
                      f'- [{title}]({link})')
        become_available.append((title, model, link, msg, price, price_incr, value))

    # Notify user
    for tup in become_available:
        title, model, link, msg, price, price_incr, value = tup

        # Get availability
        avail = get_avail_v2(link)

        # Edit message
        msg.edit_text(f'{model[0].upper()} (${price:.0f} {price_incr * 100:.0f}% {value:.0f}) Became Available!\n'
                      f'\n'
                      f'[{title}]({link})\n'
                      f'{avail_str(avail[:5])}\n',
                      parse_mode='Markdown')

        # Check alert
        if model[0] in ALERT_MODELS:
            if sum(a.n for a in avail) >= ALERT_MIN_AVAILABLE:
                TG.alert()


if __name__ == '__main__':
    web_options = Options()
    # web_options.headless = True

    browser = Chrome(options=web_options)
    # browser.get('https://www.bestbuy.ca/en-ca/collection/graphics-cards-with-nvidia-chipset/349221')
    browser.get('https://www.bestbuy.ca/en-ca/collection/rtx-30-series-graphic-cards/316108')

    # parse_page(browser)
    # browser.close()

    TG.send('Bot restarted')

    # Refresh indefinitely
    while True:
        time.sleep(5)
        parse_retry(parse_page, browser)
        browser.refresh()
        time.sleep(2)
