import { describe, expect, it, mock } from 'bun:test';
import { createResearchBridge } from './researchBridge';

describe('createResearchBridge', () => {
  it('passes request payloads to the expected Tauri commands', async () => {
    const invoke = mock(
      async <T>(command: string, args?: Record<string, unknown>): Promise<T> => {
        switch (command) {
          case 'list_research_workspaces':
            return [] as T;
          case 'create_research_workspace':
            expect(args).toEqual({
              request: {
                name: 'NVDA Research',
                primaryTicker: 'NVDA',
              },
            });
            return {
              id: 'workspace-1',
              name: 'NVDA Research',
              primaryTicker: 'NVDA',
              scope: 'single_company',
              stage: 'capture',
              defaultView: 'canvas',
              pinnedNoteIds: [],
              archived: false,
              createdAt: '2026-04-09T10:00:00Z',
              updatedAt: '2026-04-09T10:00:00Z',
            } as T;
          case 'capture_research_note':
            expect(args).toEqual({
              request: {
                workspaceId: 'workspace-1',
                rawText: 'Demand inflection may be forming.',
                ticker: 'NVDA',
              },
            });
            return {
              note: {
                id: 'note-1',
                workspaceId: 'workspace-1',
                rawText: 'Demand inflection may be forming.',
                cleanedText: 'Demand inflection may be forming.',
                embeds: [],
                fileIds: [],
                noteType: 'claim',
                analystStatus: 'captured',
                confidence: 0.7,
                evidenceStatus: 'unsourced',
                inferredLinks: [],
                ghostStatus: 'none',
                thesisStatus: 'none',
                createdAt: '2026-04-09T10:00:00Z',
                updatedAt: '2026-04-09T10:00:00Z',
                provenance: {
                  createdBy: 'manual',
                  captureMethod: 'quick_entry',
                  sourceKind: 'manual',
                  createdAt: '2026-04-09T10:00:00Z',
                  rawInputHash: 'hash',
                },
                tags: [],
                catalysts: [],
                risks: [],
                valuationRefs: [],
                priority: 'normal',
                pinned: false,
                archived: false,
                revision: 1,
              },
              placementHint: {
                tileLane: 'normal',
                kanbanColumn: 'claim',
              },
              queuedJobs: [],
            } as T;
          case 'preview_note_link':
            expect(args).toEqual({
              request: {
                url: 'https://example.com/article',
                kind: 'article',
              },
            });
            return {
              embed: {
                url: 'https://example.com/article',
                title: 'Example article',
                publisher: 'example.com',
                excerpt: 'Example excerpt',
                kind: 'article',
              },
              sourceRef: {
                kind: 'article',
                title: 'Example article',
                url: 'https://example.com/article',
                excerptText: 'Example excerpt',
              },
              suggestedTitle: 'Example article',
              suggestedBodyText: 'Example excerpt',
            } as T;
          case 'get_workspace_projection':
            expect(args).toEqual({
              request: {
                workspaceId: 'workspace-1',
                view: 'canvas',
              },
            });
            return {
              workspace: {
                id: 'workspace-1',
                name: 'NVDA Research',
                primaryTicker: 'NVDA',
                scope: 'single_company',
                stage: 'capture',
                defaultView: 'canvas',
                pinnedNoteIds: [],
                archived: false,
                createdAt: '2026-04-09T10:00:00Z',
                updatedAt: '2026-04-09T10:00:00Z',
              },
              activeView: 'canvas',
              notes: [],
              links: [],
              ghosts: [],
              memoBlocks: [],
              graphNodes: [],
              graphEdges: [],
              kanbanColumns: [],
              timelineEvents: [],
            } as T;
          case 'review_ghost_note':
            expect(args).toEqual({
              request: {
                ghostNoteId: 'ghost-1',
                action: 'accept',
              },
            });
            return {
              id: 'ghost-1',
              workspaceId: 'workspace-1',
              ghostClass: 'candidate_thesis',
              headline: 'Possible thesis',
              body: 'Observed: ...',
              tone: 'tentative',
              confidence: 0.9,
              visibilityState: 'visible',
              state: 'accepted',
              supportingNoteIds: [],
              contradictingNoteIds: [],
              sourceIds: [],
              evidenceThresholdMet: true,
              createdAt: '2026-04-09T10:00:00Z',
              updatedAt: '2026-04-09T10:00:00Z',
            } as T;
          case 'resolve_ghost_intervention':
            expect(args).toEqual({
              request: {
                ghostNoteId: 'ghost-1',
                workspaceId: 'workspace-1',
                resolutionStatus: 'waived',
                actionTaken: 'mark_waived',
              },
            });
            return {
              id: 'ghost-1',
              workspaceId: 'workspace-1',
              ghostClass: 'claim_pressure_test',
              headline: 'Pressure-test claim',
              body: 'Observed: ...',
              tone: 'tentative',
              confidence: 0.9,
              visibilityState: 'collapsed',
              state: 'ignored',
              supportingNoteIds: [],
              contradictingNoteIds: [],
              sourceIds: [],
              evidenceThresholdMet: false,
              resolutionStatus: 'waived',
              recommendedAction: 'mark_waived',
              createdAt: '2026-04-09T10:00:00Z',
              updatedAt: '2026-04-09T10:00:00Z',
            } as T;
          case 'list_research_jobs':
            expect(args).toEqual({
              request: {
                workspaceId: 'workspace-1',
              },
            });
            return [
              {
                id: 'job-1',
                workspaceId: 'workspace-1',
                entityId: 'note-1',
                jobKind: 'enrich_note',
                status: 'queued',
                attemptCount: 0,
                maxAttempts: 3,
                payloadJson: {},
                createdAt: '2026-04-09T10:00:00Z',
                updatedAt: '2026-04-09T10:00:00Z',
              },
            ] as T;
          case 'list_research_files':
            expect(args).toEqual({
              request: {
                workspaceId: 'workspace-1',
              },
            });
            return [] as T;
          case 'upload_research_files':
            expect(args).toEqual({
              request: {
                workspaceId: 'workspace-1',
                files: [
                  {
                    name: 'note.txt',
                    mimeType: 'text/plain',
                    bytes: [104, 105],
                  },
                ],
              },
            });
            return [
              {
                id: 'file-1',
                workspaceId: 'workspace-1',
                originalName: 'note.txt',
                storedPath: '/tmp/note.txt',
                mimeType: 'text/plain',
                sizeBytes: 2,
                checksum: 'hash',
                previewKind: 'text',
                createdAt: '2026-04-09T10:00:00Z',
                updatedAt: '2026-04-09T10:00:00Z',
              },
            ] as T;
          case 'get_research_file_text_preview':
            expect(args).toEqual({
              request: {
                fileId: 'file-1',
              },
            });
            return {
              fileId: 'file-1',
              text: 'hi',
              truncated: false,
            } as T;
          default:
            throw new Error(`Unexpected command: ${command}`);
        }
      },
    );

    const bridge = createResearchBridge(invoke);
    const workspaces = await bridge.listResearchWorkspaces();
    const workspace = await bridge.createResearchWorkspace({
      name: 'NVDA Research',
      primaryTicker: 'NVDA',
    });
    const capture = await bridge.captureResearchNote({
      workspaceId: 'workspace-1',
      rawText: 'Demand inflection may be forming.',
      ticker: 'NVDA',
    });
    const projection = await bridge.getWorkspaceProjection({
      workspaceId: 'workspace-1',
      view: 'canvas',
    });
    const preview = await bridge.previewNoteLink({
      url: 'https://example.com/article',
      kind: 'article',
    });
    const ghost = await bridge.reviewGhostNote({
      ghostNoteId: 'ghost-1',
      action: 'accept',
    });
    const resolvedGhost = await bridge.resolveGhostIntervention({
      ghostNoteId: 'ghost-1',
      workspaceId: 'workspace-1',
      resolutionStatus: 'waived',
      actionTaken: 'mark_waived',
    });
    const jobs = await bridge.listResearchJobs({
      workspaceId: 'workspace-1',
    });
    const files = await bridge.listResearchFiles({
      workspaceId: 'workspace-1',
    });
    const uploaded = await bridge.uploadResearchFiles({
      workspaceId: 'workspace-1',
      files: [{ name: 'note.txt', mimeType: 'text/plain', bytes: [104, 105] }],
    });
    const textPreview = await bridge.getResearchFileTextPreview({
      fileId: 'file-1',
    });

    expect(workspaces).toEqual([]);
    expect(workspace.primaryTicker).toBe('NVDA');
    expect(capture.note.id).toBe('note-1');
    expect(preview.embed.title).toBe('Example article');
    expect(projection.activeView).toBe('canvas');
    expect(ghost.state).toBe('accepted');
    expect(resolvedGhost.resolutionStatus).toBe('waived');
    expect(jobs[0]?.id).toBe('job-1');
    expect(files).toEqual([]);
    expect(uploaded[0]?.previewKind).toBe('text');
    expect(textPreview.text).toBe('hi');
  });

  it('subscribes to the research event channels', async () => {
    const listen = mock(
      async <T>(
        event: string,
        handler: (event: { payload: T }) => void,
      ): Promise<() => void> => {
        if (event === 'research_workspace_updated') {
          handler({
            payload: {
              id: 'workspace-1',
              name: 'NVDA Research',
              primaryTicker: 'NVDA',
              scope: 'single_company',
              stage: 'capture',
              defaultView: 'canvas',
              pinnedNoteIds: [],
              archived: false,
              createdAt: '2026-04-09T10:00:00Z',
              updatedAt: '2026-04-09T10:00:00Z',
            } as T,
          });
        } else if (event === 'research_note_updated') {
          handler({
            payload: {
              id: 'note-1',
              workspaceId: 'workspace-1',
            } as T,
          });
        } else if (event === 'research_ghost_updated') {
          handler({
            payload: {
              id: 'ghost-1',
              workspaceId: 'workspace-1',
            } as T,
          });
        } else {
          handler({
            payload: {
              id: 'job-1',
              workspaceId: 'workspace-1',
            } as T,
          });
        }
        return () => {};
      },
    );
    const bridge = createResearchBridge(async () => undefined as never, listen);
    const workspaceHandler = mock(() => {});
    const noteHandler = mock(() => {});
    const ghostHandler = mock(() => {});
    const jobHandler = mock(() => {});

    await bridge.listenForWorkspaceUpdates(workspaceHandler);
    await bridge.listenForNoteUpdates(noteHandler);
    await bridge.listenForGhostUpdates(ghostHandler);
    await bridge.listenForJobUpdates(jobHandler);

    expect(listen).toHaveBeenCalledWith(
      'research_workspace_updated',
      expect.any(Function),
    );
    expect(listen).toHaveBeenCalledWith(
      'research_note_updated',
      expect.any(Function),
    );
    expect(listen).toHaveBeenCalledWith(
      'research_ghost_updated',
      expect.any(Function),
    );
    expect(listen).toHaveBeenCalledWith(
      'research_job_updated',
      expect.any(Function),
    );
    expect(workspaceHandler).toHaveBeenCalledWith({
      workspaceId: 'workspace-1',
      workspace: {
        id: 'workspace-1',
        name: 'NVDA Research',
        primaryTicker: 'NVDA',
        scope: 'single_company',
        stage: 'capture',
        defaultView: 'canvas',
        pinnedNoteIds: [],
        archived: false,
        createdAt: '2026-04-09T10:00:00Z',
        updatedAt: '2026-04-09T10:00:00Z',
      },
    });
    expect(noteHandler).toHaveBeenCalledWith({
      workspaceId: 'workspace-1',
      note: {
        id: 'note-1',
        workspaceId: 'workspace-1',
      },
    });
    expect(ghostHandler).toHaveBeenCalledWith({
      workspaceId: 'workspace-1',
      ghost: {
        id: 'ghost-1',
        workspaceId: 'workspace-1',
      },
    });
    expect(jobHandler).toHaveBeenCalledWith({
      workspaceId: 'workspace-1',
      job: {
        id: 'job-1',
        workspaceId: 'workspace-1',
      },
    });
  });
});
