#!/usr/bin/env python3

import asyncio
import datetime
import json
import os
import time
from argparse import ArgumentParser
from collections.abc import Sequence

import httpx
import uvloop
from dotenv import load_dotenv
from loguru import logger
from sqlmodel import Session, create_engine

from steam2sqlite import APPIDS_URL, BATCH_SIZE, navigator, utils
from steam2sqlite.handler import (
    get_appids_from_db,
    get_apps_achievements,
    get_apps_data,
    get_error_appids,
    store_apps_achievements,
    store_apps_data,
)

load_dotenv()

APPIDS_FILE = os.getenv("APPIDS_FILE")

sqlite_file_name = "database.db"
SQLITE_URL = f"sqlite:///{sqlite_file_name}"


async def get_appids_from_steam(local_file: str = None) -> dict[int, str]:
    if local_file:
        logger.info(f"Loading appids from local file: {local_file}")
        with open(local_file) as steam_appids_fp:
            appid_data = json.load(steam_appids_fp)
    else:
        logger.info("Loading appids from Steam API")
        try:
            async with httpx.AsyncClient() as client:
                resp = await navigator.get(client, APPIDS_URL)
            appid_data = resp.json()
            await asyncio.sleep(1)
        except navigator.NavigatorError:
            logger.error("Error getting the appids from Steam")
            raise

    return {item["appid"]: item["name"] for item in appid_data["applist"]["apps"]}


def main(argv: Sequence[str] | None = None) -> int:

    parser = ArgumentParser()
    parser.add_argument(
        "-l",
        "--limit",
        type=float,
        default=None,
        nargs="?",
        const=1,
        help="limit runtime (minutes)",
    )
    args = parser.parse_args(argv)

    logger.info("Starting...")

    start_time = time.monotonic()

    uvloop.install()

    engine = create_engine(SQLITE_URL, echo=False)

    # From steam api, dict of: {appids: names}
    steam_appids_names = asyncio.run(get_appids_from_steam(APPIDS_FILE))

    with Session(engine) as session:

        # query db for all appids we already have, sort by last_modified
        db_appids_updated = get_appids_from_db(session)

        # identify any missing appids -- these go on the top of our stack to process
        missing_appids = set(steam_appids_names.keys()) - {
            appid for appid, _ in db_appids_updated
        }

        # remove any appids that have been modified recently
        db_appids = [
            appid
            for appid, updated in db_appids_updated
            if ((datetime.datetime.utcnow().date() - updated.date()).days > 3)
        ]

        appids_missing_and_older = list(missing_appids) + db_appids

        # remove any appids that have been flagged as errors from previous runs
        error_appids = get_error_appids(session)
        appids_to_process = [
            appid
            for appid in appids_missing_and_older
            if appid not in set(error_appids)
        ]

        logger.info("Loading app data from Steam API and saving to db")

        for appids in utils.grouper(appids_to_process, BATCH_SIZE, fillvalue=None):

            apps_data = get_apps_data(session, steam_appids_names, appids)
            apps = store_apps_data(session, steam_appids_names, apps_data)

            apps_with_achievements = [app for app in apps if app.achievements_total > 0]
            if apps_with_achievements:
                apps_achievements_data = utils.delay_by(len(apps_with_achievements))(
                    get_apps_achievements
                )(apps_with_achievements)
                store_apps_achievements(session, apps_achievements_data)

            if args.limit and (time.monotonic() - start_time) / 60 > args.limit:
                logger.info(f"Limit ({args.limit} min) reached shutting down...")
                break

    return 0


if __name__ == "__main__":
    exit(main())

