"""Fliiq daemon — lightweight FastAPI server for scheduler + job management."""

import asyncio
import os
import time
from contextlib import asynccontextmanager
from pathlib import Path

import structlog
from fastapi import FastAPI, HTTPException

from fliiq.runtime.agent.setup import resolve_llm_config
from fliiq.runtime.scheduler.executor import execute_job
from fliiq.runtime.scheduler.loader import load_jobs
from fliiq.runtime.scheduler.scheduler import Scheduler

log = structlog.get_logger()


@asynccontextmanager
async def _lifespan(app: FastAPI):
    """Start scheduler on boot, stop on shutdown."""
    # 1. Resolve project root + load env
    from dotenv import load_dotenv

    from fliiq.runtime.config import resolve_env_file, resolve_fliiq_dir

    env_path = resolve_env_file()
    if env_path:
        load_dotenv(env_path)

    if os.environ.get("FLIIQ_PROJECT_ROOT"):
        project_root = Path(os.environ["FLIIQ_PROJECT_ROOT"])
    else:
        project_root = resolve_fliiq_dir(require_local=True).parent
    jobs_dir = project_root / ".fliiq" / "jobs"
    jobs_dir.mkdir(parents=True, exist_ok=True)

    # 2. Resolve LLM config
    llm_config = resolve_llm_config()

    # 3. Load jobs + start scheduler
    jobs = load_jobs(jobs_dir)

    async def _on_fire(job):
        entry = await execute_job(job, jobs_dir, project_root, llm_config)
        if entry.status == "error":
            raise RuntimeError(f"Job failed: {entry.error}")

    scheduler = Scheduler(on_fire=_on_fire)
    await scheduler.start(jobs)

    # 4. Store state
    app.state.scheduler = scheduler
    app.state.llm_config = llm_config
    app.state.jobs_dir = jobs_dir
    app.state.project_root = project_root
    app.state.started_at = time.monotonic()

    # 5. Write PID file
    pid_path = project_root / ".fliiq" / "daemon.pid"
    pid_path.write_text(str(os.getpid()))

    # 6. Start Telegram listener (if configured)
    telegram_listener = None
    telegram_task = None
    telegram_bot_token = os.environ.get("TELEGRAM_BOT_TOKEN")
    if telegram_bot_token:
        from fliiq.runtime.telegram.listener import TelegramListener, parse_allowed_chat_ids

        allowed_ids = parse_allowed_chat_ids()
        telegram_listener = TelegramListener(
            telegram_bot_token, llm_config, project_root, allowed_ids,
        )
        telegram_task = asyncio.create_task(telegram_listener.run())
        log.info("telegram_listener_started")

    app.state.telegram_active = telegram_listener is not None

    log.info("daemon_started", jobs=scheduler.scheduled_count, telegram=app.state.telegram_active, pid=os.getpid())

    try:
        yield
    finally:
        if telegram_listener:
            await telegram_listener.stop()
            telegram_task.cancel()
        await scheduler.stop()
        if pid_path.exists():
            pid_path.unlink()
        log.info("daemon_stopped")


app = FastAPI(title="Fliiq Daemon", lifespan=_lifespan)

from fliiq.api.webhooks.router import router as webhook_router  # noqa: E402

app.include_router(webhook_router)


@app.get("/health")
async def health():
    uptime = int(time.monotonic() - app.state.started_at)
    return {
        "status": "ok",
        "jobs": app.state.scheduler.scheduled_count,
        "telegram": app.state.telegram_active,
        "uptime_s": uptime,
    }


@app.get("/api/jobs")
async def list_jobs():
    jobs = load_jobs(app.state.jobs_dir)
    return [j.model_dump(mode="json") for j in jobs]


@app.post("/api/jobs/{name}/run")
async def trigger_job(name: str):
    jobs = load_jobs(app.state.jobs_dir)
    job = next((j for j in jobs if j.name == name), None)
    if not job:
        raise HTTPException(status_code=404, detail=f"Job '{name}' not found")
    entry = await execute_job(
        job, app.state.jobs_dir, app.state.project_root, app.state.llm_config
    )
    return entry.model_dump(mode="json")
