#!/usr/bin/env php
<?php
/**
 * Define color codes
 */
class Colors
{
    public const RESET = "\033[0m";
    public const RED = "\033[0;31m";
    public const GREEN = "\033[0;32m";
    public const YELLOW = "\033[0;33m";
    public const BLACK = "\033[0;30m";
    public const BLUE = "\033[0;34m";
    public const MAGENTA = "\033[0;35m";
    public const CYAN = "\033[0;36m";
    public const WHITE = "\033[0;37m";
}

class SymlinkMaker
{
    public function __construct(
        public $targetFile = null,
        public $dirName = null,
        public $laragonDir = null,
        public $symlinkFile = null,
    ) {
        $this->targetFile = $targetFile ?? getcwd(); // original path with dir name
        $this->dirName = $dirName ?? basename($this->targetFile); // original dir name
        $this->laragonDir = $laragonDir ?? Config::getLaragonDir();
        $this->symlinkFile = $symlinkFile ?? $this->laragonDir . DIRECTORY_SEPARATOR . $this->dirName;
    }

    public function makeSymlink()
    {
        try {
            $targetFile = $this->targetFile;
            $dirName = $this->dirName;
            $laragonDir = $this->laragonDir;
            $symlinkFile = $this->symlinkFile;

            // Check if file exists
            self::checkFileIfExist($symlinkFile);

            // Create the symlink
            if (symlink($targetFile, $symlinkFile)) {
                echo Colors::GREEN . "INFO" . Colors::CYAN . " The [" . Colors::RESET . $symlinkFile . Colors::CYAN . "] link has been connected to [" . Colors::RESET . $targetFile . Colors::CYAN . "].\n" . Colors::RESET;
                // TODO: check laragon config to determine http or https schema and domain tld
                echo Colors::CYAN . "Visit your website at: " . Colors::CYAN . "http://" . $dirName . ".test\n" . Colors::RESET;
                echo Colors::YELLOW . "Restart Laragon to see effect.\n" . Colors::RESET;
                Config::reloadLaragon();
            } else {
                echo Colors::RED . "ERROR" . Colors::RESET . " Failed to create symlink." . Colors::RESET;
            }
        } catch (\Throwable $th) {
            echo $th->getMessage() . Colors::RESET;
        }
    }

    public function deleteSymlink()
    {
        try {
            $symlinkFile = $this->symlinkFile;
            // Check if file exists
            self::checkFileIfNotExist($symlinkFile);

            // Delete the symlink file
            if (rmdir($symlinkFile)) {
                echo Colors::GREEN . "INFO" . Colors::CYAN . " The symlink file [" . Colors::RESET . $symlinkFile . Colors::CYAN . "] has been deleted.\n" . Colors::RESET;
            } else {
                echo Colors::RED . "ERROR" . Colors::RESET . " Failed to delete symlink file." . Colors::RESET;
            }
        } catch (\Throwable $th) {
            echo Colors::RED . "ERROR" . Colors::RESET . " " . $th->getMessage() . Colors::RESET;
        }
    }

    protected static function checkFileIfExist($symlinkFile)
    {
        // Check if the symlink file already exists
        if (file_exists($symlinkFile)) {
            throw new \Exception(Colors::YELLOW . "WARNING" . Colors::CYAN . " The symlink file [" . Colors::RESET . $symlinkFile . Colors::CYAN . "] already exists.\n");
        }
    }

    protected static function checkFileIfNotExist($symlinkFile)
    {
        // Check if the symlink file already exists
        if (!file_exists($symlinkFile)) {
            throw new \Exception(Colors::YELLOW . "WARNING" . Colors::RED . " The symlink file [" . Colors::RESET . $symlinkFile . Colors::RED . "] not exists.\n");
        }
    }
}

/**
 * Define config of symlink maker
 */
class Config
{
    public const VERSION = '1.0.0'; // update tag and release as is.
    private $configFileName = 'laralink.json';

    public function __construct(
        public $laragonPath = null,
        public $userProfilePath = null,
        public $jsonFilePath = null
    ) {
        // Define the user profile path
        $this->userProfilePath = $userProfilePath ?? getenv('USERPROFILE');
        // Define the path to the JSON file
        $this->jsonFilePath = $this->userProfilePath . DIRECTORY_SEPARATOR . $this->configFileName;
    }

    public static function getConfigFileName()
    {
        $self = new static();
        return $self->configFileName;
    }

    public static function getConfigFilePath()
    {
        // Define the user profile path
        $userProfilePath = getenv('USERPROFILE');
        // Define the path to the JSON file
        return $userProfilePath . DIRECTORY_SEPARATOR . self::getConfigFileName();
    }

    public static function checkConfigFile()
    {
        return file_exists(self::getConfigFilePath());
    }

    public function setLaragonPath()
    {
        $initialConfig = [
            "www" => $this->laragonPath . DIRECTORY_SEPARATOR . 'www',
        ];
        file_put_contents($this->jsonFilePath, json_encode($initialConfig));

        // Read the existing JSON data
        $configData = json_decode(file_get_contents($this->jsonFilePath), true);
    }

