/**
 * Test Utilities pour les tests E2E
 * 
 * Fournit des fonctions utilitaires pour :
 * - Créer des fixtures de test
 * - Nettoyer l'état
 * - Générer des données de test
 * - Aider au debugging
 */

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

/**
 * Crée un utilisateur de test dans FoundryVTT
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} username - Nom de l'utilisateur
 * @param {string} role - Rôle (Player, Game Master, etc.)
 * @param {string} password - Mot de passe
 * @returns {Promise<string>} L'ID de l'utilisateur créé
 */
export async function createTestUser(page, username, role = 'Player', password = 'test123') {
  // Naviguer vers la gestion des utilisateurs
  await page.goto('https://localhost:31000/setup/users', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  
  await page.waitForSelector('#users-list', { timeout: 10000 });
  
  // Vérifier si l'utilisateur existe déjà
  const userExists = await page.locator(`#users-list [data-username="${username}"]`).count();
  
  if (userExists > 0) {
    console.log(`⚠️  User "${username}" already exists`);
    return username;
  }
  
  // Cliquer sur Add User
  await page.locator('button:has-text("Add User")').click();
  await page.waitForSelector('#add-user-dialog', { timeout: 5000 });
  
  // Remplir le formulaire
  await page.locator('#add-user-dialog input[name="username"]').fill(username);
  await page.locator('#add-user-dialog input[name="password"]').fill(password);
  await page.locator('#add-user-dialog input[name="passwordConfirm"]').fill(password);
  await page.locator('#add-user-dialog select[name="role"]').selectOption(role);
  
  // Sauvegarder
  await page.locator('#add-user-dialog button:has-text("Add")').click();
  
  // Attendre la confirmation
  await page.waitForSelector(`#users-list [data-username="${username}"]`, {
    timeout: 10000,
  });
  
  console.log(`✅ Created user: ${username} (${role})`);
  return username;
}

/**
 * Crée une scène de test dans FoundryVTT
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} name - Nom de la scène
 * @returns {Promise<string>} L'ID de la scène créée
 */
export async function createTestScene(page, name) {
  await page.goto('https://localhost:31000/setup/scenes', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  
  await page.waitForSelector('#scenes-list', { timeout: 10000 });
  
  // Vérifier si la scène existe
  const sceneExists = await page.locator(`#scenes-list [data-scene-name="${name}"]`).count();
  
  if (sceneExists > 0) {
    console.log(`⚠️  Scene "${name}" already exists`);
    return name;
  }
  
  // Cliquer sur Create Scene
  await page.locator('button:has-text("Create Scene")').click();
  await page.waitForSelector('#create-scene-dialog', { timeout: 5000 });
  
  // Remplir le nom
  await page.locator('#create-scene-dialog input[name="name"]').fill(name);
  
  // Sauvegarder
  await page.locator('#create-scene-dialog button:has-text("Create")').click();
  
  // Attendre la confirmation
  await page.waitForSelector(`#scenes-list [data-scene-name="${name}"]`, {
    timeout: 10000,
  });
  
  console.log(`✅ Created scene: ${name}`);
  return name;
}

/**
 * Active une scène
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} name - Nom de la scène
 */
export async function activateScene(page, name) {
  await page.goto('https://localhost:31000/setup/scenes', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  
  await page.waitForSelector('#scenes-list', { timeout: 10000 });
  
  const sceneItem = page.locator(`#scenes-list [data-scene-name="${name}"]`);
  await sceneItem.hover();
  
  // Cliquer sur Activate
  await page.locator('.scene-actions:has-text("Activate")').click();
  
  // Attendre que la scène soit active
  await page.waitForFunction((sceneName) => {
    const scene = game.scenes?.getName(sceneName);
    return scene?.active === true;
  }, { timeout: 10000 }, name);
  
  console.log(`✅ Activated scene: ${name}`);
}

/**
 * Supprime un utilisateur de test
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} username - Nom de l'utilisateur
 */
export async function deleteTestUser(page, username) {
  await page.goto('https://localhost:31000/setup/users', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  
  await page.waitForSelector('#users-list', { timeout: 10000 });
  
  const userItem = page.locator(`#users-list [data-username="${username}"]`);
  
  if (await userItem.count() > 0) {
    await userItem.hover();
    await page.locator('.user-actions:has-text("Delete")').click();
    
    // Confirmer
    await page.waitForSelector('.delete-confirmation-dialog', { timeout: 5000 });
    await page.locator('.delete-confirmation-dialog button:has-text("Delete")').click();
    
    // Attendre la suppression
    await page.waitForSelector(`#users-list [data-username="${username}"]`, {
      state: 'detached',
      timeout: 10000,
    });
    
    console.log(`✅ Deleted user: ${username}`);
  } else {
    console.log(`⚠️  User "${username}" not found`);
  }
}

/**
 * Supprime une scène de test
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} name - Nom de la scène
 */
export async function deleteTestScene(page, name) {
  await page.goto('https://localhost:31000/setup/scenes', {
    waitUntil: 'domcontentloaded',
    timeout: 30000,
  });
  
  await page.waitForSelector('#scenes-list', { timeout: 10000 });
  
  const sceneItem = page.locator(`#scenes-list [data-scene-name="${name}"]`);
  
  if (await sceneItem.count() > 0) {
    await sceneItem.hover();
    await page.locator('.scene-actions:has-text("Delete")').click();
    
    // Confirmer
    await page.waitForSelector('.delete-confirmation-dialog', { timeout: 5000 });
    await page.locator('.delete-confirmation-dialog button:has-text("Delete")').click();
    
    // Attendre la suppression
    await page.waitForSelector(`#scenes-list [data-scene-name="${name}"]`, {
      state: 'detached',
      timeout: 10000,
    });
    
    console.log(`✅ Deleted scene: ${name}`);
  } else {
    console.log(`⚠️  Scene "${name}" not found`);
  }
}

/**
 * Capture un screenshot pour le debugging
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} name - Nom du screenshot
 */
export async function debugScreenshot(page, name) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `_bmad-output/e2e-screenshots/${name}-${timestamp}.png`;
  
  await page.screenshot({ path: filename, fullPage: true });
  console.log(`📸 Screenshot saved: ${filename}`);
}

/**
 * Capture une vidéo de la page
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {string} name - Nom de la vidéo
 */
export async function debugVideo(page, name) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `_bmad-output/e2e-videos/${name}-${timestamp}.webm`;
  
  await page.video()?.saveAs(filename);
  console.log(`🎥 Video saved: ${filename}`);
}

/**
 * Passe en mode debugging interactif
 * @param {import('@playwright/test').Page} page - La page Playwright
 */
export async function debugPause(page) {
  console.log('🛑 Test paused for debugging. Press any key to continue...');
  await page.pause();
}

/**
 * Affiche les informations de l'élément dans la console
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {import('@playwright/test').Locator} locator - Le locator
 */
export async function debugElement(page, locator) {
  const tagName = await locator.evaluate(el => el.tagName);
  const textContent = await locator.evaluate(el => el.textContent?.trim() || '');
  const className = await locator.evaluate(el => el.className);
  const id = await locator.evaluate(el => el.id);
  const boundingBox = await locator.boundingBox();
  
  console.log('🔍 Element Debug Info:');
  console.log(`   Tag: ${tagName}`);
  console.log(`   ID: ${id}`);
  console.log(`   Classes: ${className}`);
  console.log(`   Text: "${textContent}"`);
  console.log(`   Bounding Box: ${JSON.stringify(boundingBox)}`);
}

/**
 * Affiche les propriétés CSS d'un élément
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {import('@playwright/test').Locator} locator - Le locator
 */
export async function debugStyles(page, locator) {
  const styles = await locator.evaluate(el => {
    const computedStyle = getComputedStyle(el);
    const importantProps = [
      'display', 'position', 'visibility', 'opacity',
      'width', 'height', 'top', 'left', 'right', 'bottom',
      'color', 'background-color', 'font-size', 'z-index',
      'pointer-events'
    ];
    
    const result = {};
    for (const prop of importantProps) {
      result[prop] = computedStyle.getPropertyValue(prop);
    }
    
    return result;
  });
  
  console.log('🎨 CSS Properties:');
  for (const [prop, value] of Object.entries(styles)) {
    console.log(`   ${prop}: ${value}`);
  }
}

/**
 * Vérifie si un élément est visible avec plus de détails
 * @param {import('@playwright/test').Page} page - La page Playwright
 * @param {import('@playwright/test').Locator} locator - Le locator
 */
export async function debugVisibility(page, locator) {
  const isVisible = await locator.isVisible();
  const isInViewport = await locator.isInViewport();
  const boundingBox = await locator.boundingBox();
  const opacity = await locator.evaluate(el => {
    let current = el;
    let opacity = 1;
    while (current && current !== document.body) {
      const currentOpacity = parseFloat(getComputedStyle(current).opacity);
      opacity *= currentOpacity;
      current = current.parentElement;
    }
    return opacity;
  });
  
  console.log('👀 Visibility Debug:');
  console.log(`   Is Visible: ${isVisible}`);
  console.log(`   In Viewport: ${isInViewport}`);
  console.log(`   Bounding Box: ${JSON.stringify(boundingBox)}`);
  console.log(`   Opacity (accumulated): ${opacity}`);
}

/**
 * Génère un rapport HTML pour un test
 * @param {object} data - Données du rapport
 * @param {string} filename - Nom du fichier
 */
export async function generateTestReport(data, filename) {
  const fs = require('fs');
  const path = require('path');
  
  const reportPath = `_bmad-output/e2e-reports/${filename}`;
  
  // Assurer que le dossier existe
  const dir = path.dirname(reportPath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  
  const html = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Test Report: ${data.title}</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      background: #f5f5f5;
    }
    h1 { color: #333; border-bottom: 2px solid #4a90d9; padding-bottom: 10px; }
    h2 { color: #555; margin-top: 30px; }
    .passed { color: #2e7d32; font-weight: bold; }
    .failed { color: #c62828; font-weight: bold; }
    .skipped { color: #757575; font-style: italic; }
    .section { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
    .summary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; text-align: center; }
    pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; overflow-x: auto; }
    .metadata { color: #666; font-size: 14px; margin-top: 20px; }
  </style>
</head>
<body>
  <h1>📊 Test Report: ${data.title}</h1>
  
  <div class="summary">
    <h2>Summary</h2>
    <p>Total: ${data.total} | Passed: <span class="passed">${data.passed}</span> | Failed: <span class="failed">${data.failed}</span> | Skipped: <span class="skipped">${data.skipped}</span></p>
  </div>
  
  ${data.sections?.map(section => `
    <div class="section">
      <h2>📋 ${section.title}</h2>
      ${section.items?.map(item => `
        <p>${item.status === 'passed' ? '✅' : item.status === 'failed' ? '❌' : '⏭️'} ${item.name}</p>
      `).join('')}
    </div>
  `).join('')}
  
  ${data.error ? `
    <div class="section">
      <h2>❌ Error Details</h2>
      <pre>${escapeHtml(data.error.stack || data.error.message || String(data.error))}</pre>
    </div>
  ` : ''}
  
  ${data.logs ? `
    <div class="section">
      <h2>📝 Test Logs</h2>
      <pre>${escapeHtml(data.logs)}</pre>
    </div>
  ` : ''}
  
  <div class="metadata">
    Generated: ${new Date().toISOString()}
  </div>
</body>
</html>
`;
  
  fs.writeFileSync(reportPath, html);
  console.log(`📄 Report generated: ${reportPath}`);
}

/**
 * Échappe les caractères HTML
 * @param {string} text - Texte à échapper
 * @returns {string} Texte échappé
 */
function escapeHtml(text) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;',
  };
  return text.replace(/[&<>"']/g, m => map[m]);
}

/**
 * Mesure le temps d'exécution d'une fonction
 * @param {Function} fn - Fonction à mesurer
 * @returns {Promise<{result: any, duration: number}>}
 */
export async function measureTime(fn) {
  const start = performance.now();
  const result = await fn();
  const end = performance.now();
  return { result, duration: end - start };
}

/**
 * Attend qu'une condition soit vraie
 * @param {Function} condition - Fonction qui retourne un booléen
 * @param {number} timeout - Timeout en ms
 * @param {number} interval - Intervalle de vérification en ms
 * @returns {Promise<boolean>} Vrai si la condition est devenue vraie
 */
export async function waitForCondition(condition, timeout = 10000, interval = 500) {
  const start = Date.now();
  
  while (Date.now() - start < timeout) {
    try {
      if (await condition()) {
        return true;
      }
    } catch (e) {
      // Condition a lancé une erreur, continuer à essayer
    }
    
    await new Promise(resolve => setTimeout(resolve, interval));
  }
  
  return false;
}

/**
 * Essayez plusieurs sélecteurs jusqu'à ce qu'un fonctionne
 * @param {import('@playwright/test').Page} page - La page
 * @param {string[]} selectors - Tableau de sélecteurs
 * @param {object} options - Options pour waitForSelector
 * @returns {Promise<import('@playwright/test').Locator>}
 */
export async function waitForAnySelector(page, selectors, options = {}) {
  for (const selector of selectors) {
    try {
      await page.waitForSelector(selector, { ...options, timeout: options.timeout || 500 });
      return page.locator(selector);
    } catch (e) {
      // Essayez le prochain
    }
  }
  
  // Aucun sélecteur n'a fonctionné
  throw new Error(`None of the selectors matched: ${selectors.join(', ')}`);
}
