/**
 * Tests for useIssues hooks
 *
 * Comprehensive tests for issue management React Query hooks:
 * - filterAndSortIssues function (unit tests)
 * - useIssues hook (pagination, filtering)
 * - useIssue hook (single issue fetch)
 * - useUpdateIssue hook (mutations)
 * - useUpdateIssueStatus hook (optimistic updates)
 * - useBulkIssueAction hook (bulk operations)
 * - useSyncIssue hook (external sync)
 */

import { renderHook, waitFor, act } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
  useIssues,
  useIssue,
  useUpdateIssue,
  useUpdateIssueStatus,
  useBulkIssueAction,
  useSyncIssue,
  issueKeys,
} from '@/features/issues/hooks/useIssues';
import { mockIssues, mockIssueDetail } from '@/features/issues/mocks';
import type { IssueFilters, IssueSort } from '@/features/issues/types';

// Import the filterAndSortIssues function for direct testing
// Since it's not exported, we'll test it indirectly through the hook
// But we can also create a test-only export or test via behavior

/**
 * Create a wrapper with a fresh QueryClient for each test
 */
function createWrapper() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        // Reduce stale time for tests
        staleTime: 0,
      },
      mutations: {
        retry: false,
      },
    },
  });

  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
}

/**
 * Create wrapper that exposes queryClient for cache manipulation
 */
function createWrapperWithClient() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false, staleTime: 0 },
      mutations: { retry: false },
    },
  });

  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );

  return { wrapper, queryClient };
}

describe('issueKeys', () => {
  it('generates correct all key', () => {
    expect(issueKeys.all).toEqual(['issues']);
  });

  it('generates correct lists key', () => {
    expect(issueKeys.lists()).toEqual(['issues', 'list']);
  });

  it('generates correct list key with project and filters', () => {
    const filters: IssueFilters = { status: 'open' };
    const sort: IssueSort = { field: 'priority', direction: 'desc' };
    expect(issueKeys.list('proj-1', filters, sort)).toEqual([
      'issues',
      'list',
      'proj-1',
      filters,
      sort,
    ]);
  });

  it('generates correct details key', () => {
    expect(issueKeys.details()).toEqual(['issues', 'detail']);
  });

  it('generates correct detail key with issueId', () => {
    expect(issueKeys.detail('issue-123')).toEqual(['issues', 'detail', 'issue-123']);
  });
});

