"""D&D table-tools bot for Firebird.

This one is intentionally small and friendly. It keeps initiative in memory,
rolls common dice expressions, and works with message or slash commands.

Try in chat:
  !roll 2d20+5
  !init add Mira 17
  !init next
  /roll 1d8+3
"""

import random
import re

from firebird_bot import BotClient


bot = BotClient()
ROLL_RE = re.compile(r"^(?P<count>\d*)d(?P<sides>\d+)(?P<mod>[+-]\d+)?$", re.IGNORECASE)
initiative = []
turn_index = 0


def roll_expression(expr):
    match = ROLL_RE.match(expr.strip())
    if not match:
        raise ValueError("Use dice like 1d20, 2d6+3, or d8.")
    count = int(match.group("count") or 1)
    sides = int(match.group("sides"))
    modifier = int(match.group("mod") or 0)
    if count < 1 or count > 50 or sides < 2 or sides > 1000:
        raise ValueError("Keep rolls between 1-50 dice and 2-1000 sides.")
    rolls = [random.randint(1, sides) for _ in range(count)]
    return rolls, modifier, sum(rolls) + modifier


def initiative_text():
    if not initiative:
        return "Initiative is empty. Add someone with !init add <name> <score>."
    lines = []
    for index, entry in enumerate(initiative):
        marker = ">" if index == turn_index % len(initiative) else " "
        lines.append(f"{marker} {entry['name']} ({entry['score']})")
    return "```text\n" + "\n".join(lines) + "\n```"


def handle_roll(ctx):
    expr = ctx.args.strip() or "1d20"
    try:
        rolls, modifier, total = roll_expression(expr)
    except ValueError as exc:
        ctx.reply(str(exc))
        return
    mod_text = f" {modifier:+d}" if modifier else ""
    ctx.reply(f"{expr}: {rolls}{mod_text} = **{total}**")


@bot.command("roll")
def roll_message(ctx):
    handle_roll(ctx)


@bot.slash_command(
    "roll",
    description="Roll dice like 2d20+5.",
    options=[{"name": "dice", "type": "string", "description": "Dice expression", "required": False}],
)
def roll_slash(ctx):
    handle_roll(ctx)


@bot.command("init")
def init_command(ctx):
    global turn_index
    parts = ctx.args.split()
    if not parts:
        ctx.reply(initiative_text())
        return
    action = parts[0].lower()
    if action == "add" and len(parts) >= 3:
        name = " ".join(parts[1:-1])
        score = int(parts[-1])
        initiative.append({"name": name, "score": score})
        initiative.sort(key=lambda row: row["score"], reverse=True)
        turn_index = 0
        ctx.reply(initiative_text())
    elif action == "next":
        if initiative:
            turn_index = (turn_index + 1) % len(initiative)
        ctx.reply(initiative_text())
    elif action == "clear":
        initiative.clear()
        turn_index = 0
        ctx.reply("Initiative cleared.")
    else:
        ctx.reply("Use !init add <name> <score>, !init next, !init clear, or !init.")


if __name__ == "__main__":
    bot.run_forever()
