/**
 * Generate a detailed accessibility report for WCAG 2.1 AAA compliance
 * 
 * This script analyzes all pages and generates a comprehensive report
 * of accessibility issues that need to be fixed for AAA compliance
 */

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
const axeCore = require('axe-core');

// URLs to test - expand this list to cover all pages
const BASE_URL = process.argv[2] || 'http://localhost:8080'\;
const PAGES = [
  '/',
  '/stories/',
  '/stories/open-source-success.html',
  '/stories/viperwire.html',
  '/stories/airport-dns.html',
  '/stories/wordpress-security.html',
  '/stories/nitric-leadership.html',
  '/stories/healthcare-platform.html',
  '/stories/web-design-java.html',
  '/stories/motherboard-repair.html',
  '/stories/fawe-plotsquared.html',
  '/stories/showerloop.html',
  '/stories/athion-turnaround.html',
  '/stories/youtube-game-dev.html',
  '/stories/app-development.html',
  '/one-pager-tools/csv-tool.html'
];

// Create reports directory if it doesn't exist
const reportsDir = path.join(__dirname, '../reports');
if (!fs.existsSync(reportsDir)) {
  fs.mkdirSync(reportsDir, { recursive: true });
}

// Issues storage
const allIssues = {
  byPage: {},
  byRule: {},
  summary: {
    totalPages: 0,
    pagesWithIssues: 0,
    totalIssues: 0,
    issuesByImpact: {
      critical: 0,
      serious: 0,
      moderate: 0,
      minor: 0
    }
  }
};

async function runAxe(page, pageUrl) {
  try {
    // Use a file URL to load axe-core from a local file to avoid CSP issues
    const axeScriptPath = path.join(__dirname, 'axe-core.js');
    fs.writeFileSync(axeScriptPath, axeCore.source);
    
    // Add the script as a file
    await page.addScriptTag({ path: axeScriptPath });
    
    // Run axe with WCAG 2.1 AAA rules
    const results = await page.evaluate(() => {
      return new Promise(resolve => {
        axe.run(document, { 
          runOnly: { 
            type: 'tag', 
            values: ['wcag2aaa'] 
          },
          resultTypes: ['violations', 'incomplete'],
          rules: {
            'color-contrast': { enabled: true, options: { noScroll: true } }
          }
        })
        .then(results => resolve(results))
        .catch(err => resolve({ error: err.toString() }));
      });
    });
    
    // Clean up temporary file
    fs.unlinkSync(axeScriptPath);
    
    return results;
  } catch (error) {
    console.error('Error running axe:', error);
    return { error: error.toString() };
  }
}