describe('useIssues', () => {
  // Speed up tests by reducing timeout delays
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('fetches paginated issues for a project', async () => {
    const { result } = renderHook(() => useIssues('test-project'), {
      wrapper: createWrapper(),
    });

    expect(result.current.isLoading).toBe(true);

    // Fast-forward timer for mock delay
    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data).toBeDefined();
    expect(result.current.data?.data).toBeInstanceOf(Array);
    expect(result.current.data?.pagination).toBeDefined();
    expect(result.current.data?.pagination.page).toBe(1);
    expect(result.current.data?.pagination.page_size).toBe(25);
  });

  it('returns correct pagination metadata', async () => {
    const { result } = renderHook(() => useIssues('test-project', undefined, undefined, 1, 10), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    const pagination = result.current.data?.pagination;
    expect(pagination).toBeDefined();
    expect(pagination?.page).toBe(1);
    expect(pagination?.page_size).toBe(10);
    expect(pagination?.total).toBe(mockIssues.length);
    expect(pagination?.total_pages).toBe(Math.ceil(mockIssues.length / 10));
  });

  describe('filtering', () => {
    it('filters by search text in title', async () => {
      const filters: IssueFilters = { search: 'authentication' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      // Should find the authentication issue
      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.title.toLowerCase().includes('authentication'))).toBe(true);
    });

    it('filters by search text in description', async () => {
      const filters: IssueFilters = { search: 'reusable' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.some((i) => i.description.toLowerCase().includes('reusable'))).toBe(true);
    });

    it('filters by search text matching issue number', async () => {
      const filters: IssueFilters = { search: '42' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.some((i) => i.number === 42)).toBe(true);
    });

    it('filters by status', async () => {
      const filters: IssueFilters = { status: 'open' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.every((i) => i.status === 'open')).toBe(true);
    });

    it('filters by status "all" returns all issues', async () => {
      const filters: IssueFilters = { status: 'all' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.total).toBe(mockIssues.length);
    });

    it('filters by priority', async () => {
      const filters: IssueFilters = { priority: 'high' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.priority === 'high')).toBe(true);
    });

    it('filters by priority "all" returns all issues', async () => {
      const filters: IssueFilters = { priority: 'all' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.total).toBe(mockIssues.length);
    });

    it('filters by sprint', async () => {
      const filters: IssueFilters = { sprint: 'Sprint 3' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.sprint === 'Sprint 3')).toBe(true);
    });

    it('filters by "backlog" returns issues without sprint', async () => {
      const filters: IssueFilters = { sprint: 'backlog' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.sprint === null)).toBe(true);
    });

    it('filters by sprint "all" returns all issues', async () => {
      const filters: IssueFilters = { sprint: 'all' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.total).toBe(mockIssues.length);
    });

    it('filters by assignee', async () => {
      const filters: IssueFilters = { assignee: 'agent-be' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.assignee?.id === 'agent-be')).toBe(true);
    });

    it('filters by "unassigned" returns issues without assignee', async () => {
      const filters: IssueFilters = { assignee: 'unassigned' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      expect(issues.length).toBeGreaterThan(0);
      expect(issues.every((i) => i.assignee === null)).toBe(true);
    });

    it('filters by assignee "all" returns all issues', async () => {
      const filters: IssueFilters = { assignee: 'all' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.total).toBe(mockIssues.length);
    });

    it('combines multiple filters', async () => {
      const filters: IssueFilters = {
        status: 'in_progress',
        priority: 'high',
        sprint: 'Sprint 3',
      };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      issues.forEach((issue) => {
        expect(issue.status).toBe('in_progress');
        expect(issue.priority).toBe('high');
        expect(issue.sprint).toBe('Sprint 3');
      });
    });

    it('returns empty array when no issues match filters', async () => {
      const filters: IssueFilters = { search: 'xyznonexistent123' };
      const { result } = renderHook(() => useIssues('test-project', filters), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.data).toEqual([]);
      expect(result.current.data?.pagination.total).toBe(0);
    });
  });

  describe('sorting', () => {
    it('sorts by number ascending', async () => {
      const sort: IssueSort = { field: 'number', direction: 'asc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(issues[i].number).toBeGreaterThanOrEqual(issues[i - 1].number);
      }
    });

    it('sorts by number descending', async () => {
      const sort: IssueSort = { field: 'number', direction: 'desc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(issues[i].number).toBeLessThanOrEqual(issues[i - 1].number);
      }
    });

    it('sorts by priority ascending', async () => {
      const priorityOrder: Record<string, number> = { low: 1, medium: 2, high: 3, critical: 4 };
      const sort: IssueSort = { field: 'priority', direction: 'asc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(priorityOrder[issues[i].priority]).toBeGreaterThanOrEqual(
          priorityOrder[issues[i - 1].priority]
        );
      }
    });

    it('sorts by priority descending', async () => {
      const priorityOrder: Record<string, number> = { low: 1, medium: 2, high: 3, critical: 4 };
      const sort: IssueSort = { field: 'priority', direction: 'desc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(priorityOrder[issues[i].priority]).toBeLessThanOrEqual(
          priorityOrder[issues[i - 1].priority]
        );
      }
    });

    it('sorts by updated_at ascending', async () => {
      const sort: IssueSort = { field: 'updated_at', direction: 'asc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(new Date(issues[i].updated_at).getTime()).toBeGreaterThanOrEqual(
          new Date(issues[i - 1].updated_at).getTime()
        );
      }
    });

    it('sorts by created_at descending', async () => {
      const sort: IssueSort = { field: 'created_at', direction: 'desc' };
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      const issues = result.current.data?.data || [];
      for (let i = 1; i < issues.length; i++) {
        expect(new Date(issues[i].created_at).getTime()).toBeLessThanOrEqual(
          new Date(issues[i - 1].created_at).getTime()
        );
      }
    });

    it('handles unknown sort field gracefully', async () => {
      // Cast to unknown first to bypass type checking for edge case test
      const sort = { field: 'unknown_field', direction: 'asc' } as unknown as IssueSort;
      const { result } = renderHook(() => useIssues('test-project', undefined, sort), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      // Should still return data without errors
      expect(result.current.data?.data).toBeDefined();
    });
  });

  describe('pagination', () => {
    it('paginates correctly for page 1', async () => {
      const { result } = renderHook(() => useIssues('test-project', undefined, undefined, 1, 3), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.data.length).toBeLessThanOrEqual(3);
      expect(result.current.data?.pagination.page).toBe(1);
    });

    it('paginates correctly for page 2', async () => {
      const { result } = renderHook(() => useIssues('test-project', undefined, undefined, 2, 3), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.page).toBe(2);
      expect(result.current.data?.pagination.has_prev).toBe(true);
    });

    it('calculates has_next correctly', async () => {
      const { result } = renderHook(() => useIssues('test-project', undefined, undefined, 1, 3), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      // With 8 mock issues and page size 3, page 1 should have next
      if (mockIssues.length > 3) {
        expect(result.current.data?.pagination.has_next).toBe(true);
      }
    });

    it('calculates has_prev correctly', async () => {
      const { result } = renderHook(() => useIssues('test-project', undefined, undefined, 1, 10), {
        wrapper: createWrapper(),
      });

      await act(async () => {
        jest.advanceTimersByTime(350);
      });

      await waitFor(() => {
        expect(result.current.isSuccess).toBe(true);
      });

      expect(result.current.data?.pagination.has_prev).toBe(false);
    });
  });
});

