import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';

import ThreadDetailPage from '../../src/app/(app)/threads/[threadId]/page';
import { AgentThread } from '../../src/components/agent-workspace/agent-thread';
import { extractFileDiff } from '../../src/components/agent-workspace/diff-utils';
import { Hero } from '../../src/components/landing';
import { NewSpoonForm } from '../../src/components/spoons/new-spoon-form';

const { mockUseMutation, mockUseParams, mockUseQuery } = vi.hoisted(() => ({
  mockUseMutation: vi.fn(),
  mockUseParams: vi.fn(),
  mockUseQuery: vi.fn(),
}));

vi.mock('convex/react', () => ({
  useConvexAuth: () => ({ isAuthenticated: false }),
  useMutation: mockUseMutation,
  useQuery: mockUseQuery,
}));

vi.mock('next/navigation', () => ({
  useParams: mockUseParams,
  useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
}));

vi.mock('sonner', () => ({
  toast: {
    error: vi.fn(),
    success: vi.fn(),
  },
}));

vi.mock('@/components/agent-workspace/agent-workspace-shell', () => ({
  AgentWorkspaceShell: ({ jobId }: { jobId: string }) => (
    <div>workspace shell {jobId}</div>
  ),
}));

describe('component test harness', () => {
  it('renders the Spoon landing headline', () => {
    render(<Hero />);
    expect(
      screen.getByRole('heading', {
        name: /fork freely & keep them all intimately close to upstream\./i,
      }),
    ).toBeInTheDocument();
  });

  it('renders the new Spoon form fields', () => {
    render(<NewSpoonForm />);
    expect(screen.getByLabelText(/spoon name/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/upstream owner/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/upstream repository/i)).toBeInTheDocument();
  });

  it('extracts a single file diff from a workspace diff', () => {
    const diff = [
      'diff --git a/apps/web/auth.ts b/apps/web/auth.ts',
      'index 123..456 100644',
      '--- a/apps/web/auth.ts',
      '+++ b/apps/web/auth.ts',
      '@@ -1 +1 @@',
      '-github',
      '+authentik',
      'diff --git a/README.md b/README.md',
      '--- a/README.md',
      '+++ b/README.md',
      '@@ -1 +1 @@',
      '-old',
      '+new',
    ].join('\n');

    expect(extractFileDiff(diff, 'apps/web/auth.ts')).toContain('+authentik');
    expect(extractFileDiff(diff, 'apps/web/auth.ts')).not.toContain(
      'README.md',
    );
  });

  it('renders workspace file activity and opens changed files', () => {
    const onOpenFile = vi.fn();
    const onOpenDiff = vi.fn();
    render(
      <AgentThread
        jobId='job-1'
        messages={[]}
        events={[]}
        interactions={[]}
        workspaceChanges={[
          {
            _id: 'change-1',
            _creationTime: 1,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            path: 'apps/web/auth.ts',
            source: 'agent',
            changeType: 'modified',
            diff: 'diff --git a/apps/web/auth.ts b/apps/web/auth.ts\n+authentik',
            createdAt: 1,
          } as never,
        ]}
        disabled={false}
        agentTurnActive={false}
        onOpenFile={onOpenFile}
        onOpenDiff={onOpenDiff}
      />,
    );

    fireEvent.click(screen.getByRole('button', { name: 'Files' }));
    expect(screen.getByText('apps/web/auth.ts')).toBeInTheDocument();
    fireEvent.click(screen.getByRole('button', { name: 'View diff' }));
    expect(onOpenDiff).toHaveBeenCalledWith('apps/web/auth.ts');
    fireEvent.click(screen.getByRole('button', { name: 'Open' }));
    expect(onOpenFile).toHaveBeenCalledWith('apps/web/auth.ts');
  });

  it('keeps the workspace thread focused on user, agent, and tool content', () => {
    render(
      <AgentThread
        jobId='job-1'
        messages={[
          {
            _id: 'message-system',
            _creationTime: 1,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            role: 'system',
            content: 'Workspace is ready.',
            status: 'completed',
            createdAt: 1,
            updatedAt: 1,
          } as never,
          {
            _id: 'message-empty-assistant',
            _creationTime: 2,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            role: 'assistant',
            content: '',
            status: 'completed',
            createdAt: 2,
            updatedAt: 2,
          } as never,
          {
            _id: 'message-user',
            _creationTime: 3,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            role: 'user',
            content: 'Use Authentik as the only provider.',
            status: 'completed',
            createdAt: 3,
            updatedAt: 3,
          } as never,
          {
            _id: 'message-assistant',
            _creationTime: 4,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            role: 'assistant',
            content: 'I found the Auth.js provider configuration.',
            status: 'completed',
            createdAt: 4,
            updatedAt: 4,
          } as never,
          {
            _id: 'message-tool',
            _creationTime: 5,
            jobId: 'job-1',
            spoonId: 'spoon-1',
            ownerId: 'user-1',
            role: 'tool',
            content: 'rg Authentik',
            status: 'completed',
            createdAt: 5,
            updatedAt: 5,
          } as never,
        ]}
        events={[
          {
            _id: 'event-info',
            _creationTime: 1,
            jobId: 'job-1',
            level: 'info',
            phase: 'plan',
            message: 'Sending message to agent.',
            createdAt: 1,
          } as never,
        ]}
        interactions={[]}
        workspaceChanges={[]}
        disabled={false}
        agentTurnActive={false}
        onOpenFile={vi.fn()}
        onOpenDiff={vi.fn()}
      />,
    );

    expect(screen.queryByText('Workspace is ready.')).not.toBeInTheDocument();
    expect(
      screen.queryByText('Sending message to agent.'),
    ).not.toBeInTheDocument();
    expect(screen.queryByText('Assistant')).not.toBeInTheDocument();
    expect(
      screen.getByText('Use Authentik as the only provider.'),
    ).toBeInTheDocument();
    expect(
      screen.getByText('I found the Auth.js provider configuration.'),
    ).toBeInTheDocument();
    expect(screen.getByText('rg Authentik')).toBeInTheDocument();
  });

  it('renders thread workspaces on the canonical thread route', () => {
    mockUseParams.mockReturnValue({ threadId: 'thread-1' });
    mockUseQuery.mockReturnValue({
      thread: {
        _id: 'thread-1',
        title: 'Update auth',
        status: 'running',
        source: 'user_request',
        priority: 'normal',
        summary: 'Use Authentik',
        createdAt: 1,
        updatedAt: 1,
      },
      spoon: { _id: 'spoon-1', name: 'useSend' },
      latestJob: {
        _id: 'job-1',
        spoonId: 'spoon-1',
        status: 'running',
        workspaceStatus: 'active',
      },
    });
    mockUseMutation.mockReturnValue(vi.fn());

    render(<ThreadDetailPage />);

    expect(screen.getByText('workspace shell job-1')).toBeInTheDocument();
    expect(screen.queryByText('Thread state')).not.toBeInTheDocument();
  });
});
