#!/usr/bin/env node
/**
 * Document Library UI Tests
 *
 * Tests for the library page including filters, views, PDF/text viewers,
 * and document management.
 *
 * Run: node test_library_documents_ci.js
 */

const { setupTest, teardownTest, TestResults, log, delay } = require('./test_lib');

// ============================================================================
// Library Page Structure Tests
// ============================================================================
const LibraryPageTests = {
    async libraryPageLoads(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const hasContent = document.body.textContent.length > 100;
            const title = document.title.toLowerCase();
            const hasLibraryContent = title.includes('library') || title.includes('document') ||
                                     !!document.querySelector('.library, #library, [class*="library"]');

            return {
                hasContent,
                hasLibraryContent,
                title,
                url: window.location.href
            };
        });

        return {
            passed: result.hasContent,
            message: `Library page loads (title: "${result.title}", has content: ${result.hasContent})`
        };
    },

    async libraryHeaderStats(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            // Look for statistics in header
            const statsArea = document.querySelector('.stats, .library-stats, .header-stats, [class*="statistics"]');
            const pdfCount = document.querySelector('[class*="pdf-count"], [class*="total-docs"], .doc-count');
            const storageSize = document.querySelector('[class*="storage"], [class*="size"]');

            // Look for any numbers that might be stats
            const pageText = document.body.textContent;
            const hasNumbers = /\d+\s*(PDFs?|documents?|MB|GB|files?)/i.test(pageText);

            return {
                hasStatsArea: !!statsArea,
                hasPdfCount: !!pdfCount,
                hasStorageSize: !!storageSize,
                hasNumbers,
                pdfCountText: pdfCount?.textContent?.trim(),
                storageSizeText: storageSize?.textContent?.trim()
            };
        });

        const hasStats = result.hasStatsArea || result.hasPdfCount || result.hasNumbers;

        return {
            passed: hasStats,
            message: `Library stats: area=${result.hasStatsArea}, count="${result.pdfCountText}", size="${result.storageSizeText}"`
        };
    },

    async storageModeDisplay(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const pageText = document.body.textContent.toLowerCase();

            // Look for storage mode indicators
            const hasEncrypted = pageText.includes('encrypted') || pageText.includes('database');
            const hasFilesystem = pageText.includes('filesystem') || pageText.includes('unencrypted');
            const hasTextOnly = pageText.includes('text only');

            // Look for storage mode badge/indicator
            const modeBadge = document.querySelector(
                '.storage-mode, ' +
                '[class*="storage-badge"], ' +
                '.mode-indicator, ' +
                '.badge'
            );

            return {
                hasEncrypted,
                hasFilesystem,
                hasTextOnly,
                hasModeBadge: !!modeBadge,
                badgeText: modeBadge?.textContent?.trim()
            };
        });

        const hasModeInfo = result.hasEncrypted || result.hasFilesystem || result.hasTextOnly || result.hasModeBadge;

        if (!hasModeInfo) {
            return { passed: null, skipped: true, message: 'No storage mode display found' };
        }

        return {
            passed: true,
            message: `Storage mode: encrypted=${result.hasEncrypted}, filesystem=${result.hasFilesystem}, badge="${result.badgeText}"`
        };
    }
};

