/**
 * Structured Data Validation Script
 *
 * Validates JSON-LD structured data in built HTML files
 * Run: node .agents/seo-specialist/validate-structured-data.js
 */

import { readFileSync, readdirSync, statSync } from 'fs';
import { join, extname } from 'path';

const distDir = './dist';
const errors = [];
const warnings = [];
const schemaStats = {};

// ANSI color codes for terminal output
const colors = {
  reset: '\x1b[0m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  red: '\x1b[31m',
  cyan: '\x1b[36m',
  bold: '\x1b[1m'
};

function log(message, color = 'reset') {
  console.log(`${colors[color]}${message}${colors.reset}`);
}

/**
 * Recursively find all HTML files in dist directory
 */
function findHtmlFiles(dir) {
  const files = [];

  try {
    const items = readdirSync(dir);

    for (const item of items) {
      const fullPath = join(dir, item);
      const stat = statSync(fullPath);

      if (stat.isDirectory()) {
        files.push(...findHtmlFiles(fullPath));
      } else if (extname(item) === '.html') {
        files.push(fullPath);
      }
    }
  } catch (err) {
    errors.push(`Error reading directory ${dir}: ${err.message}`);
  }

  return files;
}

/**
 * Extract and validate JSON-LD schemas from HTML content
 */
function extractSchemas(html, filePath) {
  const schemaRegex = /<script type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/gi;
  const matches = [...html.matchAll(schemaRegex)];
  const schemas = [];

  for (const match of matches) {
    try {
      const jsonContent = match[1].trim();
      const schema = JSON.parse(jsonContent);
      schemas.push(schema);

      // Track schema types
      const type = schema['@type'];
      if (type) {
        schemaStats[type] = (schemaStats[type] || 0) + 1;
      }

    } catch (err) {
      errors.push({
        file: filePath,
        type: 'PARSE_ERROR',
        message: `Invalid JSON-LD: ${err.message}`,
        content: match[1].substring(0, 100) + '...'
      });
    }
  }

  return schemas;
}

/**
 * Validate Organization schema
 */
function validateOrganization(schema, filePath) {
  const required = ['@context', '@type', 'name', 'url'];
  const recommended = ['logo', 'description', 'contactPoint', 'sameAs', 'address'];

  // Check required fields
  for (const field of required) {
    if (!schema[field]) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'Organization',
        field
      });
    }
  }

  // Check recommended fields
  for (const field of recommended) {
    if (!schema[field]) {
      warnings.push({
        file: filePath,
        type: 'MISSING_RECOMMENDED',
        schema: 'Organization',
        field
      });
    }
  }

  // Validate logo is ImageObject
  if (schema.logo && typeof schema.logo === 'object') {
    if (!schema.logo['@type'] || schema.logo['@type'] !== 'ImageObject') {
      warnings.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'Organization',
        message: 'Logo should be an ImageObject'
      });
    }
  }
}

/**
 * Validate BlogPosting schema
 */
function validateBlogPosting(schema, filePath) {
  const required = ['@context', '@type', 'headline', 'author', 'datePublished', 'publisher'];
  const recommended = ['image', 'dateModified', 'mainEntityOfPage', 'keywords'];

  for (const field of required) {
    if (!schema[field]) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'BlogPosting',
        field
      });
    }
  }

  for (const field of recommended) {
    if (!schema[field]) {
      warnings.push({
        file: filePath,
        type: 'MISSING_RECOMMENDED',
        schema: 'BlogPosting',
        field
      });
    }
  }

  // Validate author is Person
  if (schema.author && typeof schema.author === 'object') {
    if (!schema.author['@type'] || schema.author['@type'] !== 'Person') {
      errors.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'BlogPosting',
        message: 'Author must be a Person schema'
      });
    }
  }

  // Validate publisher is Organization with logo
  if (schema.publisher && typeof schema.publisher === 'object') {
    if (!schema.publisher['@type'] || schema.publisher['@type'] !== 'Organization') {
      errors.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'BlogPosting',
        message: 'Publisher must be an Organization schema'
      });
    }
    if (!schema.publisher.logo) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'BlogPosting',
        message: 'Publisher must have a logo'
      });
    }
  }
}

