/**
 * Epic 3 Scene Presets - Integration Tests
 * Tests the deployed video-view-manager module on localhost:31000
 * Run with: npx playwright test _bmad-output/tests/integration/epic-3-scene-presets.spec.js
 * 
 * Note: Chrome DevTools must be running on port 9222 with FoundryVTT
 * Browser WSEndpoint: ws://localhost:9222/devtools/browser/1aeaf428-412f-4e20-9f2d-c13533d031ae
 */

import { chromium, expect, test } from '@playwright/test';

// Configuration
const CHROME_DEVTOOLS_URL = 'ws://localhost:9222/devtools/browser/1aeaf428-412f-4e20-9f2d-c13533d031ae';
const FOUNDRY_URL = 'https://localhost:31000/game';

// ============================================================================
// Helper Functions
// ============================================================================

async function waitForModule(page) {
  await page.waitForFunction(() => {
    const module = game?.modules?.get('video-view-manager');
    return module?.active === true;
  }, { timeout: 30000 });
}

async function waitForDirectorsBoard(page) {
  await page.waitForFunction(() => {
    const board = game?.modules?.get('video-view-manager')?._directorsBoard;
    return board?.rendered === true;
  }, { timeout: 10000 });
}

async function clickByDataAction(page, action) {
  await page.evaluate((action) => {
    const element = document.querySelector(`[data-action="${action}"]`);
    if (element) element.click();
  }, action);
}

async function getPresetCount(page) {
  return await page.evaluate(() => {
    const scene = game.scenes.current;
    const presets = scene?.getFlag('video-view-manager', 'presets') || {};
    return Object.keys(presets.presets || {}).length;
  });
}

async function verifyNotification(page, text) {
  await page.waitForFunction((text) => {
    const notifications = ui.notifications?.notifications || [];
    return notifications.some(n => n.message?.includes(text));
  }, { text, timeout: 5000 });
}

async function openDirectorsBoard(page) {
  await page.keyboard.down('Control');
  await page.keyboard.press('Shift+V');
  await page.keyboard.up('Control');
  await waitForDirectorsBoard(page);
}

// ============================================================================
// Test Suite: ScenePresetManager Core Functionality
// ============================================================================

test.describe('Epic 3 - ScenePresetManager Core', () => {
  let browser;
  let page;

  test.beforeAll(async () => {
    browser = await chromium.connect({
      wsEndpoint: CHROME_DEVTOOLS_URL
    });
    
    const pages = await browser.pages();
    page = pages.find(p => p.url().includes('localhost:31000/game')) || pages[0];
    
    if (!page.url().includes('localhost:31000')) {
      await page.goto(FOUNDRY_URL);
    }
    
    await waitForModule(page);
  });

  test.afterAll(async () => {
    await browser.close();
  });

  test('Module is active and ScenePresetManager exists', async () => {
    const isActive = await page.evaluate(() => {
      const module = game.modules.get('video-view-manager');
      return module?.active && module?.scenePresetManager;
    });
    expect(isActive).toBe(true);
  });

  test('Save and load preset via DirectorsBoard', async () => {
    await openDirectorsBoard(page);
    
    const initialCount = await getPresetCount(page);
    
    await clickByDataAction(page, 'save-preset');
    
    await page.waitForSelector('.dialog [name="presetName"]', { timeout: 5000 });
    await page.fill('.dialog [name="presetName"]', 'Epic 3 Test Preset');
    await page.click('.dialog button:has-text("Save")');
    
    await page.waitForTimeout(500);
    
    const newCount = await getPresetCount(page);
    expect(newCount).toBe(initialCount + 1);
    
    await clickByDataAction(page, 'load-preset');
    await page.waitForSelector('.dialog', { timeout: 5000 });
    await page.click('.dialog [data-preset-name="Epic 3 Test Preset"]');
    await page.click('.dialog button:has-text("Load")');
    
    await verifyNotification(page, 'applied preset: Epic 3 Test Preset');
  });

  test('Duplicate preset name shows confirmation dialog', async () => {
    await openDirectorsBoard(page);
    
    await clickByDataAction(page, 'save-preset');
    await page.waitForSelector('.dialog [name="presetName"]', { timeout: 5000 });
    await page.fill('.dialog [name="presetName"]', 'Epic 3 Test Preset');
    
    await page.waitForSelector('.dialog:has-text("already exists")', { timeout: 3000 });
  });
});

