"""
Jam Discord-Bot – führt registrierte Python-Skripte per Befehl aus.
"""

from __future__ import annotations

import asyncio
from pathlib import Path

from jam.discord_script_runner import find_script, list_scripts, run_script
from jam.discord_setup import load_config


def _is_admin(user_id: int, cfg: dict) -> bool:
    admins = cfg.get("admin_user_ids") or []
    return str(user_id) in [str(a) for a in admins] or not admins


async def run_bot() -> None:
    try:
        import discord
        from discord.ext import commands
    except ImportError as e:
        raise SystemExit(
            "discord.py fehlt. Installiere: pip install discord.py\n" + str(e)
        ) from e

    cfg = load_config()
    token = (cfg.get("token") or "").strip()
    if not token:
        raise SystemExit(
            "Kein Bot-Token. Trage token in data/discord_config.json ein "
            "(Discord Developer Portal → Bot → Token)."
        )

    prefix = cfg.get("command_prefix") or "!"
    scripts_dir = Path(cfg.get("scripts_dir") or Path.home() / "Documents/Jam-Discord/scripts")
    registry = Path(cfg.get("scripts_registry") or Path(__file__).resolve().parent.parent / "data/discord_scripts.json")

    intents = discord.Intents.default()
    intents.message_content = True
    intents.members = True

    bot = commands.Bot(command_prefix=prefix, intents=intents)

    async def _exec_script(ctx: commands.Context, script_id: str, args: list[str]) -> None:
        entry = find_script(script_id)
        if not entry:
            await ctx.send(f"Unbekannt: `{script_id}` – nutze `{prefix}scripts`")
            return
        if entry.get("admin_only") and not _is_admin(ctx.author.id, cfg):
            await ctx.send("Nur für Admins.")
            return
        await ctx.trigger_typing()
        ok, msg = await asyncio.to_thread(
            run_script,
            script_id,
            scripts_dir=scripts_dir,
            registry_path=registry,
            args=args,
        )
        title = entry.get("name") or script_id
        if ok:
            await ctx.send(f"**{title}**\n```\n{msg[:1900]}\n```")
        else:
            await ctx.send(f"**{title}** Fehler:\n```\n{msg[:1900]}\n```")

    @bot.event
    async def on_ready() -> None:
        print(f"Jam-Discord online: {bot.user} ({len(list_scripts())} Skripte)")

    @bot.command(name="jam")
    async def cmd_jam(ctx: commands.Context) -> None:
        await ctx.send(
            f"**Jam-Bot** – eigene Skripte per `{prefix}<befehl>` oder `{prefix}run <id>`\n"
            f"`{prefix}scripts` · `{prefix}ping` · `{prefix}shop` · `{prefix}info`"
        )

    @bot.command(name="scripts")
    async def cmd_scripts(ctx: commands.Context) -> None:
        lines = []
        for s in list_scripts():
            cmd = s.get("discord_command") or s.get("id")
            adm = " 🔒" if s.get("admin_only") else ""
            lines.append(f"`{prefix}{cmd}` – {s.get('name', cmd)}{adm}: {s.get('description', '')[:60]}")
        await ctx.send("**Skripte:**\n" + ("\n".join(lines) or "(leer)"))

    @bot.command(name="run")
    async def cmd_run(ctx: commands.Context, script_id: str, *args: str) -> None:
        await _exec_script(ctx, script_id, list(args))

    for s in list_scripts():
        cmd_name = (s.get("discord_command") or s.get("id") or "").strip().lower()
        script_id = (s.get("id") or cmd_name).strip().lower()
        if not cmd_name or cmd_name in ("run", "scripts", "jam"):
            continue

        def _make(sid: str, name: str):
            @bot.command(name=name)
            async def dynamic_cmd(ctx: commands.Context, *args: str, _sid: str = sid) -> None:
                await _exec_script(ctx, _sid, list(args))

            return dynamic_cmd

        _make(script_id, cmd_name)

    await bot.start(token)


def main() -> None:
    asyncio.run(run_bot())


if __name__ == "__main__":
    main()