async function testPage(browser, pageUrl) {
  const context = await browser.newContext({
    bypassCSP: true
  });
  const page = await context.newPage();
  console.log(`Testing ${pageUrl}...`);
  
  try {
    // Navigate to the page
    await page.goto(pageUrl, { waitUntil: 'networkidle' });
    
    // Run axe-core tests
    const results = await runAxe(page, pageUrl);
    
    // Save results to file
    const fileName = pageUrl === BASE_URL ? 'index' : pageUrl.replace(BASE_URL, '').replace(/\//g, '-').replace(/^-/, '');
    const reportPath = path.join(reportsDir, `axe-${fileName || 'index'}.json`);
    fs.writeFileSync(reportPath, JSON.stringify(results, null, 2));
    
    // Log results summary
    if (results.error) {
      console.error(`Error running axe-core on ${pageUrl}:`, results.error);
      return { url: pageUrl, success: false, error: results.error };
    }
    
    const { violations, incomplete } = results;
    console.log(`Results for ${pageUrl}:`);
    console.log(`- Violations: ${violations.length}`);
    console.log(`- Incomplete: ${incomplete.length}`);
    
    // Track issues for detailed report
    const pageIssues = [];
    
    // Process violations
    if (violations.length > 0) {
      console.log('\nViolations:');
      violations.forEach((violation, i) => {
        console.log(`${i + 1}. ${violation.id} - ${violation.help} (Impact: ${violation.impact})`);
        console.log(`   ${violation.description}`);
        console.log(`   WCAG: ${violation.tags.filter(t => t.startsWith('wcag')).join(', ')}`);
        console.log(`   Elements: ${violation.nodes.length}`);
        
        // Add to issues tracking
        violation.nodes.forEach(node => {
          const issue = {
            rule: violation.id,
            impact: violation.impact,
            description: violation.description,
            help: violation.help,
            helpUrl: violation.helpUrl,
            wcagTags: violation.tags.filter(t => t.startsWith('wcag')),
            element: node.html,
            target: node.target,
            failureSummary: node.failureSummary,
            elementData: node.any.map(d => d.data || {})
          };
          pageIssues.push(issue);
          
          // Update rule-based tracking
          if (!allIssues.byRule[violation.id]) {
            allIssues.byRule[violation.id] = {
              id: violation.id,
              description: violation.description,
              help: violation.help,
              helpUrl: violation.helpUrl,
              impact: violation.impact,
              wcagTags: violation.tags.filter(t => t.startsWith('wcag')),
              occurrences: []
            };
          }
          
          allIssues.byRule[violation.id].occurrences.push({
            page: pageUrl,
            element: node.html,
            target: node.target,
            failureSummary: node.failureSummary,
            elementData: node.any.map(d => d.data || {})
          });
          
          // Update summary stats
          allIssues.summary.totalIssues++;
          if (violation.impact) {
            allIssues.summary.issuesByImpact[violation.impact]++;
          }
        });
      });
    }
    
    // Store page issues
    allIssues.byPage[pageUrl] = {
      url: pageUrl,
      issueCount: pageIssues.length,
      issues: pageIssues
    };
    
    // Update summary
    allIssues.summary.totalPages++;
    if (pageIssues.length > 0) {
      allIssues.summary.pagesWithIssues++;
    }
    
    return { 
      url: pageUrl, 
      success: violations.length === 0, 
      violations: violations.length,
      incomplete: incomplete.length
    };
  } catch (error) {
    console.error(`Error testing ${pageUrl}:`, error);
    return { url: pageUrl, success: false, error: error.toString() };
  } finally {
    await page.close();
    await context.close();
  }
}

function generateFixRecommendations() {
  const recommendations = {};
  
  // Process color contrast issues
  if (allIssues.byRule['color-contrast-enhanced']) {
    const colorIssues = allIssues.byRule['color-contrast-enhanced'];
    recommendations.colorContrast = {
      title: 'Color Contrast Issues',
      description: 'The following color combinations need to be updated to meet WCAG 2.1 AAA requirements (7:1 contrast ratio):',
      colorPairs: []
    };
    
    // Group by color pairs
    const colorPairMap = {};
    colorIssues.occurrences.forEach(occurrence => {
      occurrence.elementData.forEach(data => {
        if (data.fgColor && data.bgColor) {
          const key = `${data.fgColor}/${data.bgColor}`;
          if (!colorPairMap[key]) {
            colorPairMap[key] = {
              fgColor: data.fgColor,
              bgColor: data.bgColor,
              contrastRatio: data.contrastRatio,
              requiredRatio: data.expectedContrastRatio,
              pages: new Set(),
              elements: []
            };
          }
          colorPairMap[key].pages.add(occurrence.page);
          colorPairMap[key].elements.push({
            html: occurrence.element,
            page: occurrence.page
          });
        }
      });
    });
    
    // Convert to array and add suggestions
    Object.values(colorPairMap).forEach(pair => {
      const suggestion = {
        foreground: pair.fgColor,
        background: pair.bgColor,
        currentRatio: pair.contrastRatio,
        requiredRatio: pair.requiredRatio,
        pagesAffected: Array.from(pair.pages),
        elementCount: pair.elements.length,
        suggestedFixes: []
      };
      
      // Generate suggested fixes
      // Make foreground darker
      const darkerFg = darkenColor(pair.fgColor, 0.2);
      suggestion.suggestedFixes.push({
        type: 'Darken foreground',
        color: darkerFg,
        description: `Change foreground color from ${pair.fgColor} to ${darkerFg}`
      });
      
      // Make background lighter
      const lighterBg = lightenColor(pair.bgColor, 0.2);
      suggestion.suggestedFixes.push({
        type: 'Lighten background',
        color: lighterBg,
        description: `Change background color from ${pair.bgColor} to ${lighterBg}`
      });
      
      recommendations.colorContrast.colorPairs.push(suggestion);
    });
  }
  
  return recommendations;
}

// Helper function to darken a hex color
function darkenColor(hex, amount) {
  return adjustColor(hex, -amount);
}

// Helper function to lighten a hex color
function lightenColor(hex, amount) {
  return adjustColor(hex, amount);
}

// Helper function to adjust a hex color
function adjustColor(hex, amount) {
  let r = parseInt(hex.substring(1, 3), 16);
  let g = parseInt(hex.substring(3, 5), 16);
  let b = parseInt(hex.substring(5, 7), 16);
  
  if (amount > 0) {
    // Lighten
    r = Math.min(255, Math.round(r + (255 - r) * amount));
    g = Math.min(255, Math.round(g + (255 - g) * amount));
    b = Math.min(255, Math.round(b + (255 - b) * amount));
  } else {
    // Darken
    amount = -amount;
    r = Math.max(0, Math.round(r * (1 - amount)));
    g = Math.max(0, Math.round(g * (1 - amount)));
    b = Math.max(0, Math.round(b * (1 - amount)));
  }
  
  return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}

async function runTests() {
  const browser = await chromium.launch();
  const results = [];
  
  try {
    // Test each page
    for (const pagePath of PAGES) {
      const pageUrl = `${BASE_URL}${pagePath}`;
      const result = await testPage(browser, pageUrl);
      results.push(result);
    }
    
    // Generate detailed report
    const recommendations = generateFixRecommendations();
    
    const detailedReport = {
      timestamp: new Date().toISOString(),
      baseUrl: BASE_URL,
      summary: allIssues.summary,
      recommendations: recommendations,
      issuesByRule: allIssues.byRule
    };
    
    // Save detailed report
    fs.writeFileSync(
      path.join(reportsDir, 'accessibility-detailed-report.json'), 
      JSON.stringify(detailedReport, null, 2)
    );
    
    // Generate HTML report
    const htmlReport = generateHtmlReport(detailedReport);
    fs.writeFileSync(
      path.join(reportsDir, 'accessibility-report.html'), 
      htmlReport
    );
    
    console.log('\n=== Detailed Report Generated ===');
    console.log(`Total pages tested: ${detailedReport.summary.totalPages}`);
    console.log(`Pages with issues: ${detailedReport.summary.pagesWithIssues}`);
    console.log(`Total issues found: ${detailedReport.summary.totalIssues}`);
    console.log(`Issues by impact:`);
    Object.entries(detailedReport.summary.issuesByImpact).forEach(([impact, count]) => {
      if (count > 0) {
        console.log(`  - ${impact}: ${count}`);
      }
    });
    console.log(`\nDetailed report saved to: ${path.join(reportsDir, 'accessibility-detailed-report.json')}`);
    console.log(`HTML report saved to: ${path.join(reportsDir, 'accessibility-report.html')}`);
    
    // Exit with appropriate code
    process.exit(detailedReport.summary.totalIssues > 0 ? 1 : 0);
  } catch (error) {
    console.error('Error running tests:', error);
    process.exit(1);
  } finally {
    await browser.close();
  }
}

function generateHtmlReport(report) {
  const colorPairs = report.recommendations.colorContrast ? report.recommendations.colorContrast.colorPairs : [];
  
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Accessibility Report - WCAG 2.1 AAA Compliance</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      line-height: 1.6;
      color: #333;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
    }
    h1, h2, h3, h4 {
      color: #2c3e50;
    }
    .summary {
      background-color: #f8f9fa;
      border-radius: 5px;
      padding: 20px;
      margin-bottom: 30px;
    }
    .issue-count {
      font-size: 24px;
      font-weight: bold;
    }
    .color-pair {
      margin-bottom: 30px;
      border: 1px solid #ddd;
      padding: 15px;
      border-radius: 5px;
    }
    .color-sample {
      display: inline-block;
      width: 100px;
      height: 50px;
      margin-right: 10px;
      border: 1px solid #ddd;
    }
    .color-info {
      display: inline-block;
      vertical-align: top;
    }
    .suggestion {
      margin-top: 10px;
      padding: 10px;
      background-color: #f0f7ff;
      border-radius: 5px;
    }
    .pages-list {
      margin-top: 10px;
      font-size: 14px;
    }
    .impact-critical { color: #d9534f; }
    .impact-serious { color: #f0ad4e; }
    .impact-moderate { color: #5bc0de; }
    .impact-minor { color: #5cb85c; }
    table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 20px;
    }
    th, td {
      border: 1px solid #ddd;
      padding: 8px;
      text-align: left;
    }
    th {
      background-color: #f2f2f2;
    }
    tr:nth-child(even) {
      background-color: #f9f9f9;
    }
  </style>
</head>
<body>
  <h1>Accessibility Report - WCAG 2.1 AAA Compliance</h1>
  <p>Generated on: ${new Date(report.timestamp).toLocaleString()}</p>
  
  <div class="summary">
    <h2>Summary</h2>
    <p><span class="issue-count">${report.summary.totalIssues}</span> issues found across ${report.summary.pagesWithIssues} pages (${report.summary.totalPages} pages tested)</p>
    <p>Issues by impact:</p>
    <ul>
      ${report.summary.issuesByImpact.critical > 0 ? `<li><span class="impact-critical">Critical: ${report.summary.issuesByImpact.critical}</span></li>` : ''}
      ${report.summary.issuesByImpact.serious > 0 ? `<li><span class="impact-serious">Serious: ${report.summary.issuesByImpact.serious}</span></li>` : ''}
      ${report.summary.issuesByImpact.moderate > 0 ? `<li><span class="impact-moderate">Moderate: ${report.summary.issuesByImpact.moderate}</span></li>` : ''}
      ${report.summary.issuesByImpact.minor > 0 ? `<li><span class="impact-minor">Minor: ${report.summary.issuesByImpact.minor}</span></li>` : ''}
    </ul>
  </div>
  
  <h2>Color Contrast Issues</h2>
  ${colorPairs.length === 0 ? '<p>No color contrast issues found.</p>' : ''}
  ${colorPairs.map(pair => `
    <div class="color-pair">
      <h3>Color Combination</h3>
      <div>
        <div class="color-sample" style="background-color: ${pair.foreground}"></div>
        <div class="color-info">
          <p>Foreground: ${pair.foreground}</p>
        </div>
      </div>
      <div>
        <div class="color-sample" style="background-color: ${pair.background}"></div>
        <div class="color-info">
          <p>Background: ${pair.background}</p>
        </div>
      </div>
      <p>Current contrast ratio: ${pair.currentRatio} (Required: ${pair.requiredRatio})</p>
      <p>Found on ${pair.elementCount} element${pair.elementCount !== 1 ? 's' : ''} across ${pair.pagesAffected.length} page${pair.pagesAffected.length !== 1 ? 's' : ''}</p>
      
      <div class="pages-list">
        <p>Pages affected:</p>
        <ul>
          ${pair.pagesAffected.map(page => `<li>${page}</li>`).join('')}
        </ul>
      </div>
      
      <h4>Suggested Fixes</h4>
      ${pair.suggestedFixes.map(fix => `
        <div class="suggestion">
          <h5>${fix.type}</h5>
          <div class="color-sample" style="background-color: ${fix.color}"></div>
          <div class="color-info">
            <p>${fix.description}</p>
          </div>
        </div>
      `).join('')}
    </div>
  `).join('')}
  
  <h2>Issues by Rule</h2>
  <table>
    <thead>
      <tr>
        <th>Rule</th>
        <th>Impact</th>
        <th>Description</th>
        <th>Occurrences</th>
        <th>WCAG Criteria</th>
      </tr>
    </thead>
    <tbody>
      ${Object.values(report.issuesByRule).map(rule => `
        <tr>
          <td><a href="${rule.helpUrl}" target="_blank">${rule.id}</a></td>
          <td class="impact-${rule.impact}">${rule.impact}</td>
          <td>${rule.description}</td>
          <td>${rule.occurrences.length}</td>
          <td>${rule.wcagTags.join(', ')}</td>
        </tr>
      `).join('')}
    </tbody>
  </table>
</body>
</html>`;
}

runTests();