describe('useIssue', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('fetches a single issue by ID', async () => {
    const { result } = renderHook(() => useIssue('ISS-001'), {
      wrapper: createWrapper(),
    });

    expect(result.current.isLoading).toBe(true);

    await act(async () => {
      jest.advanceTimersByTime(250);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data).toBeDefined();
    expect(result.current.data?.id).toBe('ISS-001');
  });

  it('returns mock issue detail with correct structure', async () => {
    const { result } = renderHook(() => useIssue('test-issue'), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      jest.advanceTimersByTime(250);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    const issue = result.current.data;
    expect(issue).toMatchObject({
      id: 'test-issue',
      title: expect.any(String),
      description: expect.any(String),
      status: expect.any(String),
      priority: expect.any(String),
      labels: expect.any(Array),
      activity: expect.any(Array),
    });
  });

  it('is disabled when issueId is empty', async () => {
    const { result } = renderHook(() => useIssue(''), {
      wrapper: createWrapper(),
    });

    // Query should not be loading because it's disabled
    expect(result.current.isLoading).toBe(false);
    expect(result.current.isFetching).toBe(false);
    expect(result.current.data).toBeUndefined();
  });
});

describe('useUpdateIssue', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('updates an issue successfully', async () => {
    const { result } = renderHook(() => useUpdateIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        data: { title: 'Updated Title' },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.title).toBe('Updated Title');
    expect(result.current.data?.id).toBe('ISS-001');
  });

  it('updates issue status', async () => {
    const { result } = renderHook(() => useUpdateIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        data: { status: 'closed' },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.status).toBe('closed');
  });

  it('updates issue priority', async () => {
    const { result } = renderHook(() => useUpdateIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        data: { priority: 'critical' },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.priority).toBe('critical');
  });

  it('clears sprint when set to null', async () => {
    const { result } = renderHook(() => useUpdateIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        data: { sprint: null },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.sprint).toBeNull();
  });

  it('clears due_date when set to null', async () => {
    const { result } = renderHook(() => useUpdateIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        data: { due_date: null },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.due_date).toBeNull();
  });
});