// ============================================================================
// Filter Controls Tests
// ============================================================================
const FilterControlsTests = {
    async filterControlsExist(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });
        await delay(500);

        const result = await page.evaluate(() => {
            // Broader search for search inputs
            const searchInput = document.querySelector(
                'input[type="search"], ' +
                'input[type="text"][placeholder*="search" i], ' +
                'input[type="text"][placeholder*="filter" i], ' +
                '#search, ' +
                '.search-input, ' +
                '[class*="search-input"], ' +
                '[class*="filter-input"]'
            );

            // Look for any filter controls
            const collectionFilter = document.querySelector(
                'select[name*="collection"], ' +
                '#collection-filter, ' +
                '[class*="collection-filter"], ' +
                '[class*="collection-select"]'
            );

            const domainFilter = document.querySelector(
                'select[name*="domain"], ' +
                '#domain-filter, ' +
                '[class*="domain-filter"]'
            );

            const dateFilter = document.querySelector(
                'select[name*="date"], ' +
                '#date-filter, ' +
                '.date-filter, ' +
                '[class*="date-range"], ' +
                '[class*="date-filter"]'
            );

            // Also check if library page has any content at all
            const hasLibraryContent = document.querySelector('.library, #library, [class*="library"], .documents, [class*="document"]');
            const pageTitle = document.title?.toLowerCase() || '';
            const isLibraryPage = pageTitle.includes('library') || !!hasLibraryContent;

            return {
                hasSearch: !!searchInput,
                hasCollectionFilter: !!collectionFilter,
                hasDomainFilter: !!domainFilter,
                hasDateFilter: !!dateFilter,
                searchPlaceholder: searchInput?.placeholder,
                isLibraryPage,
                hasAnyContent: !!hasLibraryContent
            };
        });

        const hasFilters = result.hasSearch || result.hasCollectionFilter ||
                          result.hasDomainFilter || result.hasDateFilter;

        // If no filters but we're on library page, skip instead of fail
        if (!hasFilters) {
            if (result.isLibraryPage || result.hasAnyContent) {
                return { passed: null, skipped: true, message: 'Library page found but filter controls not visible (may be dynamic or require documents)' };
            }
            return { passed: null, skipped: true, message: 'Library page or filter controls not found' };
        }

        return {
            passed: true,
            message: `Filters: search=${result.hasSearch} ("${result.searchPlaceholder}"), collection=${result.hasCollectionFilter}, domain=${result.hasDomainFilter}, date=${result.hasDateFilter}`
        };
    },

    async searchFilterFunctionality(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const searchInput = await page.$('input[type="search"], input[placeholder*="search"], #search, .search-input');

        if (!searchInput) {
            return { passed: null, skipped: true, message: 'No search input found' };
        }

        // Count initial items
        const initialCount = await page.evaluate(() => {
            const items = document.querySelectorAll('.document-card, .doc-item, .library-item, [class*="document"]');
            return items.length;
        });

        // Type search query
        await searchInput.type('test search query');
        await delay(500);

        const afterSearchCount = await page.evaluate(() => {
            const items = document.querySelectorAll('.document-card, .doc-item, .library-item, [class*="document"]');
            return items.length;
        });

        return {
            passed: true,
            message: `Search input works (initial: ${initialCount} items, after search: ${afterSearchCount} items)`
        };
    },

    async dateFilterButtons(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, [class*="filter-btn"]'));

            const dateButtons = {
                today: buttons.find(b => b.textContent?.toLowerCase().includes('today')),
                week: buttons.find(b => b.textContent?.toLowerCase().includes('week')),
                month: buttons.find(b => b.textContent?.toLowerCase().includes('month')),
                all: buttons.find(b => b.textContent?.toLowerCase().includes('all'))
            };

            return {
                hasToday: !!dateButtons.today,
                hasWeek: !!dateButtons.week,
                hasMonth: !!dateButtons.month,
                hasAll: !!dateButtons.all,
                foundButtons: Object.entries(dateButtons).filter(([k, v]) => v).map(([k]) => k)
            };
        });

        const hasDateButtons = result.foundButtons.length > 0;

        if (!hasDateButtons) {
            return { passed: null, skipped: true, message: 'No date filter buttons found' };
        }

        return {
            passed: true,
            message: `Date filter buttons: ${result.foundButtons.join(', ')}`
        };
    }
};

