/**
 * Awareness tools — read/write presence state.
 */
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
import { awarenessStatesToArray } from '@abraca/dabra'
import { yjsToMarkdown } from '../converters/yjsToMarkdown.ts'
import type { AbracadabraMCPServer } from '../server.ts'

export function registerAwarenessTools(mcp: McpServer, server: AbracadabraMCPServer) {
  mcp.tool(
    'set_presence',
    'Set the AI agent\'s presence state. Use this to indicate which document you are viewing or your current status.',
    {
      docId: z.string().optional().describe('Document ID the agent is currently viewing. Sets docId in root awareness.'),
      status: z.string().optional().describe('Status text (e.g. "writing", "reviewing", "thinking").'),
    },
    async ({ docId, status }) => {
      const rootProvider = server.rootYProvider
      if (!rootProvider) return { content: [{ type: 'text', text: 'Not connected' }] }

      if (docId !== undefined) {
        rootProvider.awareness.setLocalStateField('docId', docId)
      }
      if (status !== undefined) {
        rootProvider.awareness.setLocalStateField('status', status)
      }

      return { content: [{ type: 'text', text: 'Presence updated' }] }
    }
  )

  mcp.tool(
    'set_doc_awareness',
    'Set renderer-specific awareness fields on a child document. Use this to broadcast item-level presence (e.g. which kanban card, table cell, or slide you are interacting with). Setting a field to null clears it.',
    {
      docId: z.string().describe('Document ID to set awareness on.'),
      fields: z.record(z.string(), z.unknown()).describe('Key-value pairs to set on the child document\'s awareness state. Use namespaced keys like "kanban:hovering", "table:editing", "slides:viewing", "outline:editing", "calendar:focused", "gallery:focused", "timeline:focused", "graph:focused", "map:focused", "doc:scroll". Set a key to null to clear it.'),
    },
    async ({ docId, fields }) => {
      server.setActiveToolCall({ name: 'set_doc_awareness', target: docId })
      try {
        const provider = await server.getChildProvider(docId)
        for (const [key, value] of Object.entries(fields)) {
          provider.awareness.setLocalStateField(key, value ?? null)
        }
        return { content: [{ type: 'text', text: 'Doc awareness updated' }] }
      } catch (error: any) {
        return {
          content: [{ type: 'text', text: `Error: ${error.message}` }],
          isError: true,
        }
      }
    }
  )

  mcp.tool(
    'poll_inbox',
    'Check the "AI Inbox" document for pending instructions from humans. Returns the inbox content and any pending task sub-documents. Create the inbox as a doc called "AI Inbox" under the hub doc if it does not exist yet. Note: channel-based watching via watch_chat is preferred for real-time use.',
    {},
    async () => {
      server.setActiveToolCall({ name: 'poll_inbox' })
      try {
        const treeMap = server.getTreeMap()
        const rootDocId = server.rootDocId
        if (!treeMap || !rootDocId) {
          return { content: [{ type: 'text', text: 'Not connected' }] }
        }

        // Find "AI Inbox" doc that is a direct child of the hub doc
        let inboxId: string | null = null
        treeMap.forEach((value: any, id: string) => {
          if (
            value.parentId === rootDocId &&
            typeof value.label === 'string' &&
            value.label.toLowerCase() === 'ai inbox'
          ) {
            inboxId = id
          }
        })

        if (!inboxId) {
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                pending: [],
                message: 'No AI Inbox document found. Create one under the hub doc with label "AI Inbox".',
              }),
            }],
          }
        }

        const resolvedInboxId: string = inboxId
        const provider = await server.getChildProvider(resolvedInboxId)
        const fragment = provider.document.getXmlFragment('default')
        const markdown = yjsToMarkdown(fragment, 'AI Inbox')

        // Collect direct children of inbox (pending task sub-docs)
        const pendingTasks: Array<{ id: string; label: string }> = []
        treeMap.forEach((value: any, id: string) => {
          if (value.parentId === resolvedInboxId) {
            pendingTasks.push({ id, label: value.label || 'Untitled' })
          }
        })

        return {
          content: [{
            type: 'text',
            text: JSON.stringify({ inboxId: resolvedInboxId, content: markdown, pendingTasks }, null, 2),
          }],
        }
      } catch (error: any) {
        return {
          content: [{ type: 'text', text: `Error: ${error.message}` }],
          isError: true,
        }
      }
    }
  )

  mcp.tool(
    'list_connected_users',
    'List all connected users and their awareness state. Shows who is online and what they are doing.',
    {
      docId: z.string().optional().describe('If provided, list users connected to this specific document. Otherwise lists users from root awareness.'),
    },
    async ({ docId }) => {
      server.setActiveToolCall({ name: 'list_connected_users', target: docId })
      try {
        let awareness
        if (docId) {
          const provider = await server.getChildProvider(docId)
          awareness = provider.awareness
        } else {
          const rootProvider = server.rootYProvider
          if (!rootProvider) return { content: [{ type: 'text', text: 'Not connected' }] }
          awareness = rootProvider.awareness
        }

        const states = awarenessStatesToArray(awareness.getStates())
        // Filter out empty states
        const users = states.filter(s => s.user)

        return {
          content: [{
            type: 'text',
            text: JSON.stringify(users, null, 2),
          }],
        }
      } catch (error: any) {
        return {
          content: [{ type: 'text', text: `Error: ${error.message}` }],
          isError: true,
        }
      }
    }
  )
}
