#!/usr/bin/env node
/**
 * History Page UI Tests
 *
 * Tests for the research history page and its functionality.
 *
 * Run: node test_history_page_ci.js
 */

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

// ============================================================================
// History Page Structure Tests
// ============================================================================
const HistoryPageTests = {
    async historyPageLoads(page, baseUrl) {
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            return {
                hasContent: !!document.querySelector('.history-container, .ldr-history, #history, .research-history'),
                hasHeader: !!document.querySelector('h1, .history-header, .page-title'),
                headerText: document.querySelector('h1, .history-header, .page-title')?.textContent?.trim(),
                hasTable: !!document.querySelector('table, .history-table, .ldr-history-list'),
                hasItems: document.querySelectorAll('.history-item, .research-item, tr[data-id], [data-research-id]').length,
                hasEmptyState: !!document.querySelector('.empty-state, .no-history, .alert-info')
            };
        });

        const passed = result.hasContent || result.hasHeader || result.hasTable || result.hasEmptyState;
        return {
            passed,
            message: passed
                ? `History page loaded (header: "${result.headerText}", items: ${result.hasItems})`
                : 'History page failed to load'
        };
    },

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

        const result = await page.evaluate(() => {
            const table = document.querySelector('table, .history-table');
            if (!table) return { hasTable: false };

            const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent?.toLowerCase().trim());
            const rows = table.querySelectorAll('tbody tr, .history-item');

            return {
                hasTable: true,
                hasQuery: headers.some(h => h.includes('query') || h.includes('question') || h.includes('topic')),
                hasMode: headers.some(h => h.includes('mode') || h.includes('type')),
                hasStatus: headers.some(h => h.includes('status')),
                hasDate: headers.some(h => h.includes('date') || h.includes('time') || h.includes('created')),
                hasDuration: headers.some(h => h.includes('duration') || h.includes('time')),
                headerCount: headers.length,
                rowCount: rows.length,
                headers: headers.slice(0, 8)
            };
        });

        if (!result.hasTable) {
            // Check for card-based layout instead
            const cardResult = await page.evaluate(() => {
                const cards = document.querySelectorAll('.history-card, .research-card, [data-research-id]');
                return { hasCards: cards.length > 0, cardCount: cards.length };
            });

            if (cardResult.hasCards) {
                return { passed: true, message: `History uses card layout (${cardResult.cardCount} cards)` };
            }

            return { passed: null, skipped: true, message: 'No history table or cards found' };
        }

        return {
            passed: result.hasQuery || result.hasDate,
            message: `History table: ${result.headerCount} columns (${result.headers.join(', ')}), ${result.rowCount} rows`
        };
    },

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

        const result = await page.evaluate(() => {
            const items = document.querySelectorAll('.history-item, .research-item, tr[data-id], [data-research-id]');
            if (items.length === 0) return { hasItems: false };

            const firstItem = items[0];
            const buttons = Array.from(firstItem.querySelectorAll('button, a.btn, .btn, a[href]'));
            const buttonTexts = buttons.map(b => b.textContent?.toLowerCase() || b.title?.toLowerCase() || '');

            return {
                hasItems: true,
                itemCount: items.length,
                hasViewButton: buttonTexts.some(t => t.includes('view') || t.includes('open') || t.includes('results')) ||
                              !!firstItem.querySelector('a[href*="/results/"]'),
                hasDeleteButton: buttonTexts.some(t => t.includes('delete') || t.includes('remove')) ||
                                !!firstItem.querySelector('.btn-danger, .delete-btn'),
                hasExportButton: buttonTexts.some(t => t.includes('export') || t.includes('download')),
                actionCount: buttons.length
            };
        });

        if (!result.hasItems) {
            return { passed: null, skipped: true, message: 'No history items to test actions' };
        }

        const hasActions = result.hasViewButton || result.hasDeleteButton || result.actionCount > 0;
        return {
            passed: hasActions,
            message: hasActions
                ? `History item actions: view=${result.hasViewButton}, delete=${result.hasDeleteButton}, export=${result.hasExportButton} (${result.actionCount} buttons)`
                : 'No action buttons found on history items'
        };
    },

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

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const clearBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('clear') ||
                b.textContent?.toLowerCase().includes('delete all') ||
                b.textContent?.toLowerCase().includes('remove all')
            );

            return {
                hasClearButton: !!clearBtn,
                buttonText: clearBtn?.textContent?.trim()
            };
        });

        if (!result.hasClearButton) {
            return { passed: null, skipped: true, message: 'No clear all history button found' };
        }

        return {
            passed: true,
            message: `Clear history button found ("${result.buttonText}")`
        };
    },

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

        const result = await page.evaluate(() => {
            const searchInput = document.querySelector(
                'input[type="search"], ' +
                'input[placeholder*="search"], ' +
                'input[placeholder*="filter"], ' +
                '#history-search, ' +
                '.history-filter'
            );

            const filterSelect = document.querySelector(
                'select[name*="filter"], ' +
                'select[name*="status"], ' +
                '.filter-dropdown'
            );

            return {
                hasSearch: !!searchInput,
                searchPlaceholder: searchInput?.placeholder,
                hasFilter: !!filterSelect,
                filterOptions: filterSelect ? Array.from(filterSelect.options).map(o => o.text).slice(0, 5) : []
            };
        });

        const hasAny = result.hasSearch || result.hasFilter;
        if (!hasAny) {
            return { passed: null, skipped: true, message: 'No search/filter functionality on history page' };
        }

        return {
            passed: true,
            message: result.hasSearch
                ? `Search input found (placeholder: "${result.searchPlaceholder}")`
                : `Filter dropdown found (options: ${result.filterOptions.join(', ')})`
        };
    },

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

        const result = await page.evaluate(() => {
            const pagination = document.querySelector('.pagination, .pager, nav[aria-label*="pagination"]');
            const pageLinks = document.querySelectorAll('.page-link, .pagination a, .pagination button');
            const loadMoreBtn = document.querySelector('.load-more, button[onclick*="loadMore"]');

            return {
                hasPagination: !!pagination || pageLinks.length > 0,
                pageCount: pageLinks.length,
                hasLoadMore: !!loadMoreBtn,
                hasNextPrev: !!document.querySelector('[aria-label*="next"], [aria-label*="previous"], .next, .prev')
            };
        });

        if (!result.hasPagination && !result.hasLoadMore) {
            return { passed: null, skipped: true, message: 'No pagination controls (may have few items)' };
        }

        return {
            passed: true,
            message: result.hasPagination
                ? `Pagination found (${result.pageCount} page links, next/prev=${result.hasNextPrev})`
                : 'Load more button found'
        };
    }
};

