"""Discord ingestion via a discord.py bot.

Scope is enforced by Discord itself, not by this code: a bot can only see guilds
it has been invited to, and cannot read personal DMs at all. There is deliberately
no selfbot path here — that is an explicit ToS violation with real ban risk
(docs/project-plan.md Phase 12 step 4).

The bot needs the Message Content privileged intent enabled at
https://discord.com/developers/applications -> your app -> Bot -> Privileged
Gateway Intents, otherwise every message body arrives empty.

Bots have no read-state on Discord, so reading history acknowledges nothing.
"""

import asyncio
import logging
import os
from datetime import datetime, timedelta, timezone

LOG = logging.getLogger(__name__)

MAX_BODY_CHARS = 2000


async def _fetch_async(lookback_hours, token, max_per_channel, connect_timeout):
    import discord

    since = datetime.now(timezone.utc) - timedelta(hours=lookback_hours)
    messages = []

    intents = discord.Intents.default()
    intents.message_content = True
    intents.guilds = True
    client = discord.Client(intents=intents)

    @client.event
    async def on_ready():
        try:
            for guild in client.guilds:
                for channel in guild.text_channels:
                    permissions = channel.permissions_for(guild.me)
                    if not (permissions.read_messages and permissions.read_message_history):
                        continue
                    try:
                        async for message in channel.history(after=since, limit=max_per_channel):
                            if not message.content:
                                continue
                            messages.append(
                                {
                                    "source": "discord",
                                    "guild": guild.name,
                                    "channel": channel.name,
                                    "from": message.author.display_name,
                                    "timestamp": message.created_at.isoformat(),
                                    "body": message.content[:MAX_BODY_CHARS],
                                }
                            )
                    except Exception:
                        LOG.warning(
                            "discord: could not read #%s in %s, skipping",
                            channel.name,
                            guild.name,
                            exc_info=True,
                        )
        finally:
            await client.close()

    await asyncio.wait_for(client.start(token), timeout=connect_timeout)
    return messages


def fetch(lookback_hours):
    token = os.environ.get("DISCORD_BOT_TOKEN", "").strip()
    max_per_channel = int(os.environ.get("DISCORD_MAX_MESSAGES_PER_CHANNEL", "50"))
    connect_timeout = float(os.environ.get("DISCORD_TIMEOUT", "120"))

    if not token:
        LOG.warning("discord: DISCORD_BOT_TOKEN not set, skipping")
        return []

    try:
        messages = asyncio.run(
            _fetch_async(lookback_hours, token, max_per_channel, connect_timeout)
        )
    except Exception:
        LOG.warning("discord: ingestion failed, returning nothing", exc_info=True)
        return []

    LOG.info("discord: %d message(s) in the last %sh", len(messages), lookback_hours)
    return messages
