/**
 * Portrait PWA layout tests.
 *
 * Verifies that the Settings panel (and other list-pane views) are fully
 * accessible in portrait orientation when the app is running as a PWA
 * (standalone display mode).
 *
 * Because Playwright cannot set display-mode:standalone natively, we inject
 * a small CSS override that activates the portrait media query rule-set via
 * a data attribute, and also add the .portrait-pwa class that the JS watcher
 * would normally add. This exercises the same CSS paths that fire in the real
 * installed PWA.
 */

const { test, expect } = require('@playwright/test');

// Portrait dimensions (iPhone 12 / similar)
const PORTRAIT_WIDTH  = 390;
const PORTRAIT_HEIGHT = 844;
const LANDSCAPE_WIDTH  = 844;
const LANDSCAPE_HEIGHT = 390;

/**
 * Inject an override so the portrait media-query CSS fires without needing
 * an actual installed PWA. We piggyback on a custom data attribute.
 */
async function enablePortraitPwaMode(page) {
  await page.addStyleTag({
    content: `
      /* Mirror (display-mode:standalone) and (orientation:portrait) rules for testing */
      [data-portrait-pwa-test] .app { grid-template-columns: 1fr; }
      [data-portrait-pwa-test] .bottom-nav { display: flex; }
      [data-portrait-pwa-test] .sidebar-toggle { display: inline-flex; }
      [data-portrait-pwa-test] .sidebar {
        position: fixed; top:0; left:0; bottom:0;
        width: min(86vw, 300px); max-width:300px; z-index:100;
        border-right:1px solid var(--line-soft); box-shadow:var(--shadow);
        transform:translateX(-100%); transition:transform 0.28s var(--ease);
      }
      [data-portrait-pwa-test] .app.sidebar-open .sidebar { transform:translateX(0); }
      [data-portrait-pwa-test] .app.sidebar-open .sidebar-backdrop { display:block; opacity:1; }
      [data-portrait-pwa-test] .topbar { position:sticky; top:0; z-index:50; }
      [data-portrait-pwa-test] .body {
        flex-direction:column;
        overflow-y:auto; overflow-x:hidden;
        overscroll-behavior:contain;
      }
      [data-portrait-pwa-test] .player-pane { flex:none; width:100%; overflow-y:visible; }
      [data-portrait-pwa-test] .player-stage { width:100%; aspect-ratio:16/9; }
      [data-portrait-pwa-test] .btn-row { flex-wrap:wrap; gap:7px; }
      [data-portrait-pwa-test] .now-meta { flex-direction:column; gap:12px; margin-top:14px; }
      [data-portrait-pwa-test] .np-actions { flex-wrap:wrap; gap:6px; }
      [data-portrait-pwa-test] .list-pane {
        flex:none; min-height:100%; width:100%;
        border-left:none; border-top:1px solid var(--line-soft);
        overflow-y:visible; overflow-x:hidden;
        padding-bottom: 60px;
      }
      [data-portrait-pwa-test] .cards { max-height:none; overflow-y:visible; padding-bottom:0; }
      [data-portrait-pwa-test] .settings { padding-bottom:20px; }
    `,
  });
  await page.evaluate(() => {
    document.documentElement.setAttribute('data-portrait-pwa-test', '1');
    // Also add the JS class that setupPortraitPwaWatcher would add
    const app = document.querySelector('.app');
    if (app) app.classList.add('portrait-pwa');
  });
}

/**
 * Playwright cannot set display-mode:standalone, so isPortraitPWA() in app.js
 * would return false and the JS scroll behaviors (scrollListIntoViewPortrait,
 * scrollPlayerIntoViewPortrait) would no-op. Stub matchMedia for that one
 * query BEFORE app.js loads so the JS layer behaves as in the installed PWA.
 */
async function stubStandalonePortraitMediaQuery(page) {
  await page.addInitScript(() => {
    const orig = window.matchMedia.bind(window);
    window.matchMedia = (q) => {
      if (q.includes('display-mode: standalone') && q.includes('portrait')) {
        return {
          matches: true, media: q, onchange: null,
          addEventListener: () => {}, removeEventListener: () => {},
          addListener: () => {}, removeListener: () => {},
          dispatchEvent: () => false,
        };
      }
      return orig(q);
    };
  });
}