// ============================================================================
// History API Tests
// ============================================================================
const HistoryApiTests = {
    async historyApiResponds(page, baseUrl) {
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/api/history`);
                if (!response.ok) return { ok: false, status: response.status };

                const data = await response.json();
                return {
                    ok: true,
                    status: response.status,
                    itemCount: Array.isArray(data) ? data.length : (data.items?.length || data.history?.length || 0)
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

        return {
            passed: result.ok,
            message: result.ok
                ? `History API responds (${result.itemCount} items)`
                : `History API failed: ${result.error || 'status ' + result.status}`
        };
    },

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

        // Get first research ID
        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], [data-id], a[href*="/results/"]');
            if (!item) return null;

            return item.dataset?.researchId ||
                   item.dataset?.id ||
                   item.href?.match(/\/results\/(\d+)/)?.[1];
        });

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No research ID found to test details API' };
        }

        const result = await page.evaluate(async (url, id) => {
            try {
                const response = await fetch(`${url}/api/research/${id}`);
                if (!response.ok) return { ok: false, status: response.status };

                const data = await response.json();
                return {
                    ok: true,
                    status: response.status,
                    hasQuery: !!data.query,
                    hasStatus: !!data.status
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl, researchId);

        return {
            passed: result.ok,
            message: result.ok
                ? `Research details API responds (query=${result.hasQuery}, status=${result.hasStatus})`
                : `Research details API failed: ${result.error || 'status ' + result.status}`
        };
    }
};

// ============================================================================
// Main Test Runner
// ============================================================================
async function main() {
    log.section('History Page Tests');

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

    try {
        // Page Structure Tests
        log.section('Page Structure');
        await results.run('Structure', 'History Page Loads', () => HistoryPageTests.historyPageLoads(page, baseUrl), { page });

        const tableResult = await HistoryPageTests.historyTableStructure(page, baseUrl);
        if (tableResult.skipped) {
            results.skip('Structure', 'History Table Structure', tableResult.message);
        } else {
            results.add('Structure', 'History Table Structure', tableResult.passed, tableResult.message);
        }

        const actionsResult = await HistoryPageTests.historyItemActions(page, baseUrl);
        if (actionsResult.skipped) {
            results.skip('Structure', 'History Item Actions', actionsResult.message);
        } else {
            results.add('Structure', 'History Item Actions', actionsResult.passed, actionsResult.message);
        }

        const clearResult = await HistoryPageTests.clearHistoryButton(page, baseUrl);
        if (clearResult.skipped) {
            results.skip('Structure', 'Clear History Button', clearResult.message);
        } else {
            results.add('Structure', 'Clear History Button', clearResult.passed, clearResult.message);
        }

        // Search/Filter Tests
        log.section('Search & Filter');
        const searchResult = await HistoryPageTests.historySearchFilter(page, baseUrl);
        if (searchResult.skipped) {
            results.skip('Filter', 'History Search/Filter', searchResult.message);
        } else {
            results.add('Filter', 'History Search/Filter', searchResult.passed, searchResult.message);
        }

        const paginationResult = await HistoryPageTests.historyPagination(page, baseUrl);
        if (paginationResult.skipped) {
            results.skip('Filter', 'History Pagination', paginationResult.message);
        } else {
            results.add('Filter', 'History Pagination', paginationResult.passed, paginationResult.message);
        }

        // API Tests
        log.section('History APIs');
        await results.run('API', 'History API Responds', () => HistoryApiTests.historyApiResponds(page, baseUrl), { page });

        const detailsResult = await HistoryApiTests.historyItemDetailsApi(page, baseUrl);
        if (detailsResult.skipped) {
            results.skip('API', 'Research Details API', detailsResult.message);
        } else {
            results.add('API', 'Research Details API', detailsResult.passed, detailsResult.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 = { HistoryPageTests, HistoryApiTests };
