#!/usr/bin/env php
<?php
/**
 * Link Htdocs CLI (Windows / XAMPP)
 *
 * Crea un symlink de directorio dentro de C:\xampp\htdocs apuntando a la raíz
 * del proyecto, para poder acceder vía http://localhost/<alias>/public/.
 *
 * Uso:
 *   php bin/link-htdocs                       Usa el nombre de la carpeta del proyecto como alias
 *   php bin/link-htdocs mialias               Usa "mialias" como nombre del enlace
 *   php bin/link-htdocs --htdocs="D:\xampp\htdocs"   Cambia la carpeta htdocs
 *   php bin/link-htdocs --target="C:\ruta\al\proyecto"  Apunta a otra ruta (default: raíz del repo)
 *   php bin/link-htdocs --force               Reemplaza el enlace si ya existe
 *   php bin/link-htdocs --remove              Elimina el enlace si existe
 *
 * Requisitos:
 *   - Windows con cmd / mklink disponible.
 *   - Ejecutar como Administrador, o tener "Modo Desarrollador" activado en Windows 10/11.
 */

if (PHP_SAPI !== 'cli') {
    http_response_code(403);
    exit("Este script solo puede ejecutarse desde la línea de comandos.\n");
}

if (stripos(PHP_OS, 'WIN') !== 0) {
    fwrite(STDERR, "Este script está pensado para Windows (mklink). SO actual: " . PHP_OS . "\n");
    exit(1);
}

$basePath = realpath(dirname(__DIR__));
if ($basePath === false) {
    fwrite(STDERR, "No se pudo resolver la ruta base del proyecto.\n");
    exit(1);
}

$args = array_slice($argv, 1);

$options = array(
    'htdocs' => 'C:\\xampp\\htdocs',
    'target' => $basePath,
    'alias'  => basename($basePath),
    'force'  => false,
    'remove' => false,
);

foreach ($args as $arg) {
    if ($arg === '--force' || $arg === '-f') {
        $options['force'] = true;
        continue;
    }
    if ($arg === '--remove' || $arg === '-r') {
        $options['remove'] = true;
        continue;
    }
    if ($arg === '--help' || $arg === '-h') {
        printHelp();
        exit(0);
    }
    if (strpos($arg, '--htdocs=') === 0) {
        $options['htdocs'] = trim(substr($arg, strlen('--htdocs=')), "\"' ");
        continue;
    }
    if (strpos($arg, '--target=') === 0) {
        $options['target'] = trim(substr($arg, strlen('--target=')), "\"' ");
        continue;
    }
    if (strpos($arg, '--') === 0) {
        fwrite(STDERR, "Opción desconocida: $arg\n");
        printHelp();
        exit(1);
    }
    // Argumento posicional => alias
    $options['alias'] = $arg;
}

$options['alias'] = sanitizeAlias($options['alias']);
if ($options['alias'] === '') {
    fwrite(STDERR, "Alias inválido. Use solo letras, números, guion y guion bajo.\n");
    exit(1);
}

if (!is_dir($options['htdocs'])) {
    fwrite(STDERR, "La carpeta htdocs no existe: {$options['htdocs']}\n");
    fwrite(STDERR, "Use --htdocs=\"C:\\ruta\\a\\htdocs\" si su instalación está en otro lugar.\n");
    exit(1);
}

$target = realpath($options['target']);
if ($target === false || !is_dir($target)) {
    fwrite(STDERR, "La ruta destino no existe: {$options['target']}\n");
    exit(1);
}

$linkPath = rtrim($options['htdocs'], "\\/") . DIRECTORY_SEPARATOR . $options['alias'];

// Modo --remove
if ($options['remove']) {
    if (!file_exists($linkPath)) {
        echo "No existe ningún enlace en: $linkPath\n";
        exit(0);
    }
    if (!isJunctionOrSymlink($linkPath)) {
        fwrite(STDERR, "Abortado: '$linkPath' existe pero NO es un symlink/junction. No se eliminará.\n");
        exit(1);
    }
    if (removeLink($linkPath)) {
        echo "Enlace eliminado: $linkPath\n";
        exit(0);
    }
    fwrite(STDERR, "No se pudo eliminar el enlace: $linkPath\n");
    exit(1);
}

// Si el enlace existe
if (file_exists($linkPath) || is_link($linkPath)) {
    if (!isJunctionOrSymlink($linkPath)) {
        fwrite(STDERR, "Ya existe una carpeta/archivo real en: $linkPath\n");
        fwrite(STDERR, "Renómbrela manualmente o use otro alias.\n");
        exit(1);
    }
    if (!$options['force']) {
        echo "Ya existe un enlace en: $linkPath\n";
        echo "Use --force para reemplazarlo, o --remove para borrarlo.\n";
        exit(0);
    }
    echo "Reemplazando enlace existente...\n";
    if (!removeLink($linkPath)) {
        fwrite(STDERR, "No se pudo eliminar el enlace previo.\n");
        exit(1);
    }
}

// Crear el symlink/junction
$cmd = sprintf('mklink /D %s %s', escapeArg($linkPath), escapeArg($target));
echo "Ejecutando: $cmd\n";

$output = array();
$exitCode = 0;
exec('cmd /c ' . $cmd . ' 2>&1', $output, $exitCode);

foreach ($output as $line) {
    echo $line . "\n";
}

if ($exitCode !== 0) {
    fwrite(STDERR, "\nFalló la creación del enlace (código $exitCode).\n");
    fwrite(STDERR, "Sugerencias:\n");
    fwrite(STDERR, "  - Abra una terminal como Administrador y vuelva a intentar.\n");
    fwrite(STDERR, "  - O active 'Modo Desarrollador' en Configuración > Privacidad y seguridad > Para programadores.\n");
    exit($exitCode);
}

echo "\nListo.\n";
echo "Acceda a su aplicación en: http://localhost/{$options['alias']}/public/\n";
exit(0);


function printHelp()
{
    echo "\n";
    echo "Klee - Link Htdocs CLI (Windows/XAMPP)\n";
    echo "\n";
    echo "Uso:\n";
    echo "  php bin/link-htdocs [alias] [opciones]\n";
    echo "\n";
    echo "Opciones:\n";
    echo "  --htdocs=PATH   Ruta a la carpeta htdocs (default: C:\\xampp\\htdocs)\n";
    echo "  --target=PATH   Ruta del proyecto (default: raíz del repo actual)\n";
    echo "  --force, -f     Reemplaza el enlace si ya existe\n";
    echo "  --remove, -r    Elimina el enlace en lugar de crearlo\n";
    echo "  --help, -h      Muestra esta ayuda\n";
    echo "\n";
    echo "Ejemplos:\n";
    echo "  php bin/link-htdocs\n";
    echo "  php bin/link-htdocs kuorum\n";
    echo "  php bin/link-htdocs timeklee2 --force\n";
    echo "  php bin/link-htdocs timeklee2 --remove\n";
    echo "\n";
}

function sanitizeAlias($alias)
{
    $alias = (string)$alias;
    $alias = preg_replace('/[^A-Za-z0-9_\-]/', '', $alias);
    return (string)$alias;
}

function escapeArg($value)
{
    return '"' . str_replace('"', '', $value) . '"';
}

function isJunctionOrSymlink($path)
{
    if (is_link($path)) {
        return true;
    }
    // Junctions en Windows no siempre son detectados por is_link.
    // Las identificamos por el atributo de reparse point.
    $attrs = @shell_exec('cmd /c dir /AL "' . dirname($path) . '" 2>nul');
    if (!is_string($attrs)) {
        return false;
    }
    $name = basename($path);
    return (strpos($attrs, $name) !== false);
}

function removeLink($linkPath)
{
    // rmdir borra junctions/symlinks de directorio sin tocar el destino.
    $cmd = 'cmd /c rmdir ' . escapeArg($linkPath) . ' 2>&1';
    $output = array();
    $exitCode = 0;
    exec($cmd, $output, $exitCode);
    return $exitCode === 0;
}
