"""Health and metrics — bot uptime, memory, handler counts, version.

Provides `/ping`, `/status`, `/stats` and `/version` commands. Also writes
metrics to a JSON file in `data/metrics.json` for external scraping.
"""
from __future__ import annotations

import json
import logging
import os
import platform
import time
from time import perf_counter
from datetime import datetime
from pathlib import Path

from telegram import Update
from telegram.constants import ParseMode
from telegram.ext import CommandHandler, ContextTypes

from bot import __version__, persian_router
from bot.config import BOT_TOKEN, DB_PATH
from bot.core.memory import memory_snapshot
from bot.core.metrics import snapshot
from bot.core.supervisor import inspect_asyncio_tasks
from bot.decorators import sudo_only
from bot.infra.db.manager import db_manager
from bot.persian_router import register_mixed
from bot.services.runtime import recent_pipeline_stats

logger = logging.getLogger(__name__)


_BOOT_TIME = time.time()


def _persian_command_count() -> int:
    return len(getattr(persian_router, "_persian_handlers", {}))


async def _measure_db_latency() -> float:
    started = perf_counter()

    await db_manager.fetchone("SELECT 1")

    return (perf_counter() - started) * 1000


async def ping_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Measure round-trip latency."""
    t0 = time.time()
    msg = await update.message.reply_text("🏓 ...")
    dt = (time.time() - t0) * 1000
    await msg.edit_text(
        f"🏓 <b>پونگ!</b>\n"
        f"پینگ: <code>{dt:.0f} ms</code>\n"
        f"آپتایم: <code>{_format_uptime()}</code>",
        parse_mode=ParseMode.HTML,
    )


@sudo_only
async def status_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Show bot status: uptime, memory, DB size, handler count."""
    import sys
    import shutil
    db_path = Path(DB_PATH)
    db_size = db_path.stat().st_size / 1024 if db_path.exists() else 0
    free = shutil.disk_usage(".").free / 1024 / 1024

    n_handlers = 0
    for group in context.application.handlers.values():
        n_handlers += len(group)

    text = (
        f"📊 <b>وضعیت ربات</b>\n"
        f"━━━━━━━━━━━━━━━\n"
        f"🤖 نام: <code>{context.bot.username}</code>\n"
        f"🆔 شناسه: <code>{context.bot.id}</code>\n"
        f"📦 نسخه: <b>bobi v{__version__}</b>\n"
        f"🐍 پایتون: <code>{platform.python_version()}</code>\n"
        f"💻 سیستم: <code>{platform.system()} {platform.machine()}</code>\n"
        f"━━━━━━━━━━━━━━━\n"
        f"⏱ آپتایم: <code>{_format_uptime()}</code>\n"
        f"🧩 هندلرها: <code>{n_handlers}</code>\n"
        f"🗂 حجم دیتابیس: <code>{db_size:.1f} KB</code>\n"
        f"💾 فضای خالی: <code>{free:.0f} MB</code>\n"
        f"📝 دستورات فارسی: <code>{_persian_command_count()}</code>\n"
    )
    await update.message.reply_text(text, parse_mode=ParseMode.HTML)


@sudo_only
async def runtime_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    db_latency = await _measure_db_latency()
    metrics = snapshot()
    pipeline = recent_pipeline_stats()
    memory = memory_snapshot()
    tasks = inspect_asyncio_tasks()

    lines = [
        "🩺 <b>Runtime Diagnostics</b>",
        "━━━━━━━━━━━━━━━",
        f"⏱ Uptime: <code>{_format_uptime()}</code>",
        f"🗄 DB latency: <code>{db_latency:.2f} ms</code>",
        f"🧩 Handlers: <code>{sum(len(g) for g in context.application.handlers.values())}</code>",
        f"📊 Runtime metrics: <code>{len(metrics)}</code>",
        f"⚡ Avg pipeline: <code>{pipeline['avg_ms']} ms</code>",
        f"🚨 Max pipeline: <code>{pipeline['max_ms']} ms</code>",
        f"🧠 Approx memory: <code>{memory['approx_memory_mb']} MB</code>",
        f"🧵 Async tasks: <code>{tasks['pending']}/{tasks['total']}</code>",
    ]

    if tasks["sample"]:
        lines.append("")
        lines.append("🧵 <b>Task Sample</b>")

        for task_name in tasks["sample"][:5]:
            lines.append(f"• <code>{task_name}</code>")

    if metrics:
        lines.append("")
        lines.append("📈 <b>Top Metrics</b>")

        for key, value in sorted(metrics.items(), key=lambda item: item[1], reverse=True)[:10]:
            lines.append(f"• <code>{key}</code>: <b>{value}</b>")

    await update.message.reply_text("\n".join(lines), parse_mode=ParseMode.HTML)


async def version_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text(
        f"🤖 <b>bobi v{__version__}</b>\n"
        f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
        parse_mode=ParseMode.HTML,
    )


def _format_uptime() -> str:
    s = int(time.time() - _BOOT_TIME)
    d, s = divmod(s, 86400)
    h, s = divmod(s, 3600)
    m, s = divmod(s, 60)
    if d:
        return f"{d}d {h}h {m}m"
    if h:
        return f"{h}h {m}m"
    return f"{m}m {s}s"


# ───────── Metrics export (for Prometheus or external scrape) ─────────
def collect_metrics(app) -> dict:
    """Collect runtime metrics into a dict (and persist to data/metrics.json)."""
    metrics = {
        "version": __version__,
        "uptime_seconds": int(time.time() - _BOOT_TIME),
        "handler_count": sum(len(g) for g in app.handlers.values()),
        "persian_commands": _persian_command_count(),
        "timestamp": int(time.time()),
        "runtime_metrics": snapshot(),
    }
    try:
        db_path = Path(DB_PATH)
        if db_path.exists():
            metrics["db_size_bytes"] = db_path.stat().st_size
    except Exception:
        pass
    try:
        Path("data").mkdir(exist_ok=True)
        Path(DB_PATH).parent.mkdir(exist_ok=True)
        (Path(DB_PATH).parent / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
    except Exception as e:
        logger.debug("metrics write failed: %s", e)
    return metrics


# ───────── Registration ─────────
def register(app) -> None:
    register_mixed(
        app,
        {
            "ping": ping_cmd,
            "status": status_cmd,
            "runtime": runtime_cmd,
            "version": version_cmd,
        },
        {
            "پینگ": ping_cmd,
            "چه خبر": ping_cmd,
            "پینگ تست": ping_cmd,
            "وضعیت ربات": status_cmd,
            "وضعیت": status_cmd,
            "سلامتی": status_cmd,
            "سلامت ران تایم": runtime_cmd,
            "ران تایم": runtime_cmd,
            "نسخه": version_cmd,
            "ورژن": version_cmd,
            "ورژن ربات": version_cmd,
        },
    )

    app.add_handler(CommandHandler("healthz", runtime_cmd))

    async def _periodic_metrics(ctx):
        try:
            collect_metrics(ctx.application)
        except Exception as e:
            logger.debug("periodic metrics: %s", e)

    try:
        app.job_queue.run_repeating(_periodic_metrics, interval=300, first=60, name="bobi:metrics")
    except Exception as e:
        logger.warning("Metrics scheduler registration failed: %s", e)
