/**
 * Version Manager Utility
 * Handles WordPress plugin version parsing, bumping, and detection
 */

const path = require('path');
const fs = require('fs').promises;

/**
 * Valid semantic version regex (X.Y.Z format)
 */
const VERSION_REGEX = /^\d+\.\d+\.\d+$/;

/**
 * WordPress plugin header version regex
 * Matches: Version: 1.2.3 or Version:1.2.3
 */
const WP_HEADER_VERSION_REGEX = /^\s*\*?\s*Version:\s*(\d+\.\d+\.\d+)/im;

/**
 * PHP define version regex
 * Matches: define('CONSTANT_NAME', '1.2.3') or define( "CONSTANT_NAME", "1.2.3" )
 */
const PHP_DEFINE_VERSION_REGEX = /define\s*\(\s*['"][A-Z_]*VERSION['"]\s*,\s*['"](\d+\.\d+\.\d+)['"]\s*\)/gi;

/**
 * PHP constant assignment regex (alternative format)
 * Matches: const VERSION = '1.2.3';
 */
const PHP_CONST_VERSION_REGEX = /const\s+[A-Z_]*VERSION\s*=\s*['"](\d+\.\d+\.\d+)['"]/gi;

/**
 * Parse a version string into components
 * @param {string} version - Version string (e.g., "1.2.3")
 * @returns {object|null} - { major, minor, patch } or null if invalid
 */
function parseVersion(version) {
  if (!version || typeof version !== 'string') return null;
  
  const cleanVersion = version.trim();
  if (!VERSION_REGEX.test(cleanVersion)) return null;
  
  const [major, minor, patch] = cleanVersion.split('.').map(Number);
  return { major, minor, patch, raw: cleanVersion };
}

/**
 * Format version components into a string
 * @param {number} major
 * @param {number} minor
 * @param {number} patch
 * @returns {string} - Formatted version (e.g., "1.2.3")
 */
function formatVersion(major, minor, patch) {
  return `${major}.${minor}.${patch}`;
}

/**
 * Bump a version number
 * @param {string} currentVersion - Current version (e.g., "1.2.3")
 * @param {string} bumpType - 'major', 'minor', 'patch', or 'keep'
 * @returns {string|null} - New version or null if invalid input
 */
function bumpVersion(currentVersion, bumpType = 'patch') {
  const parsed = parseVersion(currentVersion);
  if (!parsed) return null;
  
  const { major, minor, patch } = parsed;
  
  switch (bumpType.toLowerCase()) {
    case 'major':
      return formatVersion(major + 1, 0, 0);
    case 'minor':
      return formatVersion(major, minor + 1, 0);
    case 'patch':
      return formatVersion(major, minor, patch + 1);
    case 'keep':
      return currentVersion;
    default:
      // Default to patch bump
      return formatVersion(major, minor, patch + 1);
  }
}

/**
 * Extract version from WordPress plugin header
 * @param {string} content - PHP file content
 * @returns {string|null} - Version string or null
 */
function extractHeaderVersion(content) {
  if (!content) return null;
  const match = content.match(WP_HEADER_VERSION_REGEX);
  return match ? match[1] : null;
}

/**
 * Extract version from PHP define() constants
 * @param {string} content - PHP file content
 * @returns {string|null} - Version string or null
 */
function extractDefineVersion(content) {
  if (!content) return null;
  // Use non-global version of regex to properly capture groups
  const regex = /define\s*\(\s*['"][A-Z_]*VERSION['"]\s*,\s*['"](\d+\.\d+\.\d+)['"]\s*\)/i;
  const match = content.match(regex);
  return match ? match[1] : null;
}

/**
 * Extract version from PHP const assignments
 * @param {string} content - PHP file content
 * @returns {string|null} - Version string or null
 */
function extractConstVersion(content) {
  if (!content) return null;
  const regex = new RegExp(PHP_CONST_VERSION_REGEX);
  const match = regex.exec(content);
  return match ? match[1] : null;
}

/**
 * Find the main plugin file in a directory
 * Searches for PHP files with WordPress plugin headers
 * @param {string} dirPath - Directory to search
 * @param {Array} validFiles - Optional array of file objects from collectValidFiles
 * @returns {Promise<object|null>} - { fullPath, relativePath, content } or null
 */
async function findMainPluginFile(dirPath, validFiles = null) {
  // If validFiles not provided, collect them
  if (!validFiles) {
    validFiles = [];
    await collectPhpFiles(dirPath, dirPath, validFiles);
  }
  
  // Sort by likelihood of being main file (root level first, shorter names preferred)
  const phpFiles = validFiles
    .filter(f => f.fullPath.endsWith('.php'))
    .sort((a, b) => {
      const aDepth = a.relativePath.split(path.sep).length;
      const bDepth = b.relativePath.split(path.sep).length;
      if (aDepth !== bDepth) return aDepth - bDepth;
      return a.relativePath.length - b.relativePath.length;
    });
  
  for (const fileInfo of phpFiles) {
    try {
      const content = await fs.readFile(fileInfo.fullPath, 'utf-8');
      // Check for WordPress plugin header
      if (content.includes('Plugin Name:')) {
        return {
          fullPath: fileInfo.fullPath,
          relativePath: fileInfo.relativePath,
          content
        };
      }
    } catch (err) {
      // Continue to next file
    }
  }
  
  return null;
}

/**
 * Helper to collect PHP files recursively
 */
async function collectPhpFiles(rootDir, currentDir, files) {
  try {
    const entries = await fs.readdir(currentDir, { withFileTypes: true });
    
    for (const entry of entries) {
      const fullPath = path.join(currentDir, entry.name);
      const relativePath = path.relative(rootDir, fullPath);
      
      if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
        await collectPhpFiles(rootDir, fullPath, files);
      } else if (entry.isFile() && entry.name.endsWith('.php')) {
        files.push({ fullPath, relativePath });
      }
    }
  } catch (err) {
    // Ignore errors
  }
}

/**
 * Extract all version information from a plugin file
 * @param {string} content - PHP file content
 * @returns {object} - { headerVersion, defineVersion, constVersion, detectedVersion }
 */
function extractAllVersions(content) {
  const headerVersion = extractHeaderVersion(content);
  const defineVersion = extractDefineVersion(content);
  const constVersion = extractConstVersion(content);
  
  // Use first non-null version found (priority: header > define > const)
  const detectedVersion = headerVersion || defineVersion || constVersion;
  
  return {
    headerVersion,
    defineVersion,
    constVersion,
    detectedVersion
  };
}

/**
 * Update version in plugin file content
 * @param {string} content - Original file content
 * @param {string} oldVersion - Current version
 * @param {string} newVersion - New version to set
 * @returns {string|null} - Updated content or null if no changes needed
 */
function updateVersionInContent(content, oldVersion, newVersion) {
  if (!content || !oldVersion || !newVersion || oldVersion === newVersion) {
    return null;
  }
  
  let updated = content;
  let changesMade = false;
  
  // Update WordPress plugin header version
  const headerRegex = new RegExp(
    `^(\\s*\\*?\\s*Version:)\\s*${oldVersion.replace(/\./g, '\\.')}`,
    'gim'
  );
  if (headerRegex.test(updated)) {
    updated = updated.replace(headerRegex, `$1 ${newVersion}`);
    changesMade = true;
  }
  
  // Update PHP define() version constants
  const defineRegex = new RegExp(
    `(define\\s*\\(\\s*['"][A-Z_]*VERSION['"]\\s*,\\s*['"])${oldVersion.replace(/\./g, '\\.')}(['"]\\s*\\))`,
    'gi'
  );
  if (defineRegex.test(updated)) {
    updated = updated.replace(defineRegex, `$1${newVersion}$2`);
    changesMade = true;
  }
  
  // Update PHP const version assignments
  const constRegex = new RegExp(
    `(const\\s+[A-Z_]*VERSION\\s*=\\s*['"])${oldVersion.replace(/\./g, '\\.')}(['"])`,
    'gi'
  );
  if (constRegex.test(updated)) {
    updated = updated.replace(constRegex, `$1${newVersion}$2`);
    changesMade = true;
  }
  
  return changesMade ? updated : null;
}

/**
 * Detect if a file is a WordPress plugin file
 * @param {string} content - File content
 * @returns {boolean}
 */
function isWordPressPluginFile(content) {
  if (!content) return false;
  return content.includes('Plugin Name:') && content.includes('Version:');
}

/**
 * Get the plugin slug from folder name or file name
 * @param {string} filePath - Path to main plugin file
 * @returns {string}
 */
function getPluginSlugFromPath(filePath) {
  const parsed = path.parse(filePath);
  const parentDir = path.basename(parsed.dir);
  const fileName = parsed.name;
  
  // If parent directory name matches file name (typical WordPress plugin structure: my-plugin/my-plugin.php)
  // or if there's a meaningful parent directory name (not generic like 'to', 'path', 'src')
  // use the directory name, otherwise use the file name
  const genericDirNames = ['.', '..', 'src', 'lib', 'includes', 'dist', 'build', 'to', 'path'];
  
  if (parentDir === fileName) {
    // Perfect match: folder/my-plugin/my-plugin.php
    return parentDir;
  } else if (!genericDirNames.includes(parentDir.toLowerCase())) {
    // Parent directory has a meaningful name: folder/my-custom-plugin/plugin.php
    return parentDir;
  } else {
    // Use file name as fallback: folder/plugin-name.php
    return fileName;
  }
}

module.exports = {
  parseVersion,
  formatVersion,
  bumpVersion,
  extractHeaderVersion,
  extractDefineVersion,
  extractConstVersion,
  extractAllVersions,
  findMainPluginFile,
  updateVersionInContent,
  isWordPressPluginFile,
  getPluginSlugFromPath,
  // Regex exports for testing
  VERSION_REGEX,
  WP_HEADER_VERSION_REGEX,
  PHP_DEFINE_VERSION_REGEX,
  PHP_CONST_VERSION_REGEX
};
