/**
 * Logs Page - Static Log File Viewing E2E Tests
 *
 * Tests for log file listing, content display, filtering, pagination, and download.
 * Covers 18 test scenarios as defined in phase5-implementation.md.
 *
 * Test Categories:
 * - Page Layout (3 tests): heading, file list, empty state
 * - Log File List (4 tests): display files, file sizes, last modified, sorting
 * - Log Content Display (4 tests): select file, display content, line numbers, syntax highlighting
 * - Pagination (3 tests): page navigation, page size, page info
 * - Search/Filter (2 tests): text search, filter by level
 * - Download (2 tests): download file, download error
 *
 * Route: /tasks/logs
 * Component: Logs.tsx
 */

import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import {
  setupLogFiles,
  generateMockEntries,
  LogFile,
  CaddyAccessLog,
  LOG_SELECTORS,
} from '../utils/phase5-helpers';
import { waitForToast, waitForLoadingComplete, waitForAPIResponse } from '../utils/wait-helpers';

/**
 * Mock log files for testing
 */
const mockLogFiles: LogFile[] = [
  { name: 'access.log', size: 1048576, modified: '2024-01-15T12:00:00Z' },
  { name: 'error.log', size: 256000, modified: '2024-01-15T11:30:00Z' },
  { name: 'caddy.log', size: 512000, modified: '2024-01-14T10:00:00Z' },
];

/**
 * Mock log entries for content display testing
 */
const mockLogEntries: CaddyAccessLog[] = [
  {
    level: 'info',
    ts: Date.now() / 1000,
    logger: 'http.log.access',
    msg: 'handled request',
    request: {
      remote_ip: '192.168.1.100',
      method: 'GET',
      host: 'api.example.com',
      uri: '/api/v1/users',
      proto: 'HTTP/2',
    },
    status: 200,
    duration: 0.045,
    size: 1234,
  },
  {
    level: 'error',
    ts: Date.now() / 1000 - 60,
    logger: 'http.log.access',
    msg: 'connection refused',
    request: {
      remote_ip: '192.168.1.101',
      method: 'POST',
      host: 'api.example.com',
      uri: '/api/v1/auth/login',
      proto: 'HTTP/2',
    },
    status: 502,
    duration: 5.023,
    size: 0,
  },
  {
    level: 'warn',
    ts: Date.now() / 1000 - 120,
    logger: 'http.log.access',
    msg: 'rate limit exceeded',
    request: {
      remote_ip: '10.0.0.50',
      method: 'GET',
      host: 'web.example.com',
      uri: '/dashboard',
      proto: 'HTTP/1.1',
    },
    status: 429,
    duration: 0.001,
    size: 256,
  },
];

/**
 * Selectors for the Logs page
 */
const SELECTORS = {
  pageTitle: 'h1',
  logFileList: '[data-testid="log-file-list"]',
  logTable: '[data-testid="log-table"]',
  pageInfo: '[data-testid="page-info"]',
  searchInput: 'input[placeholder*="Search"]',
  hostFilter: 'input[placeholder*="Host"]',
  levelSelect: 'select',
  statusSelect: 'select',
  sortSelect: 'select',
  refreshButton: 'button:has-text("Refresh")',
  downloadButton: 'button:has-text("Download")',
  // Pagination buttons - scope to content area by looking for sibling showing text
  // The pagination buttons are next to "Showing x - y of z" text
  prevPageButton: '.flex.gap-2 button:has(.lucide-chevron-left), [data-testid="prev-page"], button[aria-label*="Previous"]',
  nextPageButton: '.flex.gap-2 button:has(.lucide-chevron-right), [data-testid="next-page"], button[aria-label*="Next"]',
  emptyState: '[class*="EmptyState"], [data-testid="empty-state"]',
  loadingSkeleton: '[class*="Skeleton"], [data-testid="skeleton"]',
};

/**
 * Helper to set up log files and content mocking
 */
async function setupLogFilesWithContent(
  page: import('@playwright/test').Page,
  files: LogFile[] = mockLogFiles,
  entries: CaddyAccessLog[] = mockLogEntries,
  total?: number
) {
  // Mock log files list
  await page.route('**/api/v1/logs', async (route) => {
    if (route.request().method() === 'GET') {
      await route.fulfill({ status: 200, json: files });
    } else {
      await route.continue();
    }
  });

  // Mock log content for each file
  for (const file of files) {
    await page.route(`**/api/v1/logs/${file.name}*`, async (route) => {
      const url = new URL(route.request().url());
      const offset = parseInt(url.searchParams.get('offset') || '0');
      const limit = parseInt(url.searchParams.get('limit') || '50');
      const search = url.searchParams.get('search') || '';
      const level = url.searchParams.get('level') || '';
      const host = url.searchParams.get('host') || '';

      // Apply filters
      let filteredEntries = [...entries];
      if (search) {
        filteredEntries = filteredEntries.filter(
          (e) =>
            e.msg.toLowerCase().includes(search.toLowerCase()) ||
            e.request.uri.toLowerCase().includes(search.toLowerCase())
        );
      }
      if (level) {
        filteredEntries = filteredEntries.filter(
          (e) => e.level.toLowerCase() === level.toLowerCase()
        );
      }
      if (host) {
        filteredEntries = filteredEntries.filter((e) =>
          e.request.host.toLowerCase().includes(host.toLowerCase())
        );
      }

      const paginatedEntries = filteredEntries.slice(offset, offset + limit);
      const totalCount = total || filteredEntries.length;

      await route.fulfill({
        status: 200,
        json: {
          filename: file.name,
          logs: paginatedEntries,
          total: totalCount,
          limit,
          offset,
        },
      });
    });
  }
}

