import asyncio
import logging
import os
import time
from datetime import datetime

from sqlmodel import Session, SQLModel, select
from starlette.concurrency import run_in_threadpool

from ..datatypes import ArtmuseumTimeLabel
from ..scraping.artmuseum import scrap_artmuseum
from ..scraping.philharmonia import scrap_philharmonia
from .models import ArtmuseumExhibition, PhilharmoniaConcert


def refresh_data(engine):
    """
    Scrap all the data sources for up-to-date info. Drop local values and replace them with the new data.

    We are trying to be an exact mirror of our data sources.
    The easiest way to achieve this is to regularly throw out all the data we have and scrap up-to-date info.
    The cost of this approach in performance/resources is neglectable and is much preffered over complications
    brought by trying to maintain a local copy by continuously patching it up with UPDATEs.
      (there can be edits in the source info, urls can change, etc. - it's not worth it to consider all such corner cases)
    """
    logging.info("Started scraping up-to-date info.")
    known_addrs = {}
    with Session(engine) as session:
        stmt = select(ArtmuseumExhibition.url, ArtmuseumExhibition.address).where(
            ArtmuseumExhibition.address != None
        )
        known_addrs = dict(session.exec(stmt).all())
    exhibitions = scrap_artmuseum(known_addrs)
    concerts = scrap_philharmonia()
    logging.info("Finished scraping up-to-date info.")

    logging.info("Started updating the database.")
    with Session(engine) as session:
        session.query(PhilharmoniaConcert).delete()
        session.query(ArtmuseumExhibition).delete()

        session.bulk_save_objects(concerts)
        session.bulk_save_objects(exhibitions)

        session.commit()
    logging.info("Finished updating the database.")


async def loop_refreshing_data(engine, update_interval, initial_sleep_time: int = 0):
    if initial_sleep_time > 0:
        await asyncio.sleep(initial_sleep_time)
    while True:
        await run_in_threadpool(refresh_data, engine)
        await asyncio.sleep(update_interval)


def init_db(engine):
    update_interval = 60 * 60 * 8  # 8 hours
    initial_sleep_time = 0

    if os.path.isfile(engine.url.database):
        last_modified = os.path.getmtime(engine.url.database)
        dt = time.time() - last_modified

        if dt <= update_interval:
            initial_sleep_time = update_interval - dt

            last_update = datetime.fromtimestamp(last_modified).replace(microsecond=0)
            logging.info(
                f"Last database update - {last_update}, the next one is scheduled in ...[N]h [N]m.... (at h:m)"
            )

    SQLModel.metadata.create_all(engine)

    asyncio.create_task(
        loop_refreshing_data(engine, update_interval, initial_sleep_time)
    )

sqlmodel.SQLModel.metadata.create_all os.path.getmtime os.path.isfile sqlmodel.Session datetime.datetime.fromtimestamp time.time sqlmodel.select logging.info starlette.concurrency.run_in_threadpool asyncio.sleep
[((1043, 1092), 'logging.info', 'logging.info', (['"""Started scraping up-to-date info."""'], {}), "('Started scraping up-to-date info.')\n", (1055, 1092), False, 'import logging\n'), ((1432, 1482), 'logging.info', 'logging.info', (['"""Finished scraping up-to-date info."""'], {}), "('Finished scraping up-to-date info.')\n", (1444, 1482), False, 'import logging\n'), ((1488, 1534), 'logging.info', 'logging.info', (['"""Started updating the database."""'], {}), "('Started updating the database.')\n", (1500, 1534), False, 'import logging\n'), ((1798, 1845), 'logging.info', 'logging.info', (['"""Finished updating the database."""'], {}), "('Finished updating the database.')\n", (1810, 1845), False, 'import logging\n'), ((2231, 2266), 'os.path.isfile', 'os.path.isfile', (['engine.url.database'], {}), '(engine.url.database)\n', (2245, 2266), False, 'import os\n'), ((2705, 2741), 'sqlmodel.SQLModel.metadata.create_all', 'SQLModel.metadata.create_all', (['engine'], {}), '(engine)\n', (2733, 2741), False, 'from sqlmodel import Session, SQLModel, select\n'), ((1123, 1138), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (1130, 1138), False, 'from sqlmodel import Session, SQLModel, select\n'), ((1544, 1559), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (1551, 1559), False, 'from sqlmodel import Session, SQLModel, select\n'), ((2292, 2329), 'os.path.getmtime', 'os.path.getmtime', (['engine.url.database'], {}), '(engine.url.database)\n', (2308, 2329), False, 'import os\n'), ((1979, 2012), 'asyncio.sleep', 'asyncio.sleep', (['initial_sleep_time'], {}), '(initial_sleep_time)\n', (1992, 2012), False, 'import asyncio\n'), ((2043, 2082), 'starlette.concurrency.run_in_threadpool', 'run_in_threadpool', (['refresh_data', 'engine'], {}), '(refresh_data, engine)\n', (2060, 2082), False, 'from starlette.concurrency import run_in_threadpool\n'), ((2097, 2127), 'asyncio.sleep', 'asyncio.sleep', (['update_interval'], {}), '(update_interval)\n', (2110, 2127), False, 'import asyncio\n'), ((2343, 2354), 'time.time', 'time.time', ([], {}), '()\n', (2352, 2354), False, 'import time\n'), ((2560, 2679), 'logging.info', 'logging.info', (['f"""Last database update - {last_update}, the next one is scheduled in ...[N]h [N]m.... (at h:m)"""'], {}), "(\n    f'Last database update - {last_update}, the next one is scheduled in ...[N]h [N]m.... (at h:m)'\n    )\n", (2572, 2679), False, 'import logging\n'), ((1166, 1226), 'sqlmodel.select', 'select', (['ArtmuseumExhibition.url', 'ArtmuseumExhibition.address'], {}), '(ArtmuseumExhibition.url, ArtmuseumExhibition.address)\n', (1172, 1226), False, 'from sqlmodel import Session, SQLModel, select\n'), ((2487, 2524), 'datetime.datetime.fromtimestamp', 'datetime.fromtimestamp', (['last_modified'], {}), '(last_modified)\n', (2509, 2524), False, 'from datetime import datetime\n')]