import re
from datetime import UTC, datetime

from fastmcp import Context, FastMCP

from relay.server.auth import AuthStore
from relay.server.models import Message
from relay.server.state import RelayState


def _format_message_line(m: Message) -> str:
    ts = m.timestamp.strftime("%H:%M:%S")
    return f"[{m.group_id}] [{ts}] {m.from_display}: {m.content}"


def register_tools(mcp: FastMCP, state: RelayState, auth_store: AuthStore) -> None:
    async def _resolve(
        ctx: Context, group_id: str | None = None
    ) -> tuple[str, list[str]]:
        info = auth_store.get(ctx.session_id)
        await state.ensure_peer_session(
            ctx.session_id, info.peer_id, info.authorized_groups,
            info.hostname, info.working_directory,
        )
        if group_id is not None and group_id not in info.authorized_groups:
            raise ValueError(f"Not authorized for group: {group_id}")
        return info.peer_id, info.authorized_groups

    @mcp.tool()
    async def send_message(ctx: Context, group_id: str, message: str) -> str:
        """Send a message to a group. All members of the group will see it.
        group_id: the target group to post into (use list_my_groups to see your groups).
        message: the text content to send."""
        peer_id, _ = await _resolve(ctx, group_id)
        msg = await state.send_message(group_id, peer_id, message)
        return f"Sent message {msg.message_id} to group {group_id}"

    @mcp.tool()
    async def read_new_messages(ctx: Context) -> list[str]:
        """Fetch all unread messages across every group you belong to.
        Returns messages formatted as [group_id] [timestamp] sender: content.
        Call this whenever you receive a relay notification."""
        peer_id, authorized_groups = await _resolve(ctx)
        all_messages: list[Message] = []
        for gid in authorized_groups:
            msgs = await state.read_new_messages(gid, peer_id)
            all_messages.extend(msgs)
        all_messages.sort(key=lambda m: m.timestamp)
        if not all_messages:
            return ["No new messages"]
        return [_format_message_line(m) for m in all_messages]

    @mcp.tool()
    async def list_peers(ctx: Context, group_id: str) -> list[str]:
        """List all members of a group and whether they are currently online.
        group_id: the group to query."""
        await _resolve(ctx, group_id)
        peers = await state.list_peers(group_id)
        return [
            f"{p.display_name} ({p.peer_id}) {'online' if p.online else 'offline'}"
            for p in peers
        ]

    @mcp.tool()
    async def get_history(
        ctx: Context,
        group_id: str,
        filter: str | None = None,
        start_time: str | None = None,
        end_time: str | None = None,
    ) -> list[str]:
        """Retrieve the full message history for a group.
        group_id: the group to retrieve history from.
        filter: optional regex pattern to match against message content.
        start_time: optional ISO 8601 datetime to start from (inclusive).
        end_time: optional ISO 8601 datetime to end at (inclusive)."""
        await _resolve(ctx, group_id)
        messages = await state.get_history(group_id)
        if start_time is not None:
            try:
                start_dt = datetime.fromisoformat(start_time)
            except ValueError as e:
                raise ValueError(f"Invalid start_time format: {e}")
            if start_dt.tzinfo is None:
                start_dt = start_dt.replace(tzinfo=UTC)
            messages = [m for m in messages if m.timestamp >= start_dt]
        if end_time is not None:
            try:
                end_dt = datetime.fromisoformat(end_time)
            except ValueError as e:
                raise ValueError(f"Invalid end_time format: {e}")
            if end_dt.tzinfo is None:
                end_dt = end_dt.replace(tzinfo=UTC)
            messages = [m for m in messages if m.timestamp <= end_dt]
        if filter is not None:
            try:
                pattern = re.compile(filter, re.IGNORECASE)
            except re.error as e:
                raise ValueError(f"Invalid filter pattern: {e}")
            messages = [m for m in messages if pattern.search(m.content)]
        if not messages:
            return ["No messages found"]
        return [_format_message_line(m) for m in messages]

    @mcp.tool()
    async def leave_group(ctx: Context, group_id: str) -> str:
        """Disconnect from a group. You will stop receiving messages from it.
        group_id: the group to leave."""
        await _resolve(ctx, group_id)
        await state.leave_group(ctx.session_id, group_id)
        return f"Left group {group_id}"

    @mcp.tool()
    async def list_my_groups(ctx: Context) -> list[str]:
        """List the names of all groups you are currently a member of."""
        _, authorized_groups = await _resolve(ctx)
        return authorized_groups