// ============================================================================
// View Toggle Tests
// ============================================================================
const ViewToggleTests = {
    async viewToggleExists(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const gridBtn = document.querySelector(
                '[data-view="grid"], ' +
                '.grid-view, ' +
                '#grid-view, ' +
                'button[title*="grid"], ' +
                '[class*="grid-toggle"]'
            );

            const listBtn = document.querySelector(
                '[data-view="list"], ' +
                '.list-view, ' +
                '#list-view, ' +
                'button[title*="list"], ' +
                '[class*="list-toggle"]'
            );

            const viewToggle = document.querySelector(
                '.view-toggle, ' +
                '[class*="view-switch"], ' +
                '.view-options'
            );

            return {
                hasGridBtn: !!gridBtn,
                hasListBtn: !!listBtn,
                hasViewToggle: !!viewToggle,
                gridIcon: gridBtn?.innerHTML?.includes('grid') || gridBtn?.className?.includes('grid'),
                listIcon: listBtn?.innerHTML?.includes('list') || listBtn?.className?.includes('list')
            };
        });

        const hasToggle = result.hasGridBtn || result.hasListBtn || result.hasViewToggle;

        if (!hasToggle) {
            return { passed: null, skipped: true, message: 'No view toggle found' };
        }

        return {
            passed: true,
            message: `View toggle: grid=${result.hasGridBtn}, list=${result.hasListBtn}`
        };
    },

    async viewToggleFunctionality(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const gridBtn = document.querySelector('[data-view="grid"], .grid-view, #grid-view');
            const listBtn = document.querySelector('[data-view="list"], .list-view, #list-view');

            // Get initial view state
            const container = document.querySelector('.library-content, .documents, .doc-list, main');
            const initialClass = container?.className;

            // Click list view if grid exists (or vice versa)
            if (listBtn) {
                listBtn.click();
            } else if (gridBtn) {
                gridBtn.click();
            }

            const afterClass = container?.className;

            return {
                hasButtons: !!(gridBtn || listBtn),
                classChanged: initialClass !== afterClass,
                initialClass,
                afterClass
            };
        });

        if (!result.hasButtons) {
            return { passed: null, skipped: true, message: 'No view toggle buttons to test' };
        }

        return {
            passed: result.hasButtons,
            message: `View toggle: class changed=${result.classChanged}`
        };
    }
};

// ============================================================================
// Document Card Tests
// ============================================================================
const DocumentCardTests = {
    async documentCardsDisplay(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const cards = document.querySelectorAll(
                '.document-card, ' +
                '.doc-card, ' +
                '.library-item, ' +
                '[class*="document-item"], ' +
                '.card'
            );

            if (cards.length === 0) {
                // Check for empty state
                const emptyState = document.querySelector('.empty-state, .no-documents, [class*="empty"]');
                return {
                    hasCards: false,
                    cardCount: 0,
                    hasEmptyState: !!emptyState,
                    emptyText: emptyState?.textContent?.trim()?.substring(0, 50)
                };
            }

            const firstCard = cards[0];
            const hasTitle = !!firstCard.querySelector('.title, .doc-title, h3, h4, [class*="title"]');
            const hasAuthor = !!firstCard.querySelector('.author, [class*="author"]');
            const hasDate = !!firstCard.querySelector('.date, time, [class*="date"]');
            const hasDomain = !!firstCard.querySelector('.domain, .badge, [class*="domain"]');

            return {
                hasCards: true,
                cardCount: cards.length,
                hasTitle,
                hasAuthor,
                hasDate,
                hasDomain
            };
        });

        if (!result.hasCards && result.hasEmptyState) {
            return {
                passed: true,
                message: `Empty state shown: "${result.emptyText}"`
            };
        }

        return {
            passed: result.hasCards,
            message: result.hasCards
                ? `${result.cardCount} document cards (title=${result.hasTitle}, author=${result.hasAuthor}, date=${result.hasDate})`
                : 'No document cards found'
        };
    },

    async documentCardActions(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const card = document.querySelector('.document-card, .doc-card, .library-item, .card');
            if (!card) return { hasCard: false };

            const viewPdfBtn = card.querySelector('[class*="view-pdf"], button[title*="PDF"], .pdf-btn');
            const viewTextBtn = card.querySelector('[class*="view-text"], button[title*="text"], .text-btn');
            const deleteBtn = card.querySelector('[class*="delete"], .delete-btn, button[title*="delete"]');
            const downloadBtn = card.querySelector('[class*="download"], .download-btn, a[download]');

            return {
                hasCard: true,
                hasViewPdf: !!viewPdfBtn,
                hasViewText: !!viewTextBtn,
                hasDelete: !!deleteBtn,
                hasDownload: !!downloadBtn
            };
        });

        if (!result.hasCard) {
            return { passed: null, skipped: true, message: 'No document cards to check actions' };
        }

        const hasActions = result.hasViewPdf || result.hasViewText || result.hasDelete || result.hasDownload;

        return {
            passed: hasActions,
            message: `Card actions: viewPdf=${result.hasViewPdf}, viewText=${result.hasViewText}, delete=${result.hasDelete}, download=${result.hasDownload}`
        };
    }
};