test.describe('Portrait PWA layout', () => {
  test.beforeEach(async ({ page }) => {
    await page.setViewportSize({ width: PORTRAIT_WIDTH, height: PORTRAIT_HEIGHT });
    await stubStandalonePortraitMediaQuery(page);
    await page.goto('/');
    // Wait for app JS to initialise
    await page.waitForSelector('.app', { state: 'attached' });
    await enablePortraitPwaMode(page);
  });

  test('app starts in portrait without horizontal scroll', async ({ page }) => {
    const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
    const viewportWidth   = await page.evaluate(() => window.innerWidth);
    expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 2); // allow 2px rounding
  });

  test('Settings panel is accessible in portrait', async ({ page }) => {
    // Open the sidebar drawer (hamburger)
    await page.click('#sidebarToggle');
    await page.waitForTimeout(350); // drawer animation

    // Navigate to Settings
    await page.click('[data-view="settings"]');
    await page.waitForTimeout(300);

    // Settings content must exist inside the list pane
    const settingsEl = await page.locator('.settings');
    await expect(settingsEl).toBeVisible();

    // The settings element must not be clipped — its bounding box bottom
    // should be reachable (list-pane scrolls to accommodate it)
    const listPane = page.locator('.list-pane');
    await expect(listPane).toBeVisible();

    const listPaneBox = await listPane.boundingBox();
    expect(listPaneBox).not.toBeNull();
    expect(listPaneBox.height).toBeGreaterThan(50); // must have meaningful height
  });

  test('Settings panel scrolls to bottom without overlap', async ({ page }) => {
    await page.click('#sidebarToggle');
    await page.waitForTimeout(350);
    await page.click('[data-view="settings"]');
    await page.waitForTimeout(300);

    // Scroll list-pane to the bottom
    await page.evaluate(() => {
      const lp = document.querySelector('.list-pane');
      if (lp) lp.scrollTop = lp.scrollHeight;
    });
    await page.waitForTimeout(100);

    // After scrolling, the last setting group should be visible
    const lastGroup = page.locator('.set-group').last();
    await expect(lastGroup).toBeVisible();
  });

  test('navigation works while a track is playing (player pane taller than viewport)', async ({ page }) => {
    // Simulate the DOM state afterLoad() creates for a playing track: player
    // pane populated with controls, meta, up-next and related — together
    // taller than the viewport. This used to leave the list-pane below the
    // fold with .body overflow:hidden, so every nav tap looked dead.
    await page.evaluate(() => {
      document.getElementById('playerPane').classList.remove('empty');
      document.getElementById('playerPlaceholder').classList.add('hidden');
      document.getElementById('controls').classList.remove('hidden');
      document.getElementById('nowPlayingMeta').classList.remove('hidden');
      document.getElementById('npTitle').textContent = 'Test track';
      document.getElementById('upnext').classList.remove('hidden');
      document.getElementById('upnextList').innerHTML =
        '<div class="upnext-item">queued</div>'.repeat(4);
      document.getElementById('relatedPanel').classList.remove('hidden');
      document.getElementById('relatedList').innerHTML =
        '<div class="related-item">related</div>'.repeat(6);
      document.getElementById('miniBar').classList.remove('hidden');
    });

    // Sanity: the populated player pane really is taller than the viewport.
    const paneHeight = await page.evaluate(
      () => document.getElementById('playerPane').getBoundingClientRect().height
    );
    expect(paneHeight).toBeGreaterThan(PORTRAIT_HEIGHT);

    // Navigate via the bottom nav.
    await page.click('.bottom-nav-btn[data-view="history"]');
    await page.waitForTimeout(700); // smooth scroll settle

    // The view switched AND its pane is actually on-screen.
    await expect(page.locator('#listTitle')).toHaveText('History');
    const box = await page.locator('.list-pane').boundingBox();
    expect(box).not.toBeNull();
    expect(box.y).toBeLessThan(PORTRAIT_HEIGHT / 2); // top of the page is visible
    expect(box.y + box.height).toBeGreaterThan(200);  // and it has visible extent

    // Sidebar navigation must work the same way.
    await page.click('#sidebarToggle');
    await page.waitForTimeout(350);
    await page.click('.nav-item[data-view="saved"]');
    await page.waitForTimeout(700);
    await expect(page.locator('#listTitle')).toHaveText('Saved videos');
    const box2 = await page.locator('.list-pane').boundingBox();
    expect(box2.y).toBeLessThan(PORTRAIT_HEIGHT / 2);
  });

  test('switching landscape to portrait restores layout', async ({ page }) => {
    // Start in landscape
    await page.setViewportSize({ width: LANDSCAPE_WIDTH, height: LANDSCAPE_HEIGHT });
    await page.waitForTimeout(200);

    // Switch back to portrait
    await page.setViewportSize({ width: PORTRAIT_WIDTH, height: PORTRAIT_HEIGHT });
    await page.waitForTimeout(300);

    // App should not have horizontal overflow
    const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
    const viewportWidth   = await page.evaluate(() => window.innerWidth);
    expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 2);

    // List pane should be visible and have height
    const listPane = page.locator('.list-pane');
    await expect(listPane).toBeVisible();
    const box = await listPane.boundingBox();
    expect(box.height).toBeGreaterThan(50);
  });

  test('landscape mode is unaffected — sidebar column visible at wide viewport', async ({ page }) => {
    // Remove the portrait-pwa test overrides
    await page.evaluate(() => {
      document.documentElement.removeAttribute('data-portrait-pwa-test');
      const app = document.querySelector('.app');
      if (app) app.classList.remove('portrait-pwa');
    });

    // Switch to a wide desktop viewport
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.waitForTimeout(200);

    // In landscape desktop, the sidebar should be visible (grid column)
    const sidebar = page.locator('.sidebar');
    await expect(sidebar).toBeVisible();

    // list-pane should have a fixed width (452px desktop)
    const listPaneBox = await page.locator('.list-pane').boundingBox();
    expect(listPaneBox.width).toBeGreaterThan(200);
  });
});