/**
 * Validate BreadcrumbList schema
 */
function validateBreadcrumbList(schema, filePath) {
  if (!schema.itemListElement || !Array.isArray(schema.itemListElement)) {
    errors.push({
      file: filePath,
      type: 'MISSING_REQUIRED',
      schema: 'BreadcrumbList',
      field: 'itemListElement (must be array)'
    });
    return;
  }

  schema.itemListElement.forEach((item, index) => {
    if (!item['@type'] || item['@type'] !== 'ListItem') {
      errors.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'BreadcrumbList',
        message: `Item ${index} must be ListItem`
      });
    }

    if (!item.position) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'BreadcrumbList',
        message: `Item ${index} missing position`
      });
    }

    if (!item.name) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'BreadcrumbList',
        message: `Item ${index} missing name`
      });
    }
  });
}

/**
 * Validate FAQPage schema
 */
function validateFAQPage(schema, filePath) {
  if (!schema.mainEntity || !Array.isArray(schema.mainEntity)) {
    errors.push({
      file: filePath,
      type: 'MISSING_REQUIRED',
      schema: 'FAQPage',
      field: 'mainEntity (must be array)'
    });
    return;
  }

  schema.mainEntity.forEach((item, index) => {
    if (!item['@type'] || item['@type'] !== 'Question') {
      errors.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'FAQPage',
        message: `Item ${index} must be Question`
      });
    }

    if (!item.name) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'FAQPage',
        message: `Question ${index} missing name`
      });
    }

    if (!item.acceptedAnswer) {
      errors.push({
        file: filePath,
        type: 'MISSING_REQUIRED',
        schema: 'FAQPage',
        message: `Question ${index} missing acceptedAnswer`
      });
    } else if (!item.acceptedAnswer['@type'] || item.acceptedAnswer['@type'] !== 'Answer') {
      errors.push({
        file: filePath,
        type: 'INVALID_TYPE',
        schema: 'FAQPage',
        message: `Question ${index} acceptedAnswer must be Answer`
      });
    }
  });
}

/**
 * Validate schema based on type
 */
function validateSchema(schema, filePath) {
  const type = schema['@type'];

  switch (type) {
    case 'Organization':
      validateOrganization(schema, filePath);
      break;
    case 'BlogPosting':
      validateBlogPosting(schema, filePath);
      break;
    case 'BreadcrumbList':
      validateBreadcrumbList(schema, filePath);
      break;
    case 'FAQPage':
      validateFAQPage(schema, filePath);
      break;
    // Other schema types can pass through without specific validation
    default:
      // Just check for @context and @type
      if (!schema['@context']) {
        errors.push({
          file: filePath,
          type: 'MISSING_REQUIRED',
          schema: type,
          field: '@context'
        });
      }
  }
}

/**
 * Check for Open Graph tags
 */
function validateOpenGraph(html, filePath) {
  const requiredOgTags = ['og:title', 'og:description', 'og:url', 'og:image'];

  for (const tag of requiredOgTags) {
    const regex = new RegExp(`<meta\\s+property="${tag}"`, 'i');
    if (!regex.test(html)) {
      errors.push({
        file: filePath,
        type: 'MISSING_OG_TAG',
        tag
      });
    }
  }
}

/**
 * Check for Twitter Card tags
 */
function validateTwitterCards(html, filePath) {
  const requiredTwitterTags = ['twitter:card', 'twitter:title', 'twitter:description', 'twitter:image'];

  for (const tag of requiredTwitterTags) {
    const regex = new RegExp(`<meta\\s+name="${tag}"`, 'i');
    if (!regex.test(html)) {
      errors.push({
        file: filePath,
        type: 'MISSING_TWITTER_TAG',
        tag
      });
    }
  }
}

