#!/usr/bin/env node

/**
 * Security check script for MCP WordPress
 * Performs various security checks and generates a report
 */

import { promises as fs } from "fs";
import * as path from "path";
import { fileURLToPath } from "url";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, "..");

// Colors for console output
const colors = {
  red: "\x1b[31m",
  green: "\x1b[32m",
  yellow: "\x1b[33m",
  blue: "\x1b[34m",
  reset: "\x1b[0m",
};

// Security check results
const results = {
  passed: [],
  warnings: [],
  failures: [],
};

/**
 * Log helper functions
 */
function logSuccess(message) {
  console.log(`${colors.green}✓${colors.reset} ${message}`);
  results.passed.push(message);
}

function logWarning(message) {
  console.log(`${colors.yellow}⚠${colors.reset} ${message}`);
  results.warnings.push(message);
}

function logError(message) {
  console.log(`${colors.red}✗${colors.reset} ${message}`);
  results.failures.push(message);
}

function logInfo(message) {
  console.log(`${colors.blue}ℹ${colors.reset} ${message}`);
}

/**
 * Check for exposed secrets in files
 */
async function checkForSecrets() {
  logInfo("Checking for exposed secrets...");

  const patterns = [
    /password\s*[:=]\s*["'](?!your-|example|test|placeholder|xxxx|npm_X)[\w\s]+["']/gi,
    /api[_-]?key\s*[:=]\s*["'](?!your-|example|test|placeholder|npm_X)[\w-]+["']/gi,
    /secret\s*[:=]\s*["'](?!your-|example|test|placeholder|npm_X)[\w-]+["']/gi,
    /token\s*[:=]\s*["'](?!your-|example|test|placeholder|npm_X)[\w-]+["']/gi,
  ];

  const filesToCheck = ["src/**/*.ts", "src/**/*.js", "tests/**/*.js", "scripts/**/*.js", "*.json", "*.md"];

  let secretsFound = false;

  // Check each file pattern
  for (const pattern of filesToCheck) {
    try {
      const { stdout } = await execAsync(
        `find ${rootDir} -name "${pattern}" -type f | grep -v node_modules | grep -v dist | head -20`,
      );
      const files = stdout.split("\n").filter((f) => f);

      for (const file of files) {
        try {
          const content = await fs.readFile(file, "utf-8");

          for (const regex of patterns) {
            if (regex.test(content)) {
              logError(`Potential secret found in ${path.relative(rootDir, file)}`);
              secretsFound = true;
            }
          }
        } catch (err) {
          // Skip files that can't be read
        }
      }
    } catch (err) {
      // Pattern didn't match any files
    }
  }

  if (!secretsFound) {
    logSuccess("No exposed secrets found in source files");
  }
}

/**
 * Check npm audit for vulnerabilities
 */
async function checkDependencies() {
  logInfo("Checking dependencies for vulnerabilities...");

  try {
    const { stdout } = await execAsync("npm audit --json", { cwd: rootDir });
    const audit = JSON.parse(stdout);

    if (audit.metadata.vulnerabilities.total === 0) {
      logSuccess("No known vulnerabilities in dependencies");
    } else {
      const { high, critical } = audit.metadata.vulnerabilities;
      if (critical > 0) {
        logError(`${critical} critical vulnerabilities found in dependencies`);
      }
      if (high > 0) {
        logWarning(`${high} high severity vulnerabilities found in dependencies`);
      }
      logInfo('Run "npm audit fix" to resolve vulnerabilities');
    }
  } catch (err) {
    logWarning("Could not run npm audit");
  }
}

/**
 * Check for secure configuration files
 */
async function checkConfigSecurity() {
  logInfo("Checking configuration security...");

  // Check .gitignore
  try {
    const gitignore = await fs.readFile(path.join(rootDir, ".gitignore"), "utf-8");
    const requiredEntries = [".env", "mcp-wordpress.config.json", "*.log"];

    for (const entry of requiredEntries) {
      if (gitignore.includes(entry)) {
        logSuccess(`${entry} is properly excluded from git`);
      } else {
        logError(`${entry} should be added to .gitignore`);
      }
    }
  } catch (err) {
    logError(".gitignore file not found");
  }

  // Check for config files with real credentials
  const configFiles = ["mcp-wordpress.config.json", ".env"];
  for (const file of configFiles) {
    const filePath = path.join(rootDir, file);
    try {
      await fs.access(filePath);
      logWarning(`${file} exists - ensure it doesn't contain real credentials`);
    } catch {
      // File doesn't exist, which is good for security
    }
  }
}

/**
 * Check file permissions
 */
async function checkFilePermissions() {
  logInfo("Checking file permissions...");

  const sensitivePaths = [".env", "mcp-wordpress.config.json"];

  for (const file of sensitivePaths) {
    const filePath = path.join(rootDir, file);
    try {
      const stats = await fs.stat(filePath);
      const mode = stats.mode & parseInt("777", 8);

      if (mode > parseInt("600", 8)) {
        logWarning(`${file} has permissive permissions (${mode.toString(8)})`);
      } else {
        logSuccess(`${file} has restrictive permissions`);
      }
    } catch {
      // File doesn't exist
    }
  }
}

/**
 * Check for security headers in code
 */
async function checkSecurityPatterns() {
  logInfo("Checking security patterns in code...");

  // Check for input validation
  try {
    const { stdout: validationFiles } = await execAsync(
      `grep -r "validate" ${rootDir}/src --include="*.ts" --include="*.js" | wc -l`,
    );
    const validationCount = parseInt(validationFiles.trim());

    if (validationCount > 10) {
      logSuccess(`Found ${validationCount} validation references in code`);
    } else {
      logWarning("Limited input validation found in code");
    }
  } catch {
    logWarning("Could not check for validation patterns");
  }

  // Check for rate limiting
  try {
    const { stdout: rateLimitFiles } = await execAsync(
      `grep -r "rate.*limit" ${rootDir}/src --include="*.ts" --include="*.js" -i | wc -l`,
    );
    const rateLimitCount = parseInt(rateLimitFiles.trim());

    if (rateLimitCount > 0) {
      logSuccess("Rate limiting implementation found");
    } else {
      logWarning("No rate limiting implementation found");
    }
  } catch {
    logWarning("Could not check for rate limiting");
  }
}

/**
 * Generate security report
 */
function generateReport() {
  console.log("\n" + "=".repeat(50));
  console.log("Security Check Summary");
  console.log("=".repeat(50));

  console.log(`\n${colors.green}Passed:${colors.reset} ${results.passed.length}`);
  results.passed.forEach((msg) => console.log(`  ✓ ${msg}`));

  if (results.warnings.length > 0) {
    console.log(`\n${colors.yellow}Warnings:${colors.reset} ${results.warnings.length}`);
    results.warnings.forEach((msg) => console.log(`  ⚠ ${msg}`));
  }

  if (results.failures.length > 0) {
    console.log(`\n${colors.red}Failures:${colors.reset} ${results.failures.length}`);
    results.failures.forEach((msg) => console.log(`  ✗ ${msg}`));
  }

  console.log("\n" + "=".repeat(50));

  if (results.failures.length === 0) {
    console.log(`${colors.green}✓ Security check passed with ${results.warnings.length} warnings${colors.reset}`);
    return 0;
  } else {
    console.log(`${colors.red}✗ Security check failed with ${results.failures.length} issues${colors.reset}`);
    return 1;
  }
}

/**
 * Main security check function
 */
async function main() {
  console.log("🔒 MCP WordPress Security Check\n");

  try {
    await checkForSecrets();
    await checkDependencies();
    await checkConfigSecurity();
    await checkFilePermissions();
    await checkSecurityPatterns();

    const exitCode = generateReport();
    process.exit(exitCode);
  } catch (error) {
    console.error("Error running security check:", error);
    process.exit(1);
  }
}

// Run the security check
main();
