# modules/admin/shaiadmin.py
import io
import json
from typing import Any, Dict, List

import discord
from discord import app_commands
from discord.ext import commands

from modules.common.settings import (
    cfg, SETTINGS_SCHEMA, settings_path, settings_get_all,
    settings_set, settings_reset, settings_import_bulk, ValidationError,
)
from mod_perms import is_moderator_member  # keep if you want mods as managers

def _user_has_role_ids(member: discord.Member, role_ids: List[int]) -> bool:
    if not isinstance(member, discord.Member) or not role_ids:
        return False
    rset = set(role_ids)
    return any(r.id in rset for r in member.roles)

async def _is_owner(bot: commands.Bot, user: discord.abc.User) -> bool:
    try:
        return await bot.is_owner(user)
    except Exception:
        return False

def _get_admin_lists(bot: commands.Bot) -> Dict[str, List[int]]:
    r = cfg(bot)
    users, roles = [], []
    try:
        users = json.loads(r.get("admin_user_ids", "[]"))
    except Exception:
        users = []
    try:
        roles = json.loads(r.get("admin_role_ids", "[]"))
    except Exception:
        roles = []
    return {"users": users, "roles": roles}

async def _check_admin(inter: discord.Interaction) -> bool:
    bot: commands.Bot = inter.client  # type: ignore
    user = inter.user
    if await _is_owner(bot, user):
        return True
    if isinstance(user, discord.Member):
        lists = _get_admin_lists(bot)
        if user.id in set(lists["users"]):
            return True
        if _user_has_role_ids(user, lists["roles"]):
            return True
        if is_moderator_member(user, bot):  # optional; remove if not desired
            return True
    if not inter.response.is_done():
        await inter.response.send_message("You don’t have permission to use `/shaiadmin`.", ephemeral=True)
    else:
        await inter.followup.send("You don’t have permission to use `/shaiadmin`.", ephemeral=True)
    return False


class ShaiAdminCog(commands.Cog):
    """Runtime settings administration (file-backed)."""

    def __init__(self, bot: commands.Bot):
        self.bot = bot

    # Guild-only group; prefix description with [ADMIN]
    shaiadmin = app_commands.Group(
        name="shaiadmin",
        description="[ADMIN] Owner/approved-only settings manager.",
        guild_only=True,
    )

    # ---- bound coroutine for autocomplete ----
    async def ac_setting_keys(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]:
        cur = (current or "").lower()
        keys = [k for k in sorted(SETTINGS_SCHEMA.keys()) if cur in k]
        return [app_commands.Choice(name=k, value=k) for k in keys[:25]]

    # /shaiadmin set
    @shaiadmin.command(name="set", description="[ADMIN] Set a setting (validated, persisted, applied).")
    @app_commands.describe(setting_name="Which setting to change", value="New value (type depends on setting)")
    @app_commands.autocomplete(setting_name=ac_setting_keys)
    async def set_value(self, inter: discord.Interaction, setting_name: str, value: str):
        if not await _check_admin(inter):
            return
        await inter.response.defer(ephemeral=True, thinking=True)
        setting_name = setting_name.lower().strip()
        try:
            changed = settings_set(setting_name, value)
            await inter.followup.send(
                f"✅ `{setting_name}` updated and applied." if changed else "ℹ️ No change.",
                ephemeral=True,
            )
        except ValidationError as ve:
            await inter.followup.send(f"❌ {ve}", ephemeral=True)
        except Exception as e:
            await inter.followup.send(f"❌ Failed to set `{setting_name}`: {e!r}", ephemeral=True)

    # /shaiadmin unset
    @shaiadmin.command(name="unset", description="[ADMIN] Reset/unset a setting to its default.")
    @app_commands.describe(setting_name="Which setting to reset")
    @app_commands.autocomplete(setting_name=ac_setting_keys)
    async def unset_value(self, inter: discord.Interaction, setting_name: str):
        if not await _check_admin(inter):
            return
        await inter.response.defer(ephemeral=True, thinking=True)
        setting_name = setting_name.lower().strip()
        try:
            settings_reset(setting_name)
            await inter.followup.send(f"✅ `{setting_name}` reset to default and applied.", ephemeral=True)
        except ValidationError as ve:
            await inter.followup.send(f"❌ {ve}", ephemeral=True)
        except Exception as e:
            await inter.followup.send(f"❌ Failed to reset `{setting_name}`: {e!r}", ephemeral=True)

    # /shaiadmin settings (download/upload)
    settings = app_commands.Group(
        name="settings",
        description="[ADMIN] Download or upload the full settings JSON.",
        parent=shaiadmin,
        guild_only=True,
    )

    @settings.command(name="download", description="[ADMIN] Download the current settings.json")
    async def download(self, inter: discord.Interaction):
        if not await _check_admin(inter):
            return
        await inter.response.defer(ephemeral=True, thinking=True)
        data = settings_get_all()
        buf = io.BytesIO(json.dumps(data, indent=2, ensure_ascii=False).encode("utf-8"))
        buf.seek(0)
        await inter.followup.send(
            content=f"📦 Current settings from `{settings_path()}`",
            file=discord.File(buf, filename="settings.json"),
            ephemeral=True,
        )

    @settings.command(name="upload", description="[ADMIN] Upload and apply a settings.json")
    @app_commands.describe(file="A JSON file exported by /shaiadmin settings download (or matching the schema).")
    async def upload(self, inter: discord.Interaction, file: discord.Attachment):
        if not await _check_admin(inter):
            return
        await inter.response.defer(ephemeral=True, thinking=True)
        if not file or not file.filename.lower().endswith(".json"):
            await inter.followup.send("Please attach a `.json` file.", ephemeral=True)
            return
        try:
            raw = await file.read()
            obj = json.loads(raw.decode("utf-8"))
        except Exception:
            await inter.followup.send("❌ Invalid JSON file.", ephemeral=True)
            return

        try:
            changed_keys = settings_import_bulk(obj)
            await inter.followup.send(
                f"✅ Uploaded and applied `{len(changed_keys)}` keys: {', '.join(sorted(changed_keys))}."
                if changed_keys else "ℹ️ No changes detected.",
                ephemeral=True,
            )
        except ValidationError as ve:
            await inter.followup.send(f"❌ {ve}", ephemeral=True)
        except Exception as e:
            await inter.followup.send(f"❌ Upload failed: {e!r}", ephemeral=True)

async def setup(bot: commands.Bot):
    await bot.add_cog(ShaiAdminCog(bot))
