"""Nexus — Heartbeat Flush Background Task
Every 10 minutes: batch-flush Redis heartbeat data to MySQL servers table.
MySQL only stores historical snapshots; frontend reads live data from Redis.
"""

import asyncio
import logging
from datetime import datetime, timezone

from sqlalchemy import update

from server.infrastructure.database.session import AsyncSessionLocal
from server.infrastructure.redis.client import get_redis
from server.domain.models import Server

logger = logging.getLogger("nexus.heartbeat_flush")

FLUSH_INTERVAL = 600  # 10 minutes (seconds)
REDIS_KEY_PREFIX = "heartbeat:"
REDIS_KEY_EXPIRE = 600  # 10 minutes TTL for Redis heartbeat keys


async def heartbeat_flush_loop():
    """Background task: every 10min, flush Redis heartbeat data to MySQL.

    Flow:
    1. Scan all heartbeat:* keys in Redis
    2. For each key, extract server_id + heartbeat data
    3. Batch UPDATE MySQL servers table (is_online, system_info, last_heartbeat, agent_version)
    4. Log count of flushed servers
    """
    logger.info("Heartbeat flush loop started (interval: 10min)")
    while True:
        await asyncio.sleep(FLUSH_INTERVAL)

        redis = get_redis()

        # Scan all heartbeat:* keys
        try:
            keys = []
            cursor = 0
            while True:
                cursor, batch = await redis.scan(cursor, match=f"{REDIS_KEY_PREFIX}*", count=100)
                keys.extend(batch)
                if cursor == 0:
                    break
        except Exception as e:
            logger.error(f"Redis scan failed: {e}")
            continue

        if not keys:
            logger.info("No heartbeat keys in Redis — no agents reporting yet")
            continue

        flushed = 0
        skipped = 0
        for key in keys:
            try:
                async with AsyncSessionLocal() as session:
                    server_id = int(key.replace(REDIS_KEY_PREFIX, ""))
                    data = await redis.hgetall(key)

                    if not data:
                        continue

                    is_online = data.get("is_online") == "True"
                    system_info = data.get("system_info", "{}")
                    agent_version = data.get("agent_version", "")

                    # Parse last_heartbeat — ensure timezone-aware
                    last_hb_str = data.get("last_heartbeat", "")
                    try:
                        last_heartbeat = datetime.fromisoformat(last_hb_str)
                        if last_heartbeat.tzinfo is None:
                            last_heartbeat = last_heartbeat.replace(tzinfo=timezone.utc)
                    except (ValueError, TypeError):
                        last_heartbeat = datetime.now(timezone.utc)

                    await session.execute(
                        update(Server)
                        .where(Server.id == server_id)
                        .values(
                            is_online=is_online,
                            system_info=system_info,
                            last_heartbeat=last_heartbeat,
                            agent_version=agent_version,
                        )
                    )
                    await session.commit()
                    flushed += 1
            except Exception as e:
                logger.warning(f"Failed to flush server {key}: {e}")
                skipped += 1

        logger.info(f"Heartbeat flush: {flushed}/{len(keys)} servers updated to MySQL" +
                    (f", {skipped} failed" if skipped else ""))