<?php
/**
 * Greenleaf Salt & Key Generator
 *
 * Composer script to auto-generate WordPress authentication salts
 * and CiviCRM security keys in the .env file.
 *
 * Usage:
 *   composer generate-salts
 *
 * Only replaces values that contain 'generateme'. Already-set values
 * are preserved, making this safe to re-run.
 */

namespace Greenleaf\Composer;

use Composer\Script\Event;

class SaltGenerator
{
    /**
     * WordPress salt keys that need 64-char random values
     */
    private const WP_SALT_KEYS = [
        'AUTH_KEY',
        'SECURE_AUTH_KEY',
        'LOGGED_IN_KEY',
        'NONCE_KEY',
        'AUTH_SALT',
        'SECURE_AUTH_SALT',
        'LOGGED_IN_SALT',
        'NONCE_SALT',
    ];

    /**
     * Generate salts and keys, writing them to .env
     */
    public static function generate(Event $event): void
    {
        $io = $event->getIO();
        $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
        $projectDir = dirname($vendorDir);
        $envFile = $projectDir . '/.env';

        if (!file_exists($envFile)) {
            $exampleFile = $projectDir . '/.env.example';
            if (file_exists($exampleFile)) {
                copy($exampleFile, $envFile);
                $io->write('<info>Created .env from .env.example</info>');
            } else {
                $io->writeError('<error>.env file not found and no .env.example to copy from</error>');
                return;
            }
        }

        $content = file_get_contents($envFile);
        $replaced = 0;

        // Generate WordPress salts
        foreach (self::WP_SALT_KEYS as $key) {
            $salt = self::generateSalt(64);
            if (self::replaceGenerateMe($content, $key, "'{$salt}'")) {
                $io->write("<info>  Generated {$key}</info>");
                $replaced++;
            }
        }

        // Generate CiviCRM Site Key (32-char alphanumeric)
        $siteKey = self::generateAlphanumeric(32);
        if (self::replaceGenerateMe($content, 'CIVICRM_SITE_KEY', $siteKey)) {
            $io->write('<info>  Generated CIVICRM_SITE_KEY</info>');
            $replaced++;
        }

        // Generate CiviCRM Credential Key
        $credRandom = self::generateAlphanumeric(32);
        $credKey = "aes-cbc:hkdf-sha256:{$credRandom}";
        if (self::replaceGenerateMe($content, 'CIVICRM_CRED_KEYS', $credKey)) {
            $io->write('<info>  Generated CIVICRM_CRED_KEYS</info>');
            $replaced++;
        }

        // Generate CiviCRM Signing Key
        $signRandom = self::generateAlphanumeric(32);
        $signKey = "jwt-hs256:hkdf-sha256:{$signRandom}";
        if (self::replaceGenerateMe($content, 'CIVICRM_SIGN_KEYS', $signKey)) {
            $io->write('<info>  Generated CIVICRM_SIGN_KEYS</info>');
            $replaced++;
        }

        if ($replaced > 0) {
            file_put_contents($envFile, $content);
            $io->write("<info>Generated {$replaced} key(s) in .env</info>");
        } else {
            $io->write('<comment>All keys already set — nothing to generate</comment>');
        }
    }

    /**
     * Replace a 'generateme' value in the content string
     *
     * @return bool True if replacement was made
     */
    private static function replaceGenerateMe(string &$content, string $key, string $newValue): bool
    {
        // Process line by line to avoid substring matching issues
        // (e.g. AUTH_KEY matching inside SECURE_AUTH_KEY)
        $lines = explode("\n", $content);
        $found = false;

        foreach ($lines as &$line) {
            // Check if this line starts with the exact key followed by =
            if (strpos($line, $key . '=') === 0 && stripos($line, 'generateme') !== false) {
                $line = $key . '=' . $newValue;
                $found = true;
                break;
            }
        }
        unset($line);

        if ($found) {
            $content = implode("\n", $lines);
        }

        return $found;
    }

    /**
     * Generate a random salt string safe for .env values
     */
    private static function generateSalt(int $length): string
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?';
        $salt = '';
        $max = strlen($chars) - 1;

        for ($i = 0; $i < $length; $i++) {
            $salt .= $chars[random_int(0, $max)];
        }

        return $salt;
    }

    /**
     * Generate an alphanumeric random string
     */
    private static function generateAlphanumeric(int $length): string
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $result = '';
        $max = strlen($chars) - 1;

        for ($i = 0; $i < $length; $i++) {
            $result .= $chars[random_int(0, $max)];
        }

        return $result;
    }
}
