import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import { z } from 'zod';
import { canAccessTrip } from '../../db/database';
import { isDemoUser } from '../../services/authService';
import {
  listItems as listTodoItems, createItem as createTodoItem, updateItem as updateTodoItem,
  deleteItem as deleteTodoItem, reorderItems as reorderTodoItems,
  getCategoryAssignees as getTodoCategoryAssignees, updateCategoryAssignees as updateTodoCategoryAssignees,
} from '../../services/todoService';
import {
  safeBroadcast, TOOL_ANNOTATIONS_READONLY, TOOL_ANNOTATIONS_WRITE,
  TOOL_ANNOTATIONS_DELETE, TOOL_ANNOTATIONS_NON_IDEMPOTENT,
  demoDenied, noAccess, ok,
} from './_shared';
import { canRead, canWrite } from '../scopes';
import { isAddonEnabled } from '../../services/adminService';
import { ADDON_IDS } from '../../addons';

export function registerTodoTools(server: McpServer, userId: number, scopes: string[] | null): void {
  const R = canRead(scopes, 'todos');
  const W = canWrite(scopes, 'todos');

  if (!isAddonEnabled(ADDON_IDS.PACKING)) return;

  // --- TODOS ---

  if (R) server.registerTool(
    'list_todos',
    {
      description: 'List all to-do items for a trip, ordered by position.',
      inputSchema: {
        tripId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_READONLY,
    },
    async ({ tripId }) => {
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const items = listTodoItems(tripId);
      return ok({ items });
    }
  );

  if (W) server.registerTool(
    'create_todo',
    {
      description: 'Create a new to-do item for a trip.',
      inputSchema: {
        tripId: z.number().int().positive(),
        name: z.string().min(1).max(500).describe('To-do item name'),
        category: z.string().max(100).optional().describe('Category (e.g. "Logistics", "Booking")'),
        due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe('Due date (YYYY-MM-DD)'),
        description: z.string().max(2000).optional().describe('Additional description'),
        assigned_user_id: z.number().int().positive().optional().describe('User ID to assign this task to'),
        priority: z.number().int().min(0).max(3).optional().describe('Priority: 0=none, 1=low, 2=medium, 3=high'),
      },
      annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT,
    },
    async ({ tripId, name, category, due_date, description, assigned_user_id, priority }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const item = createTodoItem(tripId, { name, category, due_date, description, assigned_user_id, priority });
      safeBroadcast(tripId, 'todo:created', { item });
      return ok({ item });
    }
  );

  if (W) server.registerTool(
    'update_todo',
    {
      description: 'Update an existing to-do item. Only provided fields are changed; omitted fields stay as-is. Pass null to clear a nullable field.',
      inputSchema: {
        tripId: z.number().int().positive(),
        itemId: z.number().int().positive(),
        name: z.string().min(1).max(500).optional(),
        category: z.string().max(100).optional(),
        due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional().describe('Set to null to clear the due date'),
        description: z.string().max(2000).nullable().optional().describe('Set to null to clear'),
        assigned_user_id: z.number().int().positive().nullable().optional().describe('Set to null to unassign'),
        priority: z.number().int().min(0).max(3).nullable().optional(),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, itemId, name, category, due_date, description, assigned_user_id, priority }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      // Build bodyKeys to signal which nullable fields were explicitly provided
      const bodyKeys: string[] = [];
      if (due_date !== undefined) bodyKeys.push('due_date');
      if (description !== undefined) bodyKeys.push('description');
      if (assigned_user_id !== undefined) bodyKeys.push('assigned_user_id');
      if (priority !== undefined) bodyKeys.push('priority');
      const item = updateTodoItem(tripId, itemId, { name, category, due_date, description, assigned_user_id, priority }, bodyKeys);
      if (!item) return { content: [{ type: 'text' as const, text: 'To-do item not found.' }], isError: true };
      safeBroadcast(tripId, 'todo:updated', { item });
      return ok({ item });
    }
  );

  if (W) server.registerTool(
    'toggle_todo',
    {
      description: 'Mark a to-do item as checked (done) or unchecked.',
      inputSchema: {
        tripId: z.number().int().positive(),
        itemId: z.number().int().positive(),
        checked: z.boolean().describe('True to mark done, false to uncheck'),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, itemId, checked }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const item = updateTodoItem(tripId, itemId, { checked: checked ? 1 : 0 }, []);
      if (!item) return { content: [{ type: 'text' as const, text: 'To-do item not found.' }], isError: true };
      safeBroadcast(tripId, 'todo:updated', { item });
      return ok({ item });
    }
  );

  if (W) server.registerTool(
    'delete_todo',
    {
      description: 'Delete a to-do item.',
      inputSchema: {
        tripId: z.number().int().positive(),
        itemId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_DELETE,
    },
    async ({ tripId, itemId }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const deleted = deleteTodoItem(tripId, itemId);
      if (!deleted) return { content: [{ type: 'text' as const, text: 'To-do item not found.' }], isError: true };
      safeBroadcast(tripId, 'todo:deleted', { itemId });
      return ok({ success: true });
    }
  );

  if (W) server.registerTool(
    'reorder_todos',
    {
      description: 'Reorder to-do items within a trip by providing a new ordered list of item IDs.',
      inputSchema: {
        tripId: z.number().int().positive(),
        orderedIds: z.array(z.number().int().positive()).min(1).describe('All item IDs in the desired order'),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, orderedIds }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      reorderTodoItems(tripId, orderedIds);
      return ok({ success: true });
    }
  );

  if (R) server.registerTool(
    'get_todo_category_assignees',
    {
      description: 'Get the default assignees configured per to-do category for a trip.',
      inputSchema: {
        tripId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_READONLY,
    },
    async ({ tripId }) => {
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const assignees = getTodoCategoryAssignees(tripId);
      return ok({ assignees });
    }
  );

  if (W) server.registerTool(
    'set_todo_category_assignees',
    {
      description: 'Set the default assignees for a to-do category on a trip. Pass an empty array to clear.',
      inputSchema: {
        tripId: z.number().int().positive(),
        categoryName: z.string().min(1).max(100).describe('Category name'),
        userIds: z.array(z.number().int().positive()).describe('User IDs to assign as defaults for this category'),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, categoryName, userIds }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const assignees = updateTodoCategoryAssignees(tripId, categoryName, userIds);
      safeBroadcast(tripId, 'todo:assignees', { category: categoryName, assignees });
      return ok({ assignees });
    }
  );
}