// ============================================================================
// Test Suite: ConfirmationBar & Auto-Apply
// ============================================================================

test.describe('Epic 3 - ConfirmationBar & Auto-Apply', () => {
  let browser;
  let page;

  test.beforeAll(async () => {
    browser = await chromium.connect({
      wsEndpoint: CHROME_DEVTOOLS_URL
    });
    
    const pages = await browser.pages();
    page = pages.find(p => p.url().includes('localhost:31000/game')) || pages[0];
    
    if (!page.url().includes('localhost:31000')) {
      await page.goto(FOUNDRY_URL);
    }
    
    await waitForModule(page);
  });

  test.afterAll(async () => {
    await browser.close();
  });

  test('ConfirmationBar appears after preset load', async () => {
    await openDirectorsBoard(page);
    
    await clickByDataAction(page, 'load-preset');
    await page.waitForSelector('.dialog', { timeout: 5000 });
    await page.click('.dialog [data-preset-name="Epic 3 Test Preset"]');
    await page.click('.dialog button:has-text("Load")');
    
    await page.waitForSelector('.sp-confirmation-bar', { timeout: 5000 });
    const barText = await page.$eval('.sp-confirmation-bar', el => el.textContent);
    expect(barText).toContain('Preset applied');
  });

  test('ConfirmationBar undo reverts matrix', async () => {
    const initialMatrix = await page.evaluate(() => {
      const controller = game.modules.get('video-view-manager')?._scryingPoolController;
      return JSON.stringify(controller?.visibilityMatrix);
    });
    
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'load-preset');
    await page.waitForSelector('.dialog', { timeout: 5000 });
    await page.click('.dialog [data-preset-name="Epic 3 Test Preset"]');
    await page.click('.dialog button:has-text("Load")');
    
    await page.waitForSelector('.sp-confirmation-bar', { timeout: 5000 });
    
    await page.click('.sp-confirmation-bar [data-action="undo"]');
    
    await page.waitForTimeout(500);
    const revertedMatrix = await page.evaluate(() => {
      const controller = game.modules.get('video-view-manager')?._scryingPoolController;
      return JSON.stringify(controller?.visibilityMatrix);
    });
    
    expect(revertedMatrix).toBe(initialMatrix);
  });

  test('ConfirmationBar auto-dismisses after 8 seconds', async () => {
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'load-preset');
    await page.waitForSelector('.dialog', { timeout: 5000 });
    await page.click('.dialog [data-preset-name="Epic 3 Test Preset"]');
    await page.click('.dialog button:has-text("Load")');
    
    await page.waitForSelector('.sp-confirmation-bar', { timeout: 5000 });
    
    // Note: This test may be flaky depending on timing
    // The bar should auto-dismiss after 8 seconds
    await page.waitForTimeout(8500);
    const isHidden = await page.evaluate(() => {
      const bar = document.querySelector('.sp-confirmation-bar');
      return !bar || bar.offsetParent === null;
    });
    expect(isHidden).toBe(true);
  });
});

// ============================================================================
// Test Suite: Import/Export
// ============================================================================