test.describe('Logs Page - Static Log File Viewing', () => {
  // =========================================================================
  // Page Layout Tests (3 tests)
  // =========================================================================
  test.describe('Page Layout', () => {
    test('should display logs page with file selector', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Verify page title
      await expect(page.locator(SELECTORS.pageTitle)).toContainText(/logs/i);

      // Verify file list sidebar is visible
      await expect(page.locator(SELECTORS.logFileList)).toBeVisible();
    });

    test('should show list of available log files', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Verify all log files are displayed in the list
      await expect(page.getByText('access.log')).toBeVisible();
      await expect(page.getByText('error.log')).toBeVisible();
      await expect(page.getByText('caddy.log')).toBeVisible();
    });

    test('should display log filters section', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Wait for filters to be visible (they appear when a log file is selected)
      // The component auto-selects the first log file
      await expect(page.locator(SELECTORS.searchInput)).toBeVisible({ timeout: 5000 });

      // Verify filter controls are present
      await expect(page.locator(SELECTORS.refreshButton)).toBeVisible();
      await expect(page.locator(SELECTORS.downloadButton)).toBeVisible();
    });
  });

  // =========================================================================
  // Log File List Tests (4 tests)
  // =========================================================================
  test.describe('Log File List', () => {
    test('should list all available log files with metadata', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Verify files are listed with size information
      // The component displays size in MB format: (log.size / 1024 / 1024).toFixed(2) MB
      await expect(page.getByText('access.log')).toBeVisible();
      await expect(page.getByText('1.00 MB')).toBeVisible(); // 1048576 bytes = 1.00 MB

      await expect(page.getByText('error.log')).toBeVisible();
      await expect(page.getByText('0.24 MB')).toBeVisible(); // 256000 bytes ≈ 0.24 MB
    });

    test('should load log content when file selected', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Set up response listener BEFORE clicking
      const responsePromise = page.waitForResponse((resp) =>
        resp.url().includes('/api/v1/logs/error.log')
      );

      // Click on error.log to select it
      await page.click('button:has-text("error.log")');

      // Wait for content to load
      await responsePromise;

      // Verify log table is displayed with content
      await expect(page.locator(SELECTORS.logTable)).toBeVisible();
    });

    test('should show empty state for empty log files', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      // Mock empty log files list
      await page.route('**/api/v1/logs', async (route) => {
        if (route.request().method() === 'GET') {
          await route.fulfill({ status: 200, json: [] });
        } else {
          await route.continue();
        }
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Should show "No log files" message (use first() since there may be multiple matching texts)
      await expect(page.getByText(/no log files|select.*log/i).first()).toBeVisible();
    });

    test('should highlight selected log file', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // The first file (access.log) is auto-selected
      // Check for visual selection indicator (brand color class)
      const accessLogButton = page.locator('button:has-text("access.log")');
      await expect(accessLogButton).toHaveClass(/brand-500|bg-brand/);

      // Set up response listener BEFORE clicking
      const responsePromise = page.waitForResponse((resp) =>
        resp.url().includes('/api/v1/logs/error.log')
      );

      // Click on error.log
      await page.click('button:has-text("error.log")');
      await responsePromise;

      // Error.log should now have the selected style
      const errorLogButton = page.locator('button:has-text("error.log")');
      await expect(errorLogButton).toHaveClass(/brand-500|bg-brand/);
    });
  });

  // =========================================================================
  // Log Content Display Tests (4 tests)
  // =========================================================================
  test.describe('Log Content Display', () => {
    test('should display log entries in table format', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Wait for auto-selected log content to load
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Verify table structure
      const logTable = page.locator(SELECTORS.logTable);
      await expect(logTable).toBeVisible();

      // Verify table has expected columns
      await expect(page.getByRole('columnheader', { name: /time/i })).toBeVisible();
      await expect(page.getByRole('columnheader', { name: /status/i })).toBeVisible();
      await expect(page.getByRole('columnheader', { name: /method/i })).toBeVisible();
    });

    test('should show timestamp, level, method, uri, status', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Wait for content to load
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Verify log entry content is displayed (use .first() where multiple matches possible)
      await expect(page.getByText('192.168.1.100').first()).toBeVisible();
      await expect(page.getByText('GET').first()).toBeVisible();
      await expect(page.getByText('/api/v1/users').first()).toBeVisible();
      await expect(page.getByText('200').first()).toBeVisible();
    });

    test('should sort logs by timestamp', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      let capturedSort = '';
      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        capturedSort = url.searchParams.get('sort') || 'desc';
        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: mockLogEntries,
            total: mockLogEntries.length,
            limit: 50,
            offset: 0,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Default sort should be 'desc' (newest first)
      expect(capturedSort).toBe('desc');

      // Change sort order via the select
      const sortSelect = page.locator('select').filter({ hasText: /newest|oldest/i });
      if (await sortSelect.isVisible()) {
        await sortSelect.selectOption('asc');
        await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });
        expect(capturedSort).toBe('asc');
      }
    });

    test('should highlight error entries with distinct styling', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Find the 502 status entry (error) - use exact text match to avoid partial matches
      const errorStatus = page.getByText('502', { exact: true });
      await expect(errorStatus).toBeVisible();

      // Error status should have red/error styling class
      await expect(errorStatus).toHaveClass(/red|error/i);
    });
  });

  // =========================================================================
  // Pagination Tests (3 tests)
  // =========================================================================
  test.describe('Pagination', () => {
    test('should paginate large log files', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      // Generate 150 mock entries for pagination testing
      const largeEntrySet = generateMockEntries(150, 1);
      let capturedOffset = 0;

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        capturedOffset = parseInt(url.searchParams.get('offset') || '0');
        const limit = parseInt(url.searchParams.get('limit') || '50');

        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: largeEntrySet.slice(capturedOffset, capturedOffset + limit),
            total: 150,
            limit,
            offset: capturedOffset,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Initial state - page 1
      expect(capturedOffset).toBe(0);

      // Click next page button
      const nextButton = page.locator(SELECTORS.nextPageButton);
      await expect(nextButton).toBeEnabled();

      // Use Promise.all to avoid race condition - set up listener BEFORE clicking
      await Promise.all([
        page.waitForResponse(
          (resp) => resp.url().includes('/api/v1/logs/access.log') && resp.status() === 200
        ),
        nextButton.click(),
      ]);

      // Should have requested offset 50 (second page)
      expect(capturedOffset).toBe(50);
    });

    test('should display page info correctly', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      const largeEntrySet = generateMockEntries(150, 1);

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        const offset = parseInt(url.searchParams.get('offset') || '0');
        const limit = parseInt(url.searchParams.get('limit') || '50');

        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: largeEntrySet.slice(offset, offset + limit),
            total: 150,
            limit,
            offset,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Verify page info displays correctly
      const pageInfo = page.locator(SELECTORS.pageInfo);
      await expect(pageInfo).toBeVisible();

      // Should show "Showing 1 - 50 of 150" or similar
      await expect(pageInfo).toContainText(/1.*50.*150/);
    });

    test('should disable prev button on first page and next on last', async ({
      page,
      authenticatedUser,
    }) => {
      await loginUser(page, authenticatedUser);

      const entries = generateMockEntries(75, 1); // 2 pages (50 + 25)

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        const offset = parseInt(url.searchParams.get('offset') || '0');
        const limit = parseInt(url.searchParams.get('limit') || '50');

        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: entries.slice(offset, offset + limit),
            total: 75,
            limit,
            offset,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      const prevButton = page.locator(SELECTORS.prevPageButton);
      const nextButton = page.locator(SELECTORS.nextPageButton);

      // On first page, prev should be disabled
      await expect(prevButton).toBeDisabled();
      await expect(nextButton).toBeEnabled();

      // Set up response listener BEFORE clicking
      const nextPageResponse = page.waitForResponse((resp) =>
        resp.url().includes('/api/v1/logs/access.log')
      );

      // Navigate to last page
      await nextButton.click();
      await nextPageResponse;

      // On last page, next should be disabled
      await expect(prevButton).toBeEnabled();
      await expect(nextButton).toBeDisabled();
    });
  });

  // =========================================================================
  // Search/Filter Tests (2 tests)
  // =========================================================================
  test.describe('Search and Filter', () => {
    test('should filter logs by search text', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      let capturedSearch = '';

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        capturedSearch = url.searchParams.get('search') || '';

        // Filter mock entries based on search
        const filtered = capturedSearch
          ? mockLogEntries.filter(
              (e) =>
                e.msg.toLowerCase().includes(capturedSearch.toLowerCase()) ||
                e.request.uri.toLowerCase().includes(capturedSearch.toLowerCase())
            )
          : mockLogEntries;

        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: filtered,
            total: filtered.length,
            limit: 50,
            offset: 0,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Type in search input
      const searchInput = page.locator(SELECTORS.searchInput);

      // Set up response listener BEFORE typing to catch the debounced request
      const searchResponsePromise = page.waitForResponse((resp) =>
        resp.url().includes('/api/v1/logs/access.log')
      );

      await searchInput.fill('users');

      // Wait for debounced search request
      await searchResponsePromise;

      // Verify search parameter was sent
      expect(capturedSearch).toBe('users');
    });

    test('should filter logs by log level', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      let capturedLevel = '';

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        const url = new URL(route.request().url());
        capturedLevel = url.searchParams.get('level') || '';

        // Filter mock entries based on level
        const filtered = capturedLevel
          ? mockLogEntries.filter(
              (e) => e.level.toLowerCase() === capturedLevel.toLowerCase()
            )
          : mockLogEntries;

        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: filtered,
            total: filtered.length,
            limit: 50,
            offset: 0,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Select Error level from dropdown
      const levelSelect = page.locator('select').filter({ hasText: /all levels/i });
      if (await levelSelect.isVisible()) {
        await levelSelect.selectOption('ERROR');
        await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

        // Verify level parameter was sent
        expect(capturedLevel.toLowerCase()).toBe('error');
      }
    });
  });

  // =========================================================================
  // Download Tests (2 tests)
  // =========================================================================
  test.describe('Download', () => {
    test('should download log file successfully', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);
      await setupLogFilesWithContent(page);

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Verify download button is visible and enabled
      const downloadButton = page.locator(SELECTORS.downloadButton);
      await expect(downloadButton).toBeVisible();
      await expect(downloadButton).toBeEnabled();

      // The component uses window.location.href for downloads
      // We verify the button is properly rendered and clickable
      // In a real test, we'd track the download event, but that requires
      // the download endpoint to be properly mocked with Content-Disposition
    });

    test('should handle download error gracefully', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        if (!route.request().url().includes('/download')) {
          await route.fulfill({
            status: 200,
            json: {
              filename: 'access.log',
              logs: mockLogEntries,
              total: mockLogEntries.length,
              limit: 50,
              offset: 0,
            },
          });
        } else {
          await route.continue();
        }
      });

      // Mock download endpoint to fail
      await page.route('**/api/v1/logs/access.log/download', async (route) => {
        await route.fulfill({
          status: 404,
          json: { error: 'Log file not found' },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Verify download button is present
      const downloadButton = page.locator(SELECTORS.downloadButton);
      await expect(downloadButton).toBeVisible();

      // Note: The current implementation uses window.location.href for downloads,
      // which navigates the browser directly. Error handling would require
      // using fetch() with blob download pattern instead.
      // This test verifies the UI is in a valid state before download.
    });
  });

  // =========================================================================
  // Additional Edge Cases
  // =========================================================================
  test.describe('Edge Cases', () => {
    test('should handle empty log content gracefully', async ({ page, authenticatedUser }) => {
      await loginUser(page, authenticatedUser);

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/access.log*', async (route) => {
        await route.fulfill({
          status: 200,
          json: {
            filename: 'access.log',
            logs: [],
            total: 0,
            limit: 50,
            offset: 0,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);
      await waitForAPIResponse(page, '/api/v1/logs/access.log', { status: 200 });

      // Should show "No logs found" or similar message
      await expect(page.getByText(/no logs found|no.*matching/i)).toBeVisible();
    });

    test('should reset to first page when changing log file', async ({
      page,
      authenticatedUser,
    }) => {
      await loginUser(page, authenticatedUser);

      const largeEntrySet = generateMockEntries(150, 1);
      let lastOffset = 0;

      await page.route('**/api/v1/logs', async (route) => {
        await route.fulfill({ status: 200, json: mockLogFiles });
      });

      await page.route('**/api/v1/logs/*', async (route) => {
        const url = new URL(route.request().url());
        lastOffset = parseInt(url.searchParams.get('offset') || '0');
        const limit = parseInt(url.searchParams.get('limit') || '50');

        await route.fulfill({
          status: 200,
          json: {
            filename: 'test.log',
            logs: largeEntrySet.slice(lastOffset, lastOffset + limit),
            total: 150,
            limit,
            offset: lastOffset,
          },
        });
      });

      await page.goto('/tasks/logs');
      await waitForLoadingComplete(page);

      // Navigate to page 2
      const nextButton = page.locator(SELECTORS.nextPageButton);
      await nextButton.click();
      await page.waitForTimeout(500);

      expect(lastOffset).toBe(50);

      // Switch to different log file
      await page.click('button:has-text("error.log")');
      await page.waitForTimeout(500);

      // Should reset to offset 0
      expect(lastOffset).toBe(0);
    });
  });
});