    public static function getLaragonDir()
    {
        if (!self::checkConfigFile()) {
            // Get the user profile path
            $userProfilePath = getenv('USERPROFILE');
            // Get the config file name
            $configFileName = self::getConfigFileName();
            // Throw an exception if config file is not exist.
            throw new Exception("The config file [{$configFileName}] is not found in {$userProfilePath}.");
        }

        // Read the contents of the config file
        $configContents = file_get_contents(self::getConfigFilePath());
        // Decode the JSON contents into an associative array
        $configData = json_decode($configContents, true);

        // Check if the 'www' key exists in the config data
        if (!isset($configData['www'])) {
            // Throw an exception or return a default value if the 'www' key is not found
            throw new Exception("The 'www' key is not found in the config file.");
        }

        return $configData['www'];
    }

    public static function info()
    {
        echo Colors::CYAN . "Laragon Linker " . Colors::GREEN . Config::VERSION . " \n" . Colors::RESET;

        echo Colors::YELLOW . "\nUsage: \n" . Colors::RESET;
        echo Colors::RESET . "   command [options] [arguments] \n" . Colors::RESET;

        echo Colors::YELLOW . "\nOptions: \n" . Colors::RESET;
        echo Colors::GREEN . "  -h, --help  " . Colors::RESET . "Display help for the " . Colors::CYAN . "Laragon Linker " . Colors::RESET . "tool.\n" . Colors::RESET;

        echo Colors::YELLOW . "\nAvailable commands: \n" . Colors::RESET;
        echo Colors::GREEN . "  link       " . Colors::RESET . "Link current directory with laragon/www. \n" . Colors::RESET;
        echo Colors::GREEN . "  unlink     " . Colors::RESET . "Remove current directory from laragon/www.\n" . Colors::RESET;
        echo Colors::GREEN . "  path       " . Colors::RESET . "Get current path of laragon. \n" . Colors::RESET;
        echo Colors::GREEN . "  set        " . Colors::RESET . "Set the path of laragon. \n" . Colors::RESET;
        echo Colors::GREEN . "  links      " . Colors::RESET . "Get paths of linked projects (coming soon). \n" . Colors::RESET;
        echo Colors::GREEN . "  php-scoop " . Colors::RESET . " Create symlinks for PHP versions from Scoop. \n" . Colors::RESET;
    }

    public static function reloadLaragon()
    {
        $command = self::getLaragonDir() . DIRECTORY_SEPARATOR . 'laragon.exe reload';
        $output = [];
        $returnVar = 0;

        exec($command, $output, $returnVar);

        // Output the command output and return value
        echo "Command Output:\n" . Colors::RESET;
        foreach ($output as $line) {
            echo $line . "\n" . Colors::RESET;
        }
    }
}

if (isset($argv[1])) {
    if (!Config::checkConfigFile()) {
        echo Colors::CYAN . 'Enter the path of Laragon: ' . Colors::RESET;
        (new Config(laragonPath: trim(fgets(STDIN))))->setLaragonPath();
    }

    // Usage: link current directory to Laragon www path
    if ($argv[1] == 'link') {
        (new SymlinkMaker())->makeSymlink();
    }

    // Usage: unlink current directory from Laragon www path
    if ($argv[1] == 'unlink') {
        (new SymlinkMaker())->deleteSymlink();
    }

    // Usage: get the path of Laragon
    if ($argv[1] == 'path') {
        echo Colors::GREEN . Config::getLaragonDir() . Colors::RESET;
    }

    // Usage: set the path of Laragon
    if ($argv[1] == 'set') {
        echo Colors::CYAN . 'Enter the path of Laragon: ' . Colors::RESET;
        (new Config(laragonPath: trim(fgets(STDIN))))->setLaragonPath();
    }

    // Usage: create symlinks for PHP versions from Scoop
    if ($argv[1] == 'php-scoop') {
        // Get the username dynamically
        $userName = getenv('USERNAME');

        // Define source directory
        $scoopAppsPath = "C:\\Users\\$userName\\scoop\\apps";

        // Ask the user for the Laragon root path with a default value
        $defaultLaragonPath = str_replace('www', '', Config::getLaragonDir());
        echo "Enter Laragon root path [default: $defaultLaragonPath]: ";
        $laragonRootPath = trim(fgets(STDIN));

        // Use the default path if the user input is empty
        $laragonPhpPath = !empty($laragonRootPath) ? $laragonRootPath . "\\bin\\php" : $defaultLaragonPath . "\\bin\\php";

        // Ensure source and destination directories exist
        if (!is_dir($scoopAppsPath)) {
            die("Error: Scoop apps directory does not exist.\n");
        }

        if (!is_dir($laragonPhpPath)) {
            die("Error: Laragon PHP directory does not exist.\n");
        }

        // Get a list of all installed PHP versions in the Scoop apps directory
        $phpVersions = glob($scoopAppsPath . "\\php*\\current", GLOB_ONLYDIR);

        foreach ($phpVersions as $phpVersionPath) {
            // Extract the PHP version from the path
            $phpVersion = basename(str_replace('current', '', $phpVersionPath));

            // Define the destination path for the symlink in Laragon
            $destinationPath = $laragonPhpPath . "\\" . $phpVersion;

            // Create a symbolic link
            if (!file_exists($destinationPath)) {
                symlink($phpVersionPath, $destinationPath);
                echo "Symlink created for PHP version: $phpVersion\n";
            } else {
                echo "Symlink already exists for PHP version: $phpVersion\n";
            }
        }

        echo "All symlinks created successfully.\n";
    }

    // Usage: get help
    if ($argv[1] == '--help' || $argv[1] == '-h') {
        Config::info();
    }
} else {
    Config::info();
}
