import { test, expect } from '@playwright/test';
import { MOCK_FS_INIT_SCRIPT } from './fixtures/mock-fs-api.js';
import * as path from 'path';

const HTML_PATH = path.resolve('archive/dist/archive.html');

test.describe('Archive Browser', () => {
    test.beforeEach(async ({ page }) => {
        await page.addInitScript(MOCK_FS_INIT_SCRIPT);
    });

    test('loads without errors', async ({ page }) => {
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#appContainer', { timeout: 15000 });

        // Page title contains "Archive"
        await expect(page).toHaveTitle(/Archive/i);

        // No-directory message is shown before any directory is opened
        await expect(page.locator('#noDirectoryMessage')).toBeVisible();

        // The open-directory button is present
        await expect(page.locator('#addDirectoryBtn')).toBeVisible();
    });

    test('scans a mock directory tree and displays files', async ({ page }) => {
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        // Log to see if the scan is working
        page.on('console', msg => {
            console.log('Browser console:', msg.text());
        });
        page.on('pageerror', err => {
            console.log('Page error:', err.message);
        });

        // The archive expects a two-level structure: root → transmittal-folder → files.
        // Flat files at the root are not counted — they must be inside subdirectories.
        await page.evaluate(() => {
            window.__setMockDirectoryTree('test-project', {
                '2025-01-15_123456-EM-TRN-0001 (IFC) - First Transmittal': {
                    '123456-EL-SPC-2623_A (IFC) - Specification.pdf': '%PDF',
                    '123456-EL-DRW-0001_B (IFR) - Drawing.dwg':        'DWG',
                },
                '2025-02-10_123456-EM-TRN-0002 (IFC) - Second Transmittal': {
                    '789012-ME-CAL-0001_A (IFA) - Calculation.pdf': '%PDF',
                },
            });
            console.log('Mock directory set up');
        });

        await page.locator('#addDirectoryBtn').click();
        
        // Wait for scanning to complete
        await page.waitForTimeout(2000);

        // Log state for debugging
        await page.evaluate(() => {
            console.log('After scanning:');
            console.log('  window.app exists:', typeof window.app !== 'undefined');
            if (typeof window.app !== 'undefined') {
                console.log('  window.app type:', typeof window.app);
                console.log('  window.app constructor:', window.app.constructor.name);
                console.log('  window.app.directories:', Array.isArray(window.app.directories) ? window.app.directories.length : window.app.directories);
                console.log('  window.app.files:', Array.isArray(window.app.files) ? window.app.files.length : window.app.files);
                console.log('  window.app.filteredFiles:', Array.isArray(window.app.filteredFiles) ? window.app.filteredFiles.length : window.app.filteredFiles);
                console.log('  window.app modules:', Object.keys(window.app || {}).filter(k => k.startsWith('modules')));
            } else {
                console.log('  window.app is undefined!');
            }
        });

        // Select all grouping folders so files are included in the file list
        await page.evaluate(() => {
            const cb = document.getElementById('selectAllGroupingCheckbox');
            if (cb && !cb.checked) cb.click();
        });
        await page.waitForTimeout(500);

        // The files table should have at least one data row
        const rowCount = await page.locator('#filesTableBody tr').count();
        expect(rowCount).toBeGreaterThanOrEqual(1);

        // Status bar should show files were found (any non-empty text is fine)
        const fileCountText = await page.locator('#fileCount').textContent();
        expect(fileCountText).toBeTruthy();
    });

    test('Mode 1: ?projects=A,B enters each project\'s Archive subfolder', async ({ page }) => {
        // Multi-project layout: server root holds project folders, each containing an
        // Archive/ subfolder with third-party folders. ?projects=A,B (set as
        // window.app.projectFilter; url-state.restore() handles parsing in real flow)
        // selects the projects to scan. Each is descended into <project>/Archive/.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('combined-root', {
                'Project-A': {
                    'Archive': {
                        'ACME': {
                            '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                                '123456-EL-SPC-0001_A (IFC) - SpecA.pdf': '%PDF',
                            },
                        },
                    },
                },
                'Project-B': {
                    'Archive': {
                        'Beta': {
                            '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': {
                                '789012-EL-SPC-0002_A (IFC) - SpecB.pdf': '%PDF',
                            },
                        },
                    },
                },
                'Project-C': {
                    'Archive': {
                        'Gamma': {
                            '2025-03-01_345678-EM-TRN-0001 (IFC) - Third': {
                                '345678-EL-SPC-0003_A (IFC) - SpecC.pdf': '%PDF',
                            },
                        },
                    },
                },
            });
        });

        await page.evaluate(() => {
            window.app.projectFilter = new Set(['Project-A', 'Project-B']);
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        // Third-party (grouping) folders should be ACME and Beta — never project IDs or "Archive"
        const groupingFolders = await page.evaluate(() =>
            window.app.groupingFolders.map(f => f.name)
        );
        expect(groupingFolders).toContain('ACME');
        expect(groupingFolders).toContain('Beta');
        expect(groupingFolders).not.toContain('Project-A');
        expect(groupingFolders).not.toContain('Project-B');
        expect(groupingFolders).not.toContain('Archive');
        expect(groupingFolders).not.toContain('Gamma');

        await page.evaluate(() => {
            const cb = document.getElementById('selectAllGroupingCheckbox');
            if (cb && !cb.checked) cb.click();
        });
        await page.waitForTimeout(500);

        const tableText = await page.locator('#filesTableBody').textContent();
        expect(tableText).toContain('SpecA');
        expect(tableText).toContain('SpecB');
        expect(tableText).not.toContain('SpecC');
    });

    test('same-name third-parties across projects merge into one party row', async ({ page }) => {
        // Both projects have a "BM" third-party folder. The parties pane must
        // show "BM" once, and selecting it must surface files from both projects.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('combined-root', {
                '176109': {
                    'Archive': {
                        'BM': {
                            'Issued': {
                                '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                                    '123456-EL-SPC-0001_A (IFC) - SpecFromA.pdf': '%PDF',
                                },
                            },
                        },
                    },
                },
                '197072': {
                    'Archive': {
                        'BM': {
                            'Issued': {
                                '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': {
                                    '789012-EL-SPC-0002_A (IFC) - SpecFromB.pdf': '%PDF',
                                },
                            },
                        },
                    },
                },
            });
        });

        await page.evaluate(() => {
            window.app.projectFilter = new Set(['176109', '197072']);
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        // The DOM should render exactly one BM row, not two.
        const renderedNames = await page.locator('#groupingFoldersList .folder-item-name').allTextContents();
        const bmRows = renderedNames.filter(n => n.trim() === 'BM');
        expect(bmRows.length).toBe(1);

        // Both projects' files should be visible under the single BM party.
        const tableText = await page.locator('#filesTableBody').textContent();
        expect(tableText).toContain('SpecFromA');
        expect(tableText).toContain('SpecFromB');
    });

    test('toggling visibleProjects hides files without re-scanning', async ({ page }) => {
        // Two projects scanned. Toggling 197072 out of visibleProjects should
        // hide its files; window.app.files (the in-memory scan result) must NOT
        // shrink — only the visible/filtered set changes.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('combined-root', {
                '176109': {
                    'Archive': {
                        'BM': {
                            'Issued': {
                                '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                                    '123456-EL-SPC-0001_A (IFC) - SpecFromA.pdf': '%PDF',
                                },
                            },
                        },
                    },
                },
                '197072': {
                    'Archive': {
                        'BM': {
                            'Issued': {
                                '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': {
                                    '789012-EL-SPC-0002_A (IFC) - SpecFromB.pdf': '%PDF',
                                },
                            },
                        },
                    },
                },
            });
        });

        await page.evaluate(() => {
            window.app.projectFilter = new Set(['176109', '197072']);
            window.app.visibleProjects = new Set(['176109', '197072']);
            window.app.isMultiProject = true;
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        const beforeFileCount = await page.evaluate(() => window.app.files.length);
        expect(beforeFileCount).toBeGreaterThanOrEqual(2);

        // Toggle 197072 out of the visible set — must NOT shrink window.app.files.
        await page.evaluate(() => {
            window.app.visibleProjects = new Set(['176109']);
            window.app.modules.app.updateUI();
            window.app.modules.filtering.applyFilters();
        });

        const afterFileCount = await page.evaluate(() => window.app.files.length);
        expect(afterFileCount).toBe(beforeFileCount);

        const tableText = await page.locator('#filesTableBody').textContent();
        expect(tableText).toContain('SpecFromA');
        expect(tableText).not.toContain('SpecFromB');

        // Toggle 197072 back — file reappears immediately, no rescan needed.
        await page.evaluate(() => {
            window.app.visibleProjects = new Set(['176109', '197072']);
            window.app.modules.app.updateUI();
            window.app.modules.filtering.applyFilters();
        });
        const tableTextAfter = await page.locator('#filesTableBody').textContent();
        expect(tableTextAfter).toContain('SpecFromA');
        expect(tableTextAfter).toContain('SpecFromB');
    });

    test('?show= URL param round-trips: serialize emits it, restore reads it', async ({ page }) => {
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        // Serialize: visibleProjects ⊊ projectFilter → ?show= present
        const qsWithSubset = await page.evaluate(() => {
            window.app.projectFilter = new Set(['A', 'B', 'C']);
            window.app.visibleProjects = new Set(['A']);
            return window.app.modules.urlState.serialize();
        });
        expect(qsWithSubset).toContain('projects=A%2CB%2CC');
        expect(qsWithSubset).toContain('show=A');

        // Serialize: visibleProjects equals projectFilter → ?show= omitted
        const qsAllVisible = await page.evaluate(() => {
            window.app.projectFilter = new Set(['A', 'B']);
            window.app.visibleProjects = new Set(['A', 'B']);
            return window.app.modules.urlState.serialize();
        });
        expect(qsAllVisible).not.toContain('show=');

        // Restore: ?show= populates visibleProjects from URL.
        await page.goto(`file://${HTML_PATH}?projects=A,B,C&show=A,B`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });
        const restored = await page.evaluate(() => {
            // Re-run restore in case the early init() raced with DOM availability —
            // matches how the existing projectFilter test bypasses this path too.
            window.app.modules.urlState.restore();
            return {
                search: location.search,
                projectFilter: Array.from(window.app.projectFilter).sort(),
                visibleProjects: Array.from(window.app.visibleProjects).sort(),
            };
        });
        expect(restored.search).toBe('?projects=A,B,C&show=A,B');
        expect(restored.projectFilter).toEqual(['A', 'B', 'C']);
        expect(restored.visibleProjects).toEqual(['A', 'B']);
    });

    test('Mode 2: project-root with Archive child auto-descends into Archive only', async ({ page }) => {
        // Picked directory is a project root containing Archive/ plus Reviewing/.
        // The app should enter Archive/ automatically and ignore Reviewing/.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('project-176109', {
                'Archive': {
                    'ACME': {
                        '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                            '123456-EL-SPC-0001_A (IFC) - InArchive.pdf': '%PDF',
                        },
                    },
                },
                'Reviewing': {
                    'ShouldNotShow': {
                        '2025-02-10_789012-EM-TRN-0001 (IFC) - HiddenStage': {
                            '789012-EL-SPC-0002_A (IFC) - InReviewing.pdf': '%PDF',
                        },
                    },
                },
            });
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        const groupingFolders = await page.evaluate(() =>
            window.app.groupingFolders.map(f => f.name)
        );
        expect(groupingFolders).toContain('ACME');
        expect(groupingFolders).not.toContain('Archive');
        expect(groupingFolders).not.toContain('Reviewing');
        expect(groupingFolders).not.toContain('ShouldNotShow');

        const fileNames = await page.evaluate(() => window.app.files.map(f => f.name));
        expect(fileNames.some(n => n.includes('InArchive'))).toBe(true);
        expect(fileNames.some(n => n.includes('InReviewing'))).toBe(false);
    });

    test('Mode 3: in-archive layout (no projects=, no Archive child) uses today\'s scan', async ({ page }) => {
        // Picked directory IS the Archive folder — children are third-parties directly.
        // Behavior must match pre-change baseline.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('Archive', {
                'ACME': {
                    'Issued': {
                        '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                            '123456-EL-SPC-0001_A (IFC) - SpecA.pdf': '%PDF',
                        },
                    },
                },
                'Beta': {
                    'Received': {
                        '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': {
                            '789012-EL-SPC-0002_A (IFC) - SpecB.pdf': '%PDF',
                        },
                    },
                },
            });
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        const groupingFolders = await page.evaluate(() =>
            window.app.groupingFolders.map(f => f.name)
        );
        expect(groupingFolders).toContain('ACME');
        expect(groupingFolders).toContain('Beta');

        await page.evaluate(() => {
            const cb = document.getElementById('selectAllGroupingCheckbox');
            if (cb && !cb.checked) cb.click();
        });
        await page.waitForTimeout(500);

        const tableText = await page.locator('#filesTableBody').textContent();
        expect(tableText).toContain('SpecA');
        expect(tableText).toContain('SpecB');
    });

    test('disabled folder types are skipped at scan time (not just hidden in UI)', async ({ page }) => {
        // Default enabledFolderTypes is {issued, received}. The Incoming folder should
        // never be listed — its files must not appear in window.app.files at all.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('Archive', {
                'ACME': {
                    'Issued': {
                        '2025-01-15_123456-EM-TRN-0001 (IFC) - First': {
                            '123456-EL-SPC-0001_A (IFC) - SpecIssued.pdf': '%PDF',
                        },
                    },
                    'Incoming': {
                        '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': {
                            '789012-EL-SPC-0002_A (IFC) - SpecIncoming.pdf': '%PDF',
                        },
                    },
                },
            });
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        const fileNames = await page.evaluate(() => window.app.files.map(f => f.name));
        expect(fileNames.some(n => n.includes('SpecIssued'))).toBe(true);
        // Incoming is in FOLDER_TYPE_NAMES but not in default enabledFolderTypes — must be skipped
        expect(fileNames.some(n => n.includes('SpecIncoming'))).toBe(false);
    });

    test('Preview toggle is checked by default', async ({ page }) => {
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        // The checkbox lives inside the (hidden) preview-controls area
        // before any directory is loaded — waitForSelector defaults to
        // 'visible' which would time out. Wait for it to be ATTACHED
        // and verify the underlying state instead.
        await page.waitForSelector('#filePreviewToggle', { state: 'attached', timeout: 15000 });
        await expect(page.locator('#filePreviewToggle')).toBeChecked();
    });

    test('default modifier filter selects only base and +C revisions', async ({ page }) => {
        // Files with mixed revision modifiers — by default only base + +C should
        // surface in the visible file table; +B (and any other non-+C modifier)
        // should be hidden until the user explicitly opts in.
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 });

        await page.evaluate(() => {
            window.__setMockDirectoryTree('test-project', {
                '2025-01-15_123456-EM-TRN-0001 (IFC) - Mixed Mods': {
                    '123456-EL-SPC-0001_A (IFC) - SpecBase.pdf': '%PDF',
                    '123456-EL-SPC-0001_A+C1 (IFC) - SpecComment.pdf': '%PDF',
                    '123456-EL-SPC-0001_A+B1 (IFC) - SpecScratch.pdf': '%PDF',
                    '123456-EL-SPC-0001_A+D1 (IFC) - SpecDraft.pdf': '%PDF',
                },
            });
        });

        await page.locator('#addDirectoryBtn').click();
        await page.waitForTimeout(2000);

        await page.evaluate(() => {
            const cb = document.getElementById('selectAllGroupingCheckbox');
            if (cb && !cb.checked) cb.click();
        });
        await page.waitForTimeout(300);

        const state = await page.evaluate(() => ({
            available: Array.from(window.app.availableModifiers).sort(),
            selected: Array.from(window.app.selectedModifiers).sort(),
            visibleNames: window.app.filteredFiles.map(f => f.name),
        }));

        // The dropdown still LISTS all modifiers found in the data…
        expect(state.available).toEqual(['+B', '+C', '+D', 'base']);
        // …but only base + +C are pre-selected by default.
        expect(state.selected).toEqual(['+C', 'base']);
        // Files with hidden modifier types must be absent from the table.
        expect(state.visibleNames.some(n => n.includes('SpecBase'))).toBe(true);
        expect(state.visibleNames.some(n => n.includes('SpecComment'))).toBe(true);
        expect(state.visibleNames.some(n => n.includes('SpecScratch'))).toBe(false);
        expect(state.visibleNames.some(n => n.includes('SpecDraft'))).toBe(false);
    });

    test('parser module uses shared zddc helpers (not its own wrappers)', async ({ page }) => {
        await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector('#appContainer', { timeout: 15000 });

        const probe = await page.evaluate(() => ({
            // archive's parser module should NOT re-export wrappers we removed
            hasParseFileNameWrapper: typeof window.app.modules.parser?.parseFileName === 'function',
            hasParseRevisionWrapper: typeof window.app.modules.parser?.parseRevision === 'function',
            // but should still expose archive-specific helpers
            hasIsTransmittalFolder:  typeof window.app.modules.parser?.isTransmittalFolder === 'function',
            hasGroupFiles:           typeof window.app.modules.parser?.groupFilesByTrackingNumber === 'function',
            // and isTransmittalFolder should agree with shared zddc.parseFolder
            validFolder:   window.app.modules.parser.isTransmittalFolder('2025-10-31_123456-EM-SUB-0001 (IFR) - General Arrangement'),
            invalidFolder: window.app.modules.parser.isTransmittalFolder('not-a-zddc-folder'),
        }));

        expect(probe.hasParseFileNameWrapper).toBe(false);
        expect(probe.hasParseRevisionWrapper).toBe(false);
        expect(probe.hasIsTransmittalFolder).toBe(true);
        expect(probe.hasGroupFiles).toBe(true);
        expect(probe.validFolder).toBe(true);
        expect(probe.invalidFolder).toBe(false);
    });
});
