# runner.py
import asyncio
import sys
import scrapy_cffi
from settings import create_settings
from typing import Tuple
if sys.platform.startswith("win"):
    # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
from scrapy_cffi.utils import setup_uvloop_once, get_or_create_loop
setup_uvloop_once()

# Ordinary users
def main(*args, **kwargs):
    settings = create_settings(spider_path="spiders.CustomSpider")

    # compatible scrapy settings.py
    # from scrapy_cffi import load_settings_with_path
    # settings = load_settings_with_path()

    scrapy_cffi.run_spider_sync(settings=settings, *args, **kwargs)

def main_all(*args, **kwargs):
    from scrapy_cffi.utils import get_run_py_dir
    spider_path = str(get_run_py_dir() / "spiders") # must be a directory when mode is 'run_all_spiders', since all spider files will be loaded from it
    settings = create_settings(spider_path=spider_path)

    # compatible scrapy settings.py
    # from scrapy_cffi import load_settings_with_path
    # settings = load_settings_with_path()
    
    scrapy_cffi.run_all_spiders_sync(settings=settings, *args, **kwargs)

# Advanced Users
async def advance_main(*args, **kwargs) -> Tuple[scrapy_cffi.crawler.Crawler, asyncio.Task]:
    settings = create_settings(spider_path="spiders.CustomSpider")

    # compatible scrapy settings.py
    # from scrapy_cffi import load_settings_with_path
    # settings = load_settings_with_path()

    crawler, engine_task = await scrapy_cffi.run_spider(settings=settings, new_loop=False, *args, **kwargs)
    return crawler, engine_task

async def advance_main_all(*args, **kwargs) -> Tuple[scrapy_cffi.crawler.Crawler, asyncio.Task]:
    from scrapy_cffi.utils import get_run_py_dir
    spider_path = str(get_run_py_dir() / "spiders") # must be a directory when mode is 'run_all_spiders', since all spider files will be loaded from it
    settings = create_settings(spider_path=spider_path)

    # compatible scrapy settings.py
    # from scrapy_cffi import load_settings_with_path
    # settings = load_settings_with_path()

    crawler, engine_task = await scrapy_cffi.run_all_spiders(settings=settings, new_loop=False, *args, **kwargs)
    return crawler, engine_task

# ————————————————————————————————————————————————————————————————————————
def setup_signal_handlers(loop: asyncio.AbstractEventLoop, shutdown_event: asyncio.Event):
    if sys.platform == "win32":
        print(">>> [info] Signal handlers not supported on Windows (fallback to KeyboardInterrupt)")
        return

    import signal
    def _handle_signal():
        print(">>> [signal] Received stop signal")
        shutdown_event.set()

    for sig in (signal.SIGINT, signal.SIGTERM):
        try:
            loop.add_signal_handler(sig, _handle_signal)
        except (NotImplementedError, ValueError):
            pass

if __name__ == "__main__":
    loop = get_or_create_loop()
    shutdown_event = asyncio.Event()
    setup_signal_handlers(loop, shutdown_event)

    crawler: scrapy_cffi.crawler.Crawler = None

    async def demo_main():
        global crawler
        # crawler, engine_task = await advance_main()
        crawler, engine_task = await advance_main_all()

        done, _ = await asyncio.wait(
            [engine_task, asyncio.create_task(shutdown_event.wait())],
            return_when=asyncio.FIRST_COMPLETED
        )

        if shutdown_event.is_set():
            print(">>> [main] Triggered shutdown, cleaning up...")
        else:
            print(">>> [main] Task finished normally.")

        await crawler.shutdown()

    try:
        loop.run_until_complete(demo_main())
    except KeyboardInterrupt:
        print(">>> [KeyboardInterrupt] manual stop")
        if crawler:
            loop.run_until_complete(crawler.shutdown())
    finally:
        scrapy_cffi.cleanup_loop(loop=loop)


    # ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
    # Ordinary user (internal automatic new loop)
    import threading
    t = threading.Thread(target=main)
    t.start()
    t.join()
    t = threading.Thread(target=main_all)
    t.start()
    t.join()