// ============================================================================
// PDF Viewer Tests
// ============================================================================
const PdfViewerTests = {
    async pdfViewerModalExists(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        // Try to click view PDF button
        const clicked = await page.evaluate(() => {
            const viewBtn = document.querySelector(
                '[class*="view-pdf"], ' +
                'button[title*="PDF"], ' +
                '.pdf-btn, ' +
                '[data-action="view-pdf"]'
            );

            if (viewBtn) {
                viewBtn.click();
                return true;
            }
            return false;
        });

        if (!clicked) {
            return { passed: null, skipped: true, message: 'No PDF view button to click' };
        }

        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector(
                '.modal, ' +
                '.pdf-viewer, ' +
                '[role="dialog"], ' +
                '.viewer-modal, ' +
                '#pdf-modal'
            );

            const pdfEmbed = document.querySelector('embed[type*="pdf"], iframe[src*="pdf"], object[type*="pdf"]');
            const pdfCanvas = document.querySelector('canvas.pdf-page, .pdf-canvas');

            return {
                hasModal: !!modal && window.getComputedStyle(modal).display !== 'none',
                hasPdfEmbed: !!pdfEmbed,
                hasPdfCanvas: !!pdfCanvas,
                modalClass: modal?.className
            };
        });

        const hasPdfViewer = result.hasModal || result.hasPdfEmbed || result.hasPdfCanvas;

        return {
            passed: hasPdfViewer,
            message: hasPdfViewer
                ? `PDF viewer: modal=${result.hasModal}, embed=${result.hasPdfEmbed}, canvas=${result.hasPdfCanvas}`
                : 'PDF viewer modal did not appear'
        };
    },

    async pdfViewerCloseButton(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        // Open PDF viewer
        await page.evaluate(() => {
            const viewBtn = document.querySelector('[class*="view-pdf"], .pdf-btn, [data-action="view-pdf"]');
            if (viewBtn) viewBtn.click();
        });

        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector('.modal, .pdf-viewer, [role="dialog"]');
            if (!modal) return { hasModal: false };

            const closeBtn = modal.querySelector(
                '.close, ' +
                '.close-btn, ' +
                '[class*="close"], ' +
                'button[aria-label="Close"], ' +
                '.modal-close'
            );

            return {
                hasModal: true,
                hasCloseBtn: !!closeBtn,
                closeBtnText: closeBtn?.textContent?.trim() || closeBtn?.getAttribute('aria-label')
            };
        });

        if (!result.hasModal) {
            return { passed: null, skipped: true, message: 'PDF modal not open for close button test' };
        }

        return {
            passed: result.hasCloseBtn,
            message: result.hasCloseBtn
                ? `Close button found: "${result.closeBtnText}"`
                : 'No close button in PDF viewer'
        };
    }
};

// ============================================================================
// Text Viewer Tests
// ============================================================================
const TextViewerTests = {
    async textViewerModalExists(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        // Try to click view text button
        const clicked = await page.evaluate(() => {
            const viewBtn = document.querySelector(
                '[class*="view-text"], ' +
                'button[title*="text"], ' +
                '.text-btn, ' +
                '[data-action="view-text"]'
            );

            if (viewBtn) {
                viewBtn.click();
                return true;
            }
            return false;
        });

        if (!clicked) {
            return { passed: null, skipped: true, message: 'No text view button to click' };
        }

        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector(
                '.modal, ' +
                '.text-viewer, ' +
                '[role="dialog"], ' +
                '.viewer-modal, ' +
                '#text-modal'
            );

            const textContent = modal?.querySelector('pre, .text-content, .document-text, textarea[readonly]');

            return {
                hasModal: !!modal && window.getComputedStyle(modal).display !== 'none',
                hasTextContent: !!textContent,
                textLength: textContent?.textContent?.length || 0
            };
        });

        const hasTextViewer = result.hasModal || result.hasTextContent;

        return {
            passed: hasTextViewer,
            message: hasTextViewer
                ? `Text viewer: modal=${result.hasModal}, content length=${result.textLength}`
                : 'Text viewer modal did not appear'
        };
    },

    async textViewerCopyButton(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        // Open text viewer
        await page.evaluate(() => {
            const viewBtn = document.querySelector('[class*="view-text"], .text-btn, [data-action="view-text"]');
            if (viewBtn) viewBtn.click();
        });

        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector('.modal, .text-viewer, [role="dialog"]');
            if (!modal) return { hasModal: false };

            const copyBtn = modal.querySelector(
                '[class*="copy"], ' +
                'button[title*="copy"], ' +
                '.copy-btn, ' +
                '[data-action="copy"]'
            );

            return {
                hasModal: true,
                hasCopyBtn: !!copyBtn,
                copyBtnText: copyBtn?.textContent?.trim() || copyBtn?.getAttribute('title')
            };
        });

        if (!result.hasModal) {
            return { passed: null, skipped: true, message: 'Text modal not open for copy button test' };
        }

        if (!result.hasCopyBtn) {
            return { passed: null, skipped: true, message: 'No copy button in text viewer' };
        }

        return {
            passed: true,
            message: `Copy button found: "${result.copyBtnText}"`
        };
    }
};

