#!/usr/bin/env php
<?php
/**
 * Environment Doctor CLI
 * Usage:
 *   php bin/doctor
 *   php bin/doctor --strict
 */

$basePath = dirname(__DIR__);
define('BASE_PATH', $basePath . '/');

$args = array_slice($argv, 1);
$strictMode = in_array('--strict', $args, true) || in_array('-s', $args, true);

if (in_array('--help', $args, true) || in_array('-h', $args, true)) {
    echo "\nKlee Doctor\n";
    echo "Uso:\n";
    echo "  php bin/doctor           Validación general de entorno\n";
    echo "  php bin/doctor --strict  Falla también por warnings (ideal CI)\n\n";
    exit(0);
}

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

if (file_exists($basePath . '/app/config/Config.php')) {
    require $basePath . '/app/config/Config.php';
}
if (file_exists($basePath . '/app/config/ConfigEnv.php')) {
    require $basePath . '/app/config/ConfigEnv.php';
}

$errors = 0;
$warnings = 0;

$print = function ($type, $message) use (&$errors, &$warnings) {
    if ($type === 'ERROR') {
        $errors++;
        echo "[error] {$message}\n";
        return;
    }

    if ($type === 'WARN') {
        $warnings++;
        echo "[warn] {$message}\n";
        return;
    }

    echo "[ok] {$message}\n";
};

echo "\nKlee Doctor\n";
echo str_repeat('-', 60) . "\n";

// 1) Config files
$configPath = $basePath . '/app/config/Config.php';
$configEnvPath = $basePath . '/app/config/ConfigEnv.php';

if (is_file($configPath)) {
    $print('OK', 'Config.php encontrado.');
} else {
    $print('ERROR', 'Falta app/config/Config.php');
}

if (is_file($configEnvPath)) {
    $print('OK', 'ConfigEnv.php encontrado.');
} else {
    $print('ERROR', 'Falta app/config/ConfigEnv.php (copiar desde ConfigEnv.example.php).');
}

if (is_file($basePath . '/.env')) {
    $print('OK', '.env encontrado.');
} else {
    $print('WARN', '.env no encontrado (usa .env.example como plantilla).');
}

// 2) Env variables (soft requirement)
$requiredEnv = array('DB_DRIVER', 'DB_HOST', 'DB_NAME', 'DB_USER');
$missingEnv = array();
foreach ($requiredEnv as $envKey) {
    if (getenv($envKey) === false || trim((string)getenv($envKey)) === '') {
        $missingEnv[] = $envKey;
    }
}

if (empty($missingEnv)) {
    $print('OK', 'Variables de entorno base de DB presentes.');
} else {
    $print('WARN', 'Variables de entorno faltantes: ' . implode(', ', $missingEnv));
}

// 2.1) SMTP variables
$mailHost = trim((string)(getenv('MAIL_HOST') ?: ''));
$mailUser = trim((string)(getenv('MAIL_USER') ?: ''));
$mailPassRaw = getenv('MAIL_PASS');
$mailPass = $mailPassRaw === false ? '' : (string)$mailPassRaw;
$mailPort = trim((string)(getenv('MAIL_PORT') ?: ''));
$mailEncryption = strtolower(trim((string)(getenv('MAIL_ENCRYPTION') ?: '')));

if ($mailHost !== '') {
    $smtpErrors = array();
    $smtpWarnings = array();

    if ($mailPort === '' || !ctype_digit($mailPort)) {
        $smtpErrors[] = 'MAIL_PORT inválido (debe ser numérico).';
    }

    if ($mailEncryption !== '' && !in_array($mailEncryption, array('tls', 'ssl', 'starttls', 'none'), true)) {
        $smtpErrors[] = 'MAIL_ENCRYPTION inválido (use tls|ssl|starttls|none).';
    }

    if ($mailUser === '') {
        $smtpWarnings[] = 'MAIL_USER vacío.';
    }

    if ($mailPassRaw === false || $mailPass === '') {
        $smtpWarnings[] = 'MAIL_PASS vacío.';
    }

    if (empty($smtpErrors) && empty($smtpWarnings)) {
        $print('OK', 'Configuración SMTP base presente.');
    } else {
        foreach ($smtpErrors as $msg) {
            $print('ERROR', $msg);
        }
        foreach ($smtpWarnings as $msg) {
            $print('WARN', $msg);
        }
    }
} else {
    $print('WARN', 'MAIL_HOST no definido; envío SMTP quedará deshabilitado o incompleto.');
}

// 3) PHP/extensiones
$print('OK', 'PHP version: ' . PHP_VERSION);
$requiredExt = array('gd', 'mbstring', 'pdo', 'pdo_mysql', 'openssl');
foreach ($requiredExt as $ext) {
    if (extension_loaded($ext)) {
        $print('OK', "Extensión {$ext} cargada.");
    } else {
        $print('ERROR', "Extensión {$ext} no cargada.");
    }
}

// 4) Writable directories
$writableDirs = array(
    $basePath . '/storage',
    $basePath . '/storage/cache',
    $basePath . '/logs',
    $basePath . '/files',
);

foreach ($writableDirs as $dir) {
    if (!is_dir($dir)) {
        $print('WARN', "Directorio no existe: {$dir}");
        continue;
    }

    if (is_writable($dir)) {
        $print('OK', "Directorio escribible: {$dir}");
    } else {
        $print('ERROR', "Directorio sin permiso de escritura: {$dir}");
    }
}

// 5) Classmap integrity
$classmapFile = $basePath . '/storage/cache/classmap.php';
if (!is_file($classmapFile)) {
    $print('WARN', 'classmap.php no encontrado (ejecuta php bin/cache rebuild para reconstruir).');
} else {
    $classmap = include $classmapFile;
    if (!is_array($classmap)) {
        $print('ERROR', 'classmap.php inválido (no retorna array).');
    } else {
        $basePathReal = realpath($basePath);
        $invalid = 0;

        foreach ($classmap as $className => $classPath) {
            if (!is_string($className) || !is_string($classPath) || !file_exists($classPath)) {
                $invalid++;
                continue;
            }

            $classPathReal = realpath($classPath);
            if ($classPathReal === false || $basePathReal === false) {
                $invalid++;
                continue;
            }

            $startsWith = (DIRECTORY_SEPARATOR === '\\')
                ? stripos($classPathReal, $basePathReal) === 0
                : strpos($classPathReal, $basePathReal) === 0;

            if (!$startsWith) {
                $invalid++;
            }
        }

        if ($invalid === 0) {
            $print('OK', 'Classmap íntegro.');
        } else {
            $print('WARN', "Classmap con {$invalid} entrada(s) inválida(s).");
        }
    }
}

// 6) DB connectivity
$dbConfig = null;
if (class_exists('ConfigEnv') && property_exists('ConfigEnv', 'DB_CONNECTIONS') && isset(ConfigEnv::$DB_CONNECTIONS['klee'])) {
    $dbConfig = ConfigEnv::$DB_CONNECTIONS['klee'];
}

if (!$dbConfig) {
    $dbConfig = array(
        'driver' => getenv('DB_DRIVER') ?: 'mysql',
        'host' => getenv('DB_HOST') ?: '',
        'dbname' => getenv('DB_NAME') ?: '',
        'user' => getenv('DB_USER') ?: '',
        'password' => getenv('DB_PASS') ?: '',
    );
}

if (empty($dbConfig['host']) || empty($dbConfig['dbname']) || empty($dbConfig['user'])) {
    $print('ERROR', 'Configuración de DB incompleta (host/dbname/user).');
} else {
    try {
        if (($dbConfig['driver'] ?? 'mysql') !== 'mysql') {
            throw new RuntimeException('Driver no soportado por doctor: ' . ($dbConfig['driver'] ?? 'null'));
        }

        $dsn = "mysql:host={$dbConfig['host']};dbname={$dbConfig['dbname']};charset=utf8mb4";
        $pdo = new PDO($dsn, $dbConfig['user'], $dbConfig['password'] ?? '');
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->query('SELECT 1');
        $print('OK', 'Conexión DB correcta.');
    } catch (Exception $e) {
        $print('ERROR', 'Conexión DB falló: ' . $e->getMessage());
    }
}

echo str_repeat('-', 60) . "\n";
echo "Resumen: errors={$errors} warnings={$warnings}\n";

if ($strictMode) {
    echo "Modo: strict\n";
} else {
    echo "Modo: normal\n";
}

if ($errors > 0) {
    exit(2);
}
if ($warnings > 0 && $strictMode) {
    exit(1);
}
exit(0);