/**
 * Main validation function
 */
function validateFile(filePath) {
  try {
    const html = readFileSync(filePath, 'utf-8');

    // Extract and validate JSON-LD schemas
    const schemas = extractSchemas(html, filePath);

    for (const schema of schemas) {
      validateSchema(schema, filePath);
    }

    // Validate Open Graph
    validateOpenGraph(html, filePath);

    // Validate Twitter Cards
    validateTwitterCards(html, filePath);

    return schemas.length;

  } catch (err) {
    errors.push({
      file: filePath,
      type: 'FILE_ERROR',
      message: err.message
    });
    return 0;
  }
}

/**
 * Print validation report
 */
function printReport(filesProcessed, totalSchemas) {
  console.log('\n' + '='.repeat(80));
  log('STRUCTURED DATA VALIDATION REPORT', 'bold');
  console.log('='.repeat(80) + '\n');

  log(`Files Processed: ${filesProcessed}`, 'cyan');
  log(`Total Schemas Found: ${totalSchemas}`, 'cyan');
  console.log();

  // Schema type breakdown
  log('Schema Types:', 'bold');
  for (const [type, count] of Object.entries(schemaStats).sort((a, b) => b[1] - a[1])) {
    log(`  ${type}: ${count}`, 'cyan');
  }
  console.log();

  // Errors
  if (errors.length > 0) {
    log(`❌ ERRORS (${errors.length}):`, 'red');
    errors.forEach((error, index) => {
      console.log(`\n${index + 1}. ${error.file || 'Unknown'}`);
      log(`   Type: ${error.type}`, 'red');
      if (error.schema) log(`   Schema: ${error.schema}`, 'red');
      if (error.field) log(`   Field: ${error.field}`, 'red');
      if (error.tag) log(`   Tag: ${error.tag}`, 'red');
      if (error.message) log(`   Message: ${error.message}`, 'red');
      if (error.content) log(`   Content: ${error.content}`, 'red');
    });
    console.log();
  }

  // Warnings
  if (warnings.length > 0) {
    log(`⚠️  WARNINGS (${warnings.length}):`, 'yellow');
    warnings.forEach((warning, index) => {
      console.log(`\n${index + 1}. ${warning.file || 'Unknown'}`);
      log(`   Type: ${warning.type}`, 'yellow');
      if (warning.schema) log(`   Schema: ${warning.schema}`, 'yellow');
      if (warning.field) log(`   Field: ${warning.field}`, 'yellow');
      if (warning.message) log(`   Message: ${warning.message}`, 'yellow');
    });
    console.log();
  }

  // Summary
  console.log('='.repeat(80));
  if (errors.length === 0 && warnings.length === 0) {
    log('✅ ALL VALIDATIONS PASSED!', 'green');
  } else if (errors.length === 0) {
    log('✅ NO ERRORS (but some warnings)', 'green');
  } else {
    log('❌ VALIDATION FAILED', 'red');
  }
  console.log('='.repeat(80) + '\n');

  // Exit code
  process.exit(errors.length > 0 ? 1 : 0);
}

/**
 * Main execution
 */
function main() {
  log('\n🔍 Starting Structured Data Validation...\n', 'cyan');

  // Check if dist directory exists
  try {
    statSync(distDir);
  } catch (err) {
    log('❌ Error: dist directory not found. Run `npm run build` first.', 'red');
    process.exit(1);
  }

  // Find all HTML files
  const htmlFiles = findHtmlFiles(distDir);
  log(`Found ${htmlFiles.length} HTML files to validate\n`, 'cyan');

  // Validate each file
  let totalSchemas = 0;
  for (const file of htmlFiles) {
    const schemaCount = validateFile(file);
    totalSchemas += schemaCount;
    log(`✓ ${file} (${schemaCount} schemas)`, 'green');
  }

  // Print report
  printReport(htmlFiles.length, totalSchemas);
}

// Run validation
main();