// ============================================================================
// Bulk Actions Tests
// ============================================================================
const BulkActionsTests = {
    async getAllPdfsButton(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a.btn'));
            const getAllBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('get all') || text.includes('download all') ||
                       text.includes('fetch all') || text.includes('bulk download');
            });

            return {
                hasButton: !!getAllBtn,
                buttonText: getAllBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No get all PDFs button found' };
        }

        return {
            passed: true,
            message: `Get all PDFs button: "${result.buttonText}"`
        };
    },

    async syncLibraryButton(page, baseUrl) {
        await page.goto(`${baseUrl}/library`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a.btn'));
            const syncBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('sync') || text.includes('refresh') || text.includes('update library');
            });

            return {
                hasButton: !!syncBtn,
                buttonText: syncBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No sync library button found' };
        }

        return {
            passed: true,
            message: `Sync button: "${result.buttonText}"`
        };
    }
};

// ============================================================================
// Main Test Runner
// ============================================================================
async function main() {
    log.section('Document Library UI Tests');

    const ctx = await setupTest({ authenticate: true });
    const results = new TestResults('Library Documents Tests');
    const { page } = ctx;
    const { baseUrl } = ctx.config;

    try {
        // Library Page Structure
        log.section('Library Page Structure');

        const pageLoadsResult = await LibraryPageTests.libraryPageLoads(page, baseUrl);
        results.add('Page', 'Loads', pageLoadsResult.passed, pageLoadsResult.message);

        const headerStatsResult = await LibraryPageTests.libraryHeaderStats(page, baseUrl);
        results.add('Page', 'Header Stats', headerStatsResult.passed, headerStatsResult.message);

        const storageModeResult = await LibraryPageTests.storageModeDisplay(page, baseUrl);
        if (storageModeResult.skipped) {
            results.skip('Page', 'Storage Mode', storageModeResult.message);
        } else {
            results.add('Page', 'Storage Mode', storageModeResult.passed, storageModeResult.message);
        }

        // Filter Controls
        log.section('Filter Controls');

        const filtersExistResult = await FilterControlsTests.filterControlsExist(page, baseUrl);
        if (filtersExistResult.skipped) {
            results.skip('Filters', 'Controls Exist', filtersExistResult.message);
        } else {
            results.add('Filters', 'Controls Exist', filtersExistResult.passed, filtersExistResult.message);
        }

        const searchResult = await FilterControlsTests.searchFilterFunctionality(page, baseUrl);
        if (searchResult.skipped) {
            results.skip('Filters', 'Search', searchResult.message);
        } else {
            results.add('Filters', 'Search', searchResult.passed, searchResult.message);
        }

        const dateButtonsResult = await FilterControlsTests.dateFilterButtons(page, baseUrl);
        if (dateButtonsResult.skipped) {
            results.skip('Filters', 'Date Buttons', dateButtonsResult.message);
        } else {
            results.add('Filters', 'Date Buttons', dateButtonsResult.passed, dateButtonsResult.message);
        }

        // View Toggle
        log.section('View Toggle');

        const viewToggleResult = await ViewToggleTests.viewToggleExists(page, baseUrl);
        if (viewToggleResult.skipped) {
            results.skip('View', 'Toggle Exists', viewToggleResult.message);
        } else {
            results.add('View', 'Toggle Exists', viewToggleResult.passed, viewToggleResult.message);
        }

        const viewFuncResult = await ViewToggleTests.viewToggleFunctionality(page, baseUrl);
        if (viewFuncResult.skipped) {
            results.skip('View', 'Toggle Works', viewFuncResult.message);
        } else {
            results.add('View', 'Toggle Works', viewFuncResult.passed, viewFuncResult.message);
        }

        // Document Cards
        log.section('Document Cards');

        const cardsResult = await DocumentCardTests.documentCardsDisplay(page, baseUrl);
        results.add('Cards', 'Display', cardsResult.passed, cardsResult.message);

        const actionsResult = await DocumentCardTests.documentCardActions(page, baseUrl);
        if (actionsResult.skipped) {
            results.skip('Cards', 'Actions', actionsResult.message);
        } else {
            results.add('Cards', 'Actions', actionsResult.passed, actionsResult.message);
        }

        // PDF Viewer
        log.section('PDF Viewer');

        const pdfViewerResult = await PdfViewerTests.pdfViewerModalExists(page, baseUrl);
        if (pdfViewerResult.skipped) {
            results.skip('PDF', 'Viewer Modal', pdfViewerResult.message);
        } else {
            results.add('PDF', 'Viewer Modal', pdfViewerResult.passed, pdfViewerResult.message);
        }

        const pdfCloseResult = await PdfViewerTests.pdfViewerCloseButton(page, baseUrl);
        if (pdfCloseResult.skipped) {
            results.skip('PDF', 'Close Button', pdfCloseResult.message);
        } else {
            results.add('PDF', 'Close Button', pdfCloseResult.passed, pdfCloseResult.message);
        }

        // Text Viewer
        log.section('Text Viewer');

        const textViewerResult = await TextViewerTests.textViewerModalExists(page, baseUrl);
        if (textViewerResult.skipped) {
            results.skip('Text', 'Viewer Modal', textViewerResult.message);
        } else {
            results.add('Text', 'Viewer Modal', textViewerResult.passed, textViewerResult.message);
        }

        const textCopyResult = await TextViewerTests.textViewerCopyButton(page, baseUrl);
        if (textCopyResult.skipped) {
            results.skip('Text', 'Copy Button', textCopyResult.message);
        } else {
            results.add('Text', 'Copy Button', textCopyResult.passed, textCopyResult.message);
        }

        // Bulk Actions
        log.section('Bulk Actions');

        const getAllResult = await BulkActionsTests.getAllPdfsButton(page, baseUrl);
        if (getAllResult.skipped) {
            results.skip('Bulk', 'Get All PDFs', getAllResult.message);
        } else {
            results.add('Bulk', 'Get All PDFs', getAllResult.passed, getAllResult.message);
        }

        const syncResult = await BulkActionsTests.syncLibraryButton(page, baseUrl);
        if (syncResult.skipped) {
            results.skip('Bulk', 'Sync Library', syncResult.message);
        } else {
            results.add('Bulk', 'Sync Library', syncResult.passed, syncResult.message);
        }

    } catch (error) {
        // Check if this is a navigation/browser error (not a real test failure)
        const isNavigationError = error.message &&
            (error.message.includes('context was destroyed') ||
             error.message.includes('Target closed') ||
             error.message.includes('Protocol error') ||
             error.message.includes('Session closed') ||
             error.message.includes('page has been closed'));

        if (isNavigationError) {
            log.warn(`Browser navigation error (test infrastructure issue): ${error.message}`);
            // Don't count as failure - this is a flaky test issue
        } else {
            log.error(`Fatal error: ${error.message}`);
            console.error(error.stack);
        }
    } finally {
        results.print();
        results.save();
        await teardownTest(ctx);
        process.exit(results.exitCode());
    }
}

// Run if executed directly
if (require.main === module) {
    main().catch(error => {
        console.error('Test runner failed:', error);
        process.exit(1);
    });
}

module.exports = { LibraryPageTests, FilterControlsTests, ViewToggleTests, DocumentCardTests, PdfViewerTests, TextViewerTests, BulkActionsTests };
