import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
import { getTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';

const fetchMock = vi.fn();

const req: McpToolRequest = {
  id: 'req_1',
  headers: { authorization: 'Bearer token_1' },
  jwtPayload: { sub: 'user_1', role: 'admin', productId: 'bytelyst-notes' },
  log: {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
  },
};

beforeAll(() => {
  process.env.JWT_SECRET ??= 'test-secret';
  process.env.BYTELYST_NOTES_BACKEND_URL ??= 'http://localhost:4016';
  vi.stubGlobal('fetch', fetchMock);
  return import('./notes-tools.js');
});

afterEach(() => {
  fetchMock.mockReset();
});

describe('notes MCP tools', () => {
  it('maps list output to the shared MCP contract shape', async () => {
    fetchMock.mockResolvedValue({
      ok: true,
      json: async () => ({
        items: [
          {
            id: 'note_1',
            productId: 'bytelyst-notes',
            workspaceId: 'ws_1',
            userId: 'user_1',
            title: 'Weekly plan',
            body: 'body text',
            status: 'draft',
            tags: ['planning'],
            links: [],
            createdAt: '2026-03-10T00:00:00.000Z',
            updatedAt: '2026-03-10T01:00:00.000Z',
            createdBy: 'user_1',
            updatedBy: 'user_1',
          },
        ],
        total: 1,
        limit: 50,
        offset: 0,
      }),
    });

    const tool = getTool('notes.notes.list');
    const result = await tool?.execute({ workspaceId: 'ws_1', limit: 50, offset: 0 }, req);

    expect(result).toEqual({
      items: [
        {
          id: 'note_1',
          workspaceId: 'ws_1',
          title: 'Weekly plan',
          status: 'draft',
          updatedAt: '2026-03-10T01:00:00.000Z',
          tags: ['planning'],
        },
      ],
      total: 1,
      limit: 50,
      offset: 0,
    });
  });

  it('maps search output and derives matchFields from the query', async () => {
    fetchMock.mockResolvedValue({
      ok: true,
      json: async () => ({
        query: 'plan',
        items: [
          {
            id: 'note_1',
            productId: 'bytelyst-notes',
            workspaceId: 'ws_1',
            userId: 'user_1',
            title: 'Weekly plan',
            body: 'Plan the sprint and align tags',
            status: 'draft',
            tags: ['planning'],
            links: [],
            createdAt: '2026-03-10T00:00:00.000Z',
            updatedAt: '2026-03-10T01:00:00.000Z',
            createdBy: 'user_1',
            updatedBy: 'user_1',
          },
        ],
        total: 1,
        limit: 25,
        offset: 0,
      }),
    });

    const tool = getTool('notes.notes.search');
    const result = await tool?.execute(
      { workspaceId: 'ws_1', query: 'plan', limit: 25, offset: 0 },
      req
    );

    expect(result).toEqual({
      query: 'plan',
      items: [
        {
          id: 'note_1',
          workspaceId: 'ws_1',
          title: 'Weekly plan',
          status: 'draft',
          updatedAt: '2026-03-10T01:00:00.000Z',
          tags: ['planning'],
          matchFields: ['title', 'body', 'tags'],
        },
      ],
      total: 1,
      limit: 25,
      offset: 0,
    });
  });

  it('returns the shared create_draft response shape and records audit metadata', async () => {
    fetchMock
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({
          id: 'note_1',
          productId: 'bytelyst-notes',
          workspaceId: 'ws_1',
          userId: 'user_1',
          title: 'Draft title',
          body: 'Draft body',
          status: 'draft',
          tags: ['draft'],
          links: ['https://example.com'],
          sourceType: 'web',
          sourceUri: 'https://example.com',
          createdAt: '2026-03-10T00:00:00.000Z',
          updatedAt: '2026-03-10T00:00:00.000Z',
          createdBy: 'user_1',
          updatedBy: 'user_1',
          agentId: 'agent_1',
        }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({
          id: 'action_1',
          noteId: 'note_1',
          state: 'proposed',
          toolName: 'notes.notes.create_draft',
        }),
      });

    const tool = getTool('notes.notes.create_draft');
    const result = await tool?.execute(
      {
        workspaceId: 'ws_1',
        title: 'Draft title',
        body: 'Draft body',
        tags: ['draft'],
        links: ['https://example.com'],
        sourceType: 'web',
        sourceUri: 'https://example.com',
        agentId: 'agent_1',
        idempotencyKey: 'idem_1',
        correlationId: 'corr_1',
        dryRun: false,
      },
      req
    );

    expect(fetchMock).toHaveBeenCalledTimes(2);
    expect(fetchMock.mock.calls[1]?.[1]).toMatchObject({
      method: 'POST',
    });
    expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toMatchObject({
      workspaceId: 'ws_1',
      noteId: 'note_1',
      actorId: 'agent_1',
      actorType: 'agent',
      toolName: 'notes.notes.create_draft',
      actionType: 'create',
      state: 'proposed',
      idempotencyKey: 'idem_1',
      correlationId: 'corr_1',
      workflowId: 'req_1',
    });
    expect(result).toEqual({
      dryRun: false,
      state: 'draft',
      note: {
        id: 'note_1',
        workspaceId: 'ws_1',
        title: 'Draft title',
        body: 'Draft body',
        status: 'draft',
        tags: ['draft'],
        links: ['https://example.com'],
        sourceType: 'web',
        sourceUri: 'https://example.com',
        agentId: 'agent_1',
        createdAt: '2026-03-10T00:00:00.000Z',
        updatedAt: '2026-03-10T00:00:00.000Z',
      },
      idempotencyKey: 'idem_1',
      correlationId: 'corr_1',
    });
  });
});