describe('useUpdateIssueStatus', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('performs optimistic update on status change', async () => {
    const { wrapper, queryClient } = createWrapperWithClient();

    // Pre-populate the cache with issue data
    queryClient.setQueryData(issueKeys.detail('ISS-001'), {
      ...mockIssueDetail,
      id: 'ISS-001',
      status: 'open',
    });

    const { result } = renderHook(() => useUpdateIssueStatus(), { wrapper });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        status: 'in_progress',
      });
    });

    // Check optimistic update was applied
    const cachedIssue = queryClient.getQueryData(issueKeys.detail('ISS-001')) as any;
    expect(cachedIssue?.status).toBe('in_progress');

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });
  });

  it('sets up rollback context with previous value in onMutate', async () => {
    const { wrapper, queryClient } = createWrapperWithClient();

    // Pre-populate the cache with original state
    const originalIssue = {
      ...mockIssueDetail,
      id: 'ISS-001',
      status: 'open',
    };
    queryClient.setQueryData(issueKeys.detail('ISS-001'), originalIssue);

    const { result } = renderHook(() => useUpdateIssueStatus(), { wrapper });

    // Start the mutation and wait for onMutate to process
    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        status: 'closed',
      });
    });

    // After onMutate completes, optimistic update should be applied
    let cachedIssue = queryClient.getQueryData(issueKeys.detail('ISS-001')) as any;
    expect(cachedIssue?.status).toBe('closed'); // Optimistic update applied

    // Get the current mutation from cache to verify rollback context was set up
    const mutationCache = queryClient.getMutationCache();
    const activeMutation = mutationCache.getAll().find((m) => m.state.status === 'pending');

    // Verify onMutate set up the rollback context with previous value
    const mutationContext = activeMutation?.state.context as { previousIssue?: any } | undefined;
    expect(mutationContext?.previousIssue).toBeDefined();
    expect(mutationContext?.previousIssue.status).toBe('open');

    // Simulate what onError would do by restoring from context
    if (mutationContext?.previousIssue) {
      queryClient.setQueryData(issueKeys.detail('ISS-001'), mutationContext.previousIssue);
    }

    // Verify rollback mechanism works
    cachedIssue = queryClient.getQueryData(issueKeys.detail('ISS-001')) as any;
    expect(cachedIssue?.status).toBe('open'); // Rolled back to original

    // Clean up - let mutation complete
    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });
  });

  it('handles undefined status gracefully', async () => {
    const { wrapper, queryClient } = createWrapperWithClient();

    queryClient.setQueryData(issueKeys.detail('ISS-001'), {
      ...mockIssueDetail,
      id: 'ISS-001',
      status: 'open',
    });

    const { result } = renderHook(() => useUpdateIssueStatus(), { wrapper });

    await act(async () => {
      result.current.mutate({
        issueId: 'ISS-001',
        status: undefined,
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });
  });

  it('skips optimistic update when no previous data exists', async () => {
    const { wrapper } = createWrapperWithClient();

    // Don't pre-populate cache - no previous issue data

    const { result } = renderHook(() => useUpdateIssueStatus(), { wrapper });

    await act(async () => {
      result.current.mutate({
        issueId: 'nonexistent-issue',
        status: 'in_progress',
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(350);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });
  });
});

describe('useBulkIssueAction', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('performs bulk action successfully', async () => {
    const { result } = renderHook(() => useBulkIssueAction(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        action: 'change_status',
        issue_ids: ['ISS-001', 'ISS-002', 'ISS-003'],
        payload: { status: 'closed' },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(550);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.affected_count).toBe(3);
  });

  it('handles empty issue_ids array', async () => {
    const { result } = renderHook(() => useBulkIssueAction(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        action: 'assign',
        issue_ids: [],
        payload: { assignee_id: 'agent-1' },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(550);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.affected_count).toBe(0);
  });

  it('handles add_labels action', async () => {
    const { result } = renderHook(() => useBulkIssueAction(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        action: 'add_labels',
        issue_ids: ['ISS-001', 'ISS-002'],
        payload: { labels: ['urgent', 'bug'] },
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(550);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.affected_count).toBe(2);
  });

  it('handles delete action', async () => {
    const { result } = renderHook(() => useBulkIssueAction(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({
        action: 'delete',
        issue_ids: ['ISS-005'],
      });
    });

    await act(async () => {
      jest.advanceTimersByTime(550);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.affected_count).toBe(1);
  });
});

describe('useSyncIssue', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('syncs an issue with external tracker', async () => {
    const { result } = renderHook(() => useSyncIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({ issueId: 'ISS-001' });
    });

    await act(async () => {
      jest.advanceTimersByTime(1050);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data?.sync_status).toBe('synced');
  });

  it('returns the matching mock issue when found', async () => {
    const { result } = renderHook(() => useSyncIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({ issueId: 'ISS-001' });
    });

    await act(async () => {
      jest.advanceTimersByTime(1050);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    // Should return the issue with id ISS-001
    expect(result.current.data?.id).toBe('ISS-001');
  });

  it('returns first mock issue when issue not found', async () => {
    const { result } = renderHook(() => useSyncIssue(), {
      wrapper: createWrapper(),
    });

    await act(async () => {
      result.current.mutate({ issueId: 'nonexistent-issue' });
    });

    await act(async () => {
      jest.advanceTimersByTime(1050);
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    // Should return first mock issue as fallback
    expect(result.current.data?.sync_status).toBe('synced');
  });
});
