import os, signal, asyncio, pathlib
import discord
from discord.ext import commands
from dotenv import load_dotenv

from data_manager import DataManager
from modules.common.settings import cfg as cfg_helper
from modules.common.boot_notice import post_boot_notice

# Version consists of:
# Major.Enhancement.Minor.Patch.Test  (Test is alphanumeric; doesn’t trigger auto update)
VERSION = "0.5.1.1.a1"

# ---------- Env loading ----------
load_dotenv()

def _get_env(name: str, default: str = "") -> str:
    v = os.getenv(name, "")
    return (v or "").strip().strip('"').strip("'") or default

TOKEN = _get_env("DISCORD_TOKEN")
DATA_FILE = _get_env("DATA_FILE") or "./data/data.json"

print("[Config] DISCORD_TOKEN set:", bool(TOKEN))
print("[Config] DATA_FILE:", DATA_FILE)

# ---------- Ensure data path exists (fallback if not writable) ----------
data_dir = os.path.dirname(DATA_FILE) or "."
try:
    os.makedirs(data_dir, exist_ok=True)
except PermissionError:
    fallback = "./data/data.json"
    print(f"[Config] No permission to create '{data_dir}'. Falling back to {fallback}")
    DATA_FILE = fallback
    data_dir = os.path.dirname(DATA_FILE)
    os.makedirs(data_dir, exist_ok=True)

if not os.path.exists(DATA_FILE):
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        f.write("{}")

# ---------- Discord intents ----------

intents = discord.Intents.default()
intents.guilds = True
intents.members = True
intents.message_content = True
intents.reactions = True
intents.emojis_and_stickers = True
intents.voice_states = True

# ---------- Bot + DataManager ----------

if not TOKEN:
    print("[Config] WARNING: DISCORD_TOKEN is empty. The bot will fail to log in.")

bot = commands.Bot(command_prefix="!", intents=intents)

# Ensure data path exists and is seeded
os.makedirs(os.path.dirname(DATA_FILE) or ".", exist_ok=True)
if not os.path.exists(DATA_FILE):
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        f.write("{}")

bot.data_manager = DataManager(DATA_FILE)

# ---------- Self-check: resolve from ENV first, then cfg_helper ----------
def _resolve_channel_id(c, key: str) -> int:
    """
    Resolve channel IDs from the runtime settings store (cfg), with a final
    fallback to legacy bot.config['DEFAULT'] if present. No SHAI_* env usage.
    """
    try:
        v = int(c.int(key, 0))
        if v:
            return v
    except Exception:
        pass

    try:
        # legacy DEFAULT mapping (ConfigParser-like or our shim)
        v = int(getattr(c, "get", lambda *_: 0)(key, 0))
        if v:
            return v
    except Exception:
        pass

    return 0

async def _guild_selfcheck(g: discord.Guild, c):
    problems = []

    def _need_channel(id_key, *perms):
        cid = _resolve_channel_id(c, id_key)
        if not cid:
            problems.append(f"Missing config key: {id_key}")
            return

        ch = g.get_channel(cid)
        if not ch:
            problems.append(f"Channel not found: {id_key}={cid}")
            return

        me = g.me
        p = ch.permissions_for(me)
        for perm in perms:
            if not getattr(p, perm, False):
                problems.append(f"Missing permission on #{ch.name}: {perm}")

    _need_channel('mod_channel_id', 'read_messages', 'send_messages', 'add_reactions', 'read_message_history')
    _need_channel('modlog_channel_id', 'read_messages', 'send_messages')
    _need_channel('pirates_list_channel_id', 'read_messages', 'send_messages')
    _need_channel('dd_channel_id', 'read_messages', 'send_messages', 'read_message_history')

    if problems:
        print(f"[SelfCheck:{g.name}]")
        for p in problems:
            print(" -", p)

# ---------- events ----------

@bot.event
async def on_ready():
    import asyncio

    print(f"Logged in as {bot.user} (ID: {bot.user.id})")
    print("[Intents] members:", bot.intents.members,
          "/ message_content:", bot.intents.message_content,
          "/ voice_states:", bot.intents.voice_states)

    env_cfg = cfg_helper(bot)

    # Per-guild permission sanity checks (env-aware)
    try:
        await asyncio.gather(*[_guild_selfcheck(g, env_cfg) for g in bot.guilds])
    except Exception as e:
        print("[SelfCheck] failed:", repr(e))

    # ---------- Slash command scope & sync ----------
    try:
        # env_cfg already exists above in on_ready()
        gid = env_cfg.int("home_guild_id", 0)

        if gid > 0:
            print(f"[Slash] Mode: GUILD-ONLY → {gid}")

            guild_obj = discord.Object(id=gid)

            # Copy all currently-loaded global commands to HOME guild
            bot.tree.copy_global_to(guild=guild_obj)
            g_cmds = await bot.tree.sync(guild=guild_obj)
            g_names = ", ".join(f"/{c.name}" for c in g_cmds) if g_cmds else "(none)"
            print(f"[Slash] Synced {len(g_cmds)} commands to guild {gid}: {g_names}")

            # Clear global so only guild-scoped remain
            bot.tree.clear_commands(guild=None)
            cleared = await bot.tree.sync()  # push empty global set
            print(f"[Slash] Cleared global commands (now {len(cleared)}).")

            # Debug: list actual state after sync
            try:
                global_cmds = await bot.tree.fetch_commands()
                print(f"[Slash] Global commands ({len(global_cmds)}): {', '.join(f'/{c.name}' for c in global_cmds) or '(none)'}")
            except Exception as e:
                print("[Slash] Failed to fetch global commands:", repr(e))

            try:
                g_cmds = await bot.tree.fetch_commands(guild=guild_obj)
                print(f"[Slash] Guild {gid} commands ({len(g_cmds)}): {', '.join(f'/{c.name}' for c in g_cmds) or '(none)'}")
            except Exception as e:
                print(f"[Slash] Failed to fetch commands for guild {gid}:", repr(e))
        else:
            print("[Slash] Mode: GLOBAL (HOME_GUILD_ID not set)")
            global_cmds = await bot.tree.sync()
            names = ", ".join(f"/{c.name}" for c in global_cmds) if global_cmds else "(none)"
            print(f"[Slash] Synced {len(global_cmds)} commands globally: {names}")

    except Exception as e:
        print("[Slash] Sync failed:", repr(e))

# ---------- Auto-discover extensions ----------

modules_path = pathlib.Path(__file__).parent / "modules"
extensions = []
for folder in modules_path.iterdir():
    if not folder.is_dir():
        continue
    # skip non-cog helpers under modules/common
    if folder.name == "common":
        continue
    for file in folder.glob("*.py"):
        if file.name == "__init__.py":
            continue
        extensions.append(f"modules.{folder.name}.{file.stem}")

def _install_signal_handlers(loop, bot_obj):
    def _graceful(*_):
        loop.create_task(bot_obj.close())
    for s in (signal.SIGTERM, signal.SIGINT):
        try:
            loop.add_signal_handler(s, _graceful)
        except NotImplementedError:
            pass  # Windows

async def main():
    print(f"[STARTUP] ShaiWatcher booting v{VERSION}")
    async with bot:
        for ext in extensions:
            try:
                await bot.load_extension(ext)
                print(f"[Modules] Loaded: {ext}")
            except Exception as e:
                print(f"[Modules] Failed to load {ext}:", repr(e))
        loop = asyncio.get_running_loop()
        _install_signal_handlers(loop, bot)
        await bot.start(TOKEN)

if __name__ == "__main__":
    asyncio.run(main())