steam2sqlite.handler.store_apps_data sqlmodel.create_engine steam2sqlite.handler.get_apps_data uvloop.install argparse.ArgumentParser dotenv.load_dotenv asyncio.sleep steam2sqlite.handler.get_error_appids steam2sqlite.handler.get_appids_from_db time.monotonic steam2sqlite.navigator.get loguru.logger.error httpx.AsyncClient steam2sqlite.utils.grouper steam2sqlite.handler.store_apps_achievements loguru.logger.info os.getenv datetime.datetime.utcnow json.load sqlmodel.Session
[((538, 551), 'dotenv.load_dotenv', 'load_dotenv', ([], {}), '()\n', (549, 551), False, 'from dotenv import load_dotenv\n'), ((567, 591), 'os.getenv', 'os.getenv', (['"""APPIDS_FILE"""'], {}), "('APPIDS_FILE')\n", (576, 591), False, 'import os\n'), ((1478, 1494), 'argparse.ArgumentParser', 'ArgumentParser', ([], {}), '()\n', (1492, 1494), False, 'from argparse import ArgumentParser\n'), ((1717, 1743), 'loguru.logger.info', 'logger.info', (['"""Starting..."""'], {}), "('Starting...')\n", (1728, 1743), False, 'from loguru import logger\n'), ((1762, 1778), 'time.monotonic', 'time.monotonic', ([], {}), '()\n', (1776, 1778), False, 'import time\n'), ((1784, 1800), 'uvloop.install', 'uvloop.install', ([], {}), '()\n', (1798, 1800), False, 'import uvloop\n'), ((1815, 1852), 'sqlmodel.create_engine', 'create_engine', (['SQLITE_URL'], {'echo': '(False)'}), '(SQLITE_URL, echo=False)\n', (1828, 1852), False, 'from sqlmodel import Session, create_engine\n'), ((775, 835), 'loguru.logger.info', 'logger.info', (['f"""Loading appids from local file: {local_file}"""'], {}), "(f'Loading appids from local file: {local_file}')\n", (786, 835), False, 'from loguru import logger\n'), ((956, 1000), 'loguru.logger.info', 'logger.info', (['"""Loading appids from Steam API"""'], {}), "('Loading appids from Steam API')\n", (967, 1000), False, 'from loguru import logger\n'), ((1984, 1999), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (1991, 1999), False, 'from sqlmodel import Session, create_engine\n'), ((2114, 2141), 'steam2sqlite.handler.get_appids_from_db', 'get_appids_from_db', (['session'], {}), '(session)\n', (2132, 2141), False, 'from steam2sqlite.handler import get_appids_from_db, get_apps_achievements, get_apps_data, get_error_appids, store_apps_achievements, store_apps_data\n'), ((2766, 2791), 'steam2sqlite.handler.get_error_appids', 'get_error_appids', (['session'], {}), '(session)\n', (2782, 2791), False, 'from steam2sqlite.handler import get_appids_from_db, get_apps_achievements, get_apps_data, get_error_appids, store_apps_achievements, store_apps_data\n'), ((2955, 3018), 'loguru.logger.info', 'logger.info', (['"""Loading app data from Steam API and saving to db"""'], {}), "('Loading app data from Steam API and saving to db')\n", (2966, 3018), False, 'from loguru import logger\n'), ((3042, 3102), 'steam2sqlite.utils.grouper', 'utils.grouper', (['appids_to_process', 'BATCH_SIZE'], {'fillvalue': 'None'}), '(appids_to_process, BATCH_SIZE, fillvalue=None)\n', (3055, 3102), False, 'from steam2sqlite import APPIDS_URL, BATCH_SIZE, navigator, utils\n'), ((911, 937), 'json.load', 'json.load', (['steam_appids_fp'], {}), '(steam_appids_fp)\n', (920, 937), False, 'import json\n'), ((3129, 3179), 'steam2sqlite.handler.get_apps_data', 'get_apps_data', (['session', 'steam_appids_names', 'appids'], {}), '(session, steam_appids_names, appids)\n', (3142, 3179), False, 'from steam2sqlite.handler import get_appids_from_db, get_apps_achievements, get_apps_data, get_error_appids, store_apps_achievements, store_apps_data\n'), ((3199, 3254), 'steam2sqlite.handler.store_apps_data', 'store_apps_data', (['session', 'steam_appids_names', 'apps_data'], {}), '(session, steam_appids_names, apps_data)\n', (3214, 3254), False, 'from steam2sqlite.handler import get_appids_from_db, get_apps_achievements, get_apps_data, get_error_appids, store_apps_achievements, store_apps_data\n'), ((1037, 1056), 'httpx.AsyncClient', 'httpx.AsyncClient', ([], {}), '()\n', (1054, 1056), False, 'import httpx\n'), ((1186, 1202), 'asyncio.sleep', 'asyncio.sleep', (['(1)'], {}), '(1)\n', (1199, 1202), False, 'import asyncio\n'), ((1256, 1307), 'loguru.logger.error', 'logger.error', (['"""Error getting the appids from Steam"""'], {}), "('Error getting the appids from Steam')\n", (1268, 1307), False, 'from loguru import logger\n'), ((3570, 3626), 'steam2sqlite.handler.store_apps_achievements', 'store_apps_achievements', (['session', 'apps_achievements_data'], {}), '(session, apps_achievements_data)\n', (3593, 3626), False, 'from steam2sqlite.handler import get_appids_from_db, get_apps_achievements, get_apps_data, get_error_appids, store_apps_achievements, store_apps_data\n'), ((3725, 3790), 'loguru.logger.info', 'logger.info', (['f"""Limit ({args.limit} min) reached shutting down..."""'], {}), "(f'Limit ({args.limit} min) reached shutting down...')\n", (3736, 3790), False, 'from loguru import logger\n'), ((1097, 1130), 'steam2sqlite.navigator.get', 'navigator.get', (['client', 'APPIDS_URL'], {}), '(client, APPIDS_URL)\n', (1110, 1130), False, 'from steam2sqlite import APPIDS_URL, BATCH_SIZE, navigator, utils\n'), ((3659, 3675), 'time.monotonic', 'time.monotonic', ([], {}), '()\n', (3673, 3675), False, 'import time\n'), ((2521, 2547), 'datetime.datetime.utcnow', 'datetime.datetime.utcnow', ([], {}), '()\n', (2545, 2547), False, 'import datetime\n')]