#!/usr/bin/env php
<?php
/**
 * Cache Utility CLI
 * Usage:
 *   php bin/cache clear   - Clear file cache and rebuild classmap
 *   php bin/cache rebuild - Rebuild classmap only
 *   php bin/cache status  - Show cache status
 *   php bin/cache verify  - Verify classmap integrity
 *   php bin/doctor        - Validate environment and runtime prerequisites
 */

$basePath = dirname(__DIR__);
require $basePath . '/core/helpers/Env.php';
Env::load($basePath . '/.env');

$command = $argv[1] ?? 'status';

$cacheDir = $basePath . '/storage/cache';
$classmapFile = $cacheDir . '/classmap.php';

function bytesToHuman($bytes)
{
    $units = ['B', 'KB', 'MB', 'GB'];
    $power = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
    $power = min($power, count($units) - 1);
    $bytes /= pow(1024, $power);
    return number_format($bytes, 2) . ' ' . $units[$power];
}

function pathStartsWith($fullPath, $prefix)
{
    if ($fullPath === '' || $prefix === '') {
        return false;
    }

    if (DIRECTORY_SEPARATOR === '\\') {
        return stripos($fullPath, $prefix) === 0;
    }

    return strpos($fullPath, $prefix) === 0;
}

function collectClassmapEntries($basePath)
{
    $searchDirs = array(
        'core',
        'core/traits',
        'core/db',
        'core/middleware',
        'core/attributes',
        'core/contracts',
        'core/helpers',
        'app/models',
        'app/controllers',
        'app/config',
    );

    $classmap = array();

    foreach ($searchDirs as $relativeDir) {
        $absoluteDir = $basePath . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativeDir);
        if (!is_dir($absoluteDir)) {
            continue;
        }

        try {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($absoluteDir, FilesystemIterator::SKIP_DOTS)
            );
        } catch (Exception $e) {
            continue;
        }

        foreach ($iterator as $fileInfo) {
            if (!$fileInfo->isFile()) {
                continue;
            }

            if (strtolower((string)$fileInfo->getExtension()) !== 'php') {
                continue;
            }

            $className = (string)$fileInfo->getBasename('.php');
            if ($className === '' || isset($classmap[$className])) {
                continue;
            }

            $realPath = realpath($fileInfo->getPathname());
            if ($realPath === false) {
                continue;
            }

            $classmap[$className] = $realPath;
        }
    }

    ksort($classmap);
    return $classmap;
}

function writeClassmapFile($classmapFile, array $classmap)
{
    $content = "<?php\nreturn " . var_export($classmap, true) . ";\n";
    return file_put_contents($classmapFile, $content) !== false;
}

function rebuildClassmap($basePath, $classmapFile)
{
    $classmap = collectClassmapEntries($basePath);
    if (!writeClassmapFile($classmapFile, $classmap)) {
        return false;
    }

    return count($classmap);
}

switch ($command) {
    case 'clear':
        $removed = 0;

        if (is_file($classmapFile)) {
            @unlink($classmapFile);
            $removed++;
            echo "[ok] classmap.php removido\n";
        }

        foreach (glob($cacheDir . '/mon_cache_*.cache') ?: [] as $file) {
            @unlink($file);
            $removed++;
        }

        foreach (glob($cacheDir . '/index_*.idx') ?: [] as $file) {
            @unlink($file);
            $removed++;
        }

        echo "[ok] limpieza finalizada. archivos eliminados: {$removed}\n";

        $entries = rebuildClassmap($basePath, $classmapFile);
        if ($entries === false) {
            echo "[warn] no se pudo reconstruir classmap.php\n";
            exit(1);
        }

        echo "[ok] classmap.php reconstruido. clases indexadas: {$entries}\n";
        exit(0);

    case 'rebuild':
        $entries = rebuildClassmap($basePath, $classmapFile);
        if ($entries === false) {
            echo "[error] no se pudo reconstruir classmap.php\n";
            exit(2);
        }

        echo "[ok] classmap.php reconstruido. clases indexadas: {$entries}\n";
        exit(0);

    case 'verify':
        if (!is_file($classmapFile)) {
            echo "[warn] classmap no existe: {$classmapFile}\n";
            echo "[hint] ejecuta: php bin/cache rebuild\n";
            exit(1);
        }

        $classmap = include $classmapFile;
        if (!is_array($classmap)) {
            echo "[error] classmap inválido (no retorna array): {$classmapFile}\n";
            exit(2);
        }

        $basePathReal = realpath($basePath);
        $invalid = [];
        $valid = 0;

        foreach ($classmap as $className => $classPath) {
            if (!is_string($className) || !is_string($classPath)) {
                $invalid[] = ['class' => (string)$className, 'path' => (string)$classPath, 'reason' => 'invalid-type'];
                continue;
            }

            if (!file_exists($classPath)) {
                $invalid[] = ['class' => $className, 'path' => $classPath, 'reason' => 'missing-file'];
                continue;
            }

            $classPathReal = realpath($classPath);
            if ($classPathReal === false || $basePathReal === false) {
                $invalid[] = ['class' => $className, 'path' => $classPath, 'reason' => 'realpath-failed'];
                continue;
            }

            if (!pathStartsWith($classPathReal, $basePathReal)) {
                $invalid[] = ['class' => $className, 'path' => $classPathReal, 'reason' => 'outside-base-path'];
                continue;
            }

            $valid++;
        }

        if (empty($invalid)) {
            echo "[ok] classmap íntegro. entradas válidas: {$valid}\n";
            exit(0);
        }

        $total = count($classmap);
        $invalidCount = count($invalid);
        echo "[warn] classmap con inconsistencias. total={$total} valid={$valid} invalid={$invalidCount}\n";

        foreach (array_slice($invalid, 0, 10) as $item) {
            echo "  - class={$item['class']} reason={$item['reason']} path={$item['path']}\n";
        }

        if ($invalidCount > 10) {
            echo "  ... y " . ($invalidCount - 10) . " más\n";
        }

        echo "[hint] ejecuta: php bin/cache rebuild\n";
        exit(2);

    case 'status':
    default:
        if (!is_dir($cacheDir)) {
            echo "Cache dir no existe: {$cacheDir}\n";
            exit(0);
        }

        $files = glob($cacheDir . '/*') ?: [];
        $count = 0;
        $total = 0;

        foreach ($files as $file) {
            if (is_file($file)) {
                $count++;
                $total += filesize($file) ?: 0;
            }
        }

        echo "cache_dir: {$cacheDir}\n";
        echo "files: {$count}\n";
        echo "size: " . bytesToHuman($total) . "\n";
        echo "classmap: " . (is_file($classmapFile) ? 'present' : 'missing') . "\n";
        exit(0);
}
