"""Entrypoint: runs scheduler + web server + DAG file watcher in one process."""
from __future__ import annotations

import asyncio
import logging
import signal

import uvicorn

from crondag.config import Settings
from crondag.models.base import init_db, get_engine, get_session_factory
from crondag.scheduler.loop import Scheduler

logger = logging.getLogger(__name__)


async def run_server(settings: Settings):
    """Run all components in a single asyncio process."""
    logger.info("Starting crondag...")
    logger.info("  DAGs dir: %s", settings.dags_dir)
    logger.info("  Database: %s", settings.database_url)
    logger.info("  Web UI:   http://%s:%d", settings.webserver_host, settings.webserver_port)

    # Initialize database
    await init_db(settings.database_url)

    # Create scheduler
    scheduler = Scheduler(settings)

    # Create FastAPI app
    from crondag.api.app import create_app
    app = create_app()
    app.state.scheduler = scheduler
    app.state.settings = settings

    # Setup signal handling
    stop_event = asyncio.Event()

    def _signal_handler():
        logger.info("Shutdown signal received")
        stop_event.set()

    loop = asyncio.get_running_loop()
    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, _signal_handler)

    async def _run_scheduler():
        """Run the scheduler loop."""
        try:
            await scheduler.start()
        except asyncio.CancelledError:
            await scheduler.stop()

    async def _run_webserver():
        """Run the FastAPI server."""
        if not settings.webserver_enabled:
            logger.info("Web server disabled")
            return

        config = uvicorn.Config(
            app=app,
            host=settings.webserver_host,
            port=settings.webserver_port,
            log_level=settings.log_level.lower(),
            access_log=False,
        )
        server = uvicorn.Server(config)
        try:
            await server.serve()
        except asyncio.CancelledError:
            server.should_exit = True

    async def _run_dag_watcher():
        """Periodically rescan DAG files for changes."""
        interval = settings.scheduler_dag_scan_interval
        try:
            while not stop_event.is_set():
                try:
                    await asyncio.wait_for(stop_event.wait(), timeout=interval)
                    break
                except asyncio.TimeoutError:
                    pass
                logger.debug("Rescanning DAG files...")
                await scheduler.rescan_dags()
        except asyncio.CancelledError:
            pass

    async def _wait_for_stop():
        """Wait for the stop signal, then cancel everything."""
        await stop_event.wait()
        logger.info("Shutting down...")
        await scheduler.stop()

    logger.info("crondag is running. Press Ctrl+C to stop.")

    # Run all components concurrently
    tasks = [
        asyncio.create_task(_run_scheduler(), name="scheduler"),
        asyncio.create_task(_run_dag_watcher(), name="dag-watcher"),
        asyncio.create_task(_wait_for_stop(), name="stop-waiter"),
    ]

    if settings.webserver_enabled:
        tasks.append(asyncio.create_task(_run_webserver(), name="webserver"))

    try:
        # Wait for any task to complete (usually the stop waiter)
        done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)

        # Cancel remaining tasks
        for task in pending:
            task.cancel()

        # Wait for cancellation to complete
        if pending:
            await asyncio.wait(pending, timeout=5.0)

    except asyncio.CancelledError:
        for task in tasks:
            task.cancel()

    logger.info("crondag stopped.")
