/**
 * Epic 1: Core Camera Visibility Control - Tests E2E
 * 
 * FR-1: GM toggles Participant visibility via right-click context menu
 * FR-2: All Visibility Matrix changes are broadcast to all connected clients
 * FR-3: Visibility Matrix state persists across page refreshes
 * FR-4: AV Tile visual indicator distinguishes all Participant States
 * FR-5: All eight Participant States render without layout disruption
 * FR-6: GM always sees all activated Participant feeds
 * FR-7: WebRTC track disabling (CSS fallback for v14)
 * FR-8: Portrait Fallback displayed when Participant has no camera
 * FR-22: Persistent feed status badge on own AV Tile
 */

import { test, expect } from '@playwright/test';
import {
  waitForFoundryReady,
  waitForVVMModule,
  hideParticipant,
  showParticipant,
  verifyAVTileState,
  clickFoundryButton,
} from '../utils/foundry-helpers';

// Données de test - à adapter selon votre environnement
const TEST_PLAYERS = [
  { name: 'Player1', id: 'player1' },
  { name: 'Player2', id: 'player2' },
];

// Configuration du test
test.describe('Epic 1: Core Camera Visibility Control', () => {
  // Timeout plus long pour Foundry
  test.setTimeout(120000);

  test.beforeEach(async ({ page }) => {
    // Naviguer vers FoundryVTT
    await page.goto('https://localhost:31000', {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });
    
    // Attendre que Foundry soit prêt
    await waitForFoundryReady(page);
    
    // Attendre que le module soit chargé
    await waitForVVMModule(page);
    
    // Vérifier qu'on est en mode GM
    const isGM = await page.evaluate(() => game.user?.isGM || false);
    if (!isGM) {
      test.skip(true, 'Test requires GM user');
    }
  });

  // ============================================================================
  // FR-1: GM toggles Participant visibility via right-click context menu
  // ============================================================================
  test.describe('FR-1: Hide/Show Participant via Context Menu', () => {
    test('GM can hide a participant from the table via right-click', async ({ page }) => {
      // Attendre qu'une tuile AV soit présente
      await page.waitForSelector('.av-tile', { state: 'visible', timeout: 10000 });
      
      const participantName = 'Player1';
      
      // Cacher le participant
      await hideParticipant(page, participantName);
      
      // Vérifier que le badge montre "Hidden from table"
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      await expect(tile.locator('.sp-visibility-badge')).toContainText('Hidden from table', {
        timeout: 5000,
      });
    });

    test('GM can show a hidden participant via right-click', async ({ page }) => {
      const participantName = 'Player1';
      
      // D'abord cacher
      await hideParticipant(page, participantName);
      
      // Puis montrer
      await showParticipant(page, participantName);
      
      // Vérifier que le badge montre "Live"
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      await expect(tile.locator('.sp-visibility-badge')).toContainText('Live', {
        timeout: 5000,
      });
    });

    test('Visibility change is immediate (no layout shift)', async ({ page }) => {
      const participantName = 'Player1';
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      
      // Capturer la position avant
      const beforeBox = await tile.boundingBox();
      
      // Changer la visibilité
      await hideParticipant(page, participantName);
      
      // Capturer la position après
      const afterBox = await tile.boundingBox();
      
      // Vérifier qu'il n'y a pas de shift de layout
      expect(afterBox.x).toBeCloseTo(beforeBox.x, 1);
      expect(afterBox.y).toBeCloseTo(beforeBox.y, 1);
    });
  });

  // ============================================================================
  // FR-4: AV Tile visual indicator distinguishes all Participant States
  // ============================================================================
  test.describe('FR-4: AV Tile Visual State Indicators', () => {
    test('Hidden state shows grey overlay + lock icon + tooltip', async ({ page }) => {
      const participantName = 'Player1';
      
      // Cacher le participant
      await hideParticipant(page, participantName);
      
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      
      // Vérifier l'overlay gris
      const overlay = tile.locator('.sp-visibility-overlay');
      await expect(overlay).toBeVisible();
      
      // Vérifier l'icône de lock
      const lockIcon = tile.locator('.fa-lock');
      await expect(lockIcon).toBeVisible();
    });

    test('Self-muted state shows camera-off icon', async ({ page }) => {
      // Simuler l'état self-muted via le module
      // Note: Cela nécessite une implémentation spécifique ou un mock
      // Pour les tests E2E, on peut utiliser l'API du module directement
      await page.evaluate((userId) => {
        if (game.modules.get('scrying-pool')) {
          // Appeler l'API interne si disponible
          const controller = game.modules.get('scrying-pool').api?.controller;
          if (controller) {
            controller.action('test', userId, 'self-muted');
          }
        }
      }, 'player1');
      
      const tile = page.locator(`.av-tile:has-text("Player1")`);
      const badge = tile.locator('.sp-visibility-badge');
      await expect(badge).toContainText('Camera paused');
    });

    test('Active state shows no overlay or indicator', async ({ page }) => {
      const participantName = 'Player1';
      
      // S'assurer que le participant est visible
      await showParticipant(page, participantName);
      
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      
      // Vérifier qu'il n'y a pas d'overlay
      const overlay = tile.locator('.sp-visibility-overlay');
      await expect(overlay).not.toBeVisible();
    });
  });

  // ============================================================================
  // FR-6: GM always sees all activated Participant feeds
  // ============================================================================
  test.describe('FR-6: GM Visibility Guarantee', () => {
    test('GM can see all participant tiles regardless of visibility state', async ({ page }) => {
      // Cacher plusieurs participants
      for (const player of TEST_PLAYERS) {
        await hideParticipant(page, player.name);
      }
      
      // Vérifier que toutes les tuiles sont toujours visibles pour le GM
      for (const player of TEST_PLAYERS) {
        const tile = page.locator(`.av-tile:has-text("${player.name}")`);
        await expect(tile).toBeVisible({ timeout: 5000 });
        
        // Vérifier qu'elles ont l'overlay "hidden" mais sont visibles
        const overlay = tile.locator('.sp-visibility-overlay');
        await expect(overlay).toBeVisible();
      }
    });

    test('Hidden tiles have reduced opacity for GM', async ({ page }) => {
      const participantName = 'Player1';
      await hideParticipant(page, participantName);
      
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      
      // Vérifier l'opacité réduite via CSS
      const opacity = await tile.evaluate(el => {
        return window.getComputedStyle(el).opacity;
      });
      
      // L'opacité devrait être réduite (par exemple 0.5)
      expect(parseFloat(opacity)).toBeLessThan(1);
    });
  });

  // ============================================================================
  // FR-8: Portrait Fallback displayed when Participant has no camera
  // ============================================================================
  test.describe('FR-8: Portrait Fallback', () => {
    test('Never-connected participants show portrait fallback', async ({ page }) => {
      // Un participant qui n'a jamais connecté son caméra
      // devrait montrer le fallback
      const tile = page.locator('.av-tile[data-state="never-connected"]');
      
      if (await tile.count() > 0) {
        const img = tile.locator('img.sp-portrait-fallback');
        await expect(img).toBeVisible({ timeout: 5000 });
      } else {
        test.skip(true, 'No never-connected participants in test');
      }
    });

    test('Cam-lost participants show portrait fallback', async ({ page }) => {
      // Simuler la perte de caméra
      await page.evaluate((userId) => {
        if (game.modules.get('scrying-pool')) {
          const controller = game.modules.get('scrying-pool').api?.controller;
          if (controller) {
            controller.action('test', userId, 'cam-lost');
          }
        }
      }, 'player1');
      
      const tile = page.locator(`.av-tile:has-text("Player1")`);
      const img = tile.locator('img.sp-portrait-fallback');
      await expect(img).toBeVisible({ timeout: 5000 });
    });
  });

  // ============================================================================
  // FR-22: Persistent feed status badge on own AV Tile
  // ============================================================================
  test.describe('FR-22: Player Self Status Badge', () => {
    test('Own AV Tile shows Live badge when visible', async ({ page }) => {
      const tile = page.locator('.av-tile.self');
      const badge = tile.locator('.sp-visibility-badge');
      await expect(badge).toContainText('Live');
    });

    test('Own AV Tile shows Hidden by GM when hidden', async ({ page }) => {
      // Se cacher soi-même (si c'est un joueur) ou simuler
      // Pour les tests GM, nous pouvons vérifier sur un autre utilisateur
      // qui a été caché
      const participantName = 'Player1';
      await hideParticipant(page, participantName);
      
      const tile = page.locator(`.av-tile:has-text("${participantName}")`);
      const badge = tile.locator('.sp-visibility-badge');
      await expect(badge).toContainText('Hidden by GM');
    });

    test('First encounter shows explanatory tooltip', async ({ page }) => {
      // Effacer le flag de premier badge
      await page.evaluate(() => {
        game.user?.unsetFlag('scrying-pool', 'firstBadgeEncounter');
      });
      
      // Recharger la page pour déclencher le first encounter
      await page.reload();
      await waitForFoundryReady(page);
      await waitForVVMModule(page);
      
      // Vérifier que le FirstEncounterPanel apparaît
      await page.waitForSelector('.sp-first-encounter-panel', {
        state: 'visible',
        timeout: 10000,
      });
    });
  });
});