test.describe('Epic 3 - Preset Import/Export', () => {
  let browser;
  let page;

  test.beforeAll(async () => {
    browser = await chromium.connect({
      wsEndpoint: CHROME_DEVTOOLS_URL
    });
    
    const pages = await browser.pages();
    page = pages.find(p => p.url().includes('localhost:31000/game')) || pages[0];
    
    if (!page.url().includes('localhost:31000')) {
      await page.goto(FOUNDRY_URL);
    }
    
    await waitForModule(page);
  });

  test.afterAll(async () => {
    await browser.close();
  });

  test('Export presets downloads JSON file', async () => {
    await openDirectorsBoard(page);
    
    await clickByDataAction(page, 'export-presets');
    
    const download = await page.waitForEvent('download');
    const path = await download.path();
    const { readFileSync } = await import('fs');
    const contents = readFileSync(path, 'utf8');
    const data = JSON.parse(contents);
    
    expect(data._version).toBe(1);
    expect(data.presets).toBeDefined();
    expect(Object.keys(data.presets).length).toBeGreaterThan(0);
  });

  test('Import with merge adds new presets', async () => {
    // First export
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'export-presets');
    const download = await page.waitForEvent('download');
    const exportPath = await download.path();
    
    // Delete all presets
    await page.evaluate(() => {
      const scene = game.scenes.current;
      scene.unsetFlag('video-view-manager', 'presets');
    });
    
    // Import with merge
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'import-presets');
    
    const fileInput = page.locator('input[type="file"]');
    await fileInput.setInputFiles(exportPath);
    
    await page.click('text="Merge"');
    await page.click('button:has-text("Import")');
    
    await page.waitForTimeout(1000);
    const finalCount = await getPresetCount(page);
    expect(finalCount).toBeGreaterThan(0);
    
    await verifyNotification(page, 'Imported');
  });

  test('Import with invalid JSON shows error', async () => {
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'import-presets');
    
    const { writeFileSync } = await import('fs');
    const invalidJsonPath = '/tmp/invalid-presets.json';
    writeFileSync(invalidJsonPath, '{invalid json}');
    
    const fileInput = page.locator('input[type="file"]');
    await fileInput.setInputFiles(invalidJsonPath);
    
    await verifyNotification(page, 'Invalid JSON');
  });
});

// ============================================================================
// Test Suite: Edge Cases
// ============================================================================

test.describe('Epic 3 - Edge Cases', () => {
  let browser;
  let page;

  test.beforeAll(async () => {
    browser = await chromium.connect({
      wsEndpoint: CHROME_DEVTOOLS_URL
    });
    
    const pages = await browser.pages();
    page = pages.find(p => p.url().includes('localhost:31000/game')) || pages[0];
    
    if (!page.url().includes('localhost:31000')) {
      await page.goto(FOUNDRY_URL);
    }
    
    await waitForModule(page);
  });

  test.afterAll(async () => {
    await browser.close();
  });

  test('Load preset with no presets shows empty message', async () => {
    await page.evaluate(() => {
      const scene = game.scenes.current;
      scene.unsetFlag('video-view-manager', 'presets');
    });
    
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'load-preset');
    
    await page.waitForSelector('.dialog:has-text("No presets saved yet")', { timeout: 5000 });
  });

  test('Auto-apply respects per-scene disable', async () => {
    const hasSetting = await page.evaluate(() => {
      const scene = game.scenes.current;
      const autoApply = scene.getFlag('video-view-manager', 'autoApply') || {};
      return autoApply.enabled !== undefined;
    });
    
    expect(hasSetting).toBe(true);
  });

  test('Socket events fire correctly', async () => {
    await page.evaluate(() => {
      window.testEvents = [];
      Hooks.off('scrying-pool.preset.apply');
      Hooks.off('scrying-pool.preset.applied');
    });
    
    await page.evaluate(() => {
      Hooks.on('scrying-pool.preset.apply', () => window.testEvents.push('apply'));
      Hooks.on('scrying-pool.preset.applied', () => window.testEvents.push('applied'));
    });
    
    await openDirectorsBoard(page);
    await clickByDataAction(page, 'load-preset');
    await page.waitForSelector('.dialog', { timeout: 5000 });
    await page.click('.dialog [data-preset-name="Epic 3 Test Preset"]');
    await page.click('.dialog button:has-text("Load")');
    
    await page.waitForTimeout(1000);
    
    const events = await page.evaluate(() => window.testEvents);
    expect(events).toContain('apply');
    expect(events).toContain('applied');
  });
});
