<?php

namespace WPD\Diagnostics\Checks;

use WPD\Diagnostics\CheckInterface;
use WPD\Domain\CheckResult;
use WPD\Domain\Finding;
use WPD\Domain\ScanContext;

final class SyntaxCheck implements CheckInterface
{
    public function id()
    {
        return 'php_syntax';
    }

    public function label()
    {
        return 'Sintaxis de archivos de carga';
    }

    public function run(ScanContext $context, array $cursor)
    {
        $paths = $this->candidates($context);
        $offset = isset($cursor['offset']) ? (int) $cursor['offset'] : 0;
        $errors = isset($cursor['errors']) ? $cursor['errors'] : array();
        $skipped = isset($cursor['skipped']) ? (int) $cursor['skipped'] : 0;
        $limit = min(count($paths), $offset + 20);

        for ($i = $offset; $i < $limit; $i++) {
            $relative = $paths[$i];
            $safe = $context->guard()->safeFile($context->root() . '/' . $relative);
            if ($safe === null || !is_readable($safe) || filesize($safe) > 524288) {
                $skipped++;
                continue;
            }
            $source = @file_get_contents($safe);
            if (!is_string($source)) {
                $skipped++;
                continue;
            }
            try {
                token_get_all($source, TOKEN_PARSE);
            } catch (\ParseError $error) {
                if (count($errors) < 15) {
                    $errors[] = $relative . ': línea ' . max(1, (int) $error->getLine());
                }
            }
        }

        if ($limit < count($paths)) {
            return new CheckResult(false, array('offset' => $limit, 'errors' => $errors, 'skipped' => $skipped), array(), 'correcta', 'Analizados ' . $limit . ' de ' . count($paths) . ' archivos de carga.');
        }

        $findings = array();
        if ($errors) {
            $findings[] = new Finding(
                'php_syntax.parse_errors',
                'Hay archivos PHP que no se pueden interpretar',
                'problema confirmado',
                'crítica',
                implode(' | ', $errors),
                'Un error de sintaxis en un archivo cargado detendrá WordPress con un error fatal.',
                array('Haz una copia del archivo señalado.', 'Comprueba si el archivo quedó incompleto durante una subida.', 'Restaura la misma versión oficial del núcleo, plugin o tema correspondiente.'),
                'Repite el diagnóstico y confirma que ya no aparece el error.'
            );
        }
        if ($skipped > 0) {
            $findings[] = new Finding(
                'php_syntax.partial',
                'Algunos archivos de carga no se analizaron',
                'información',
                'informativa',
                'Archivos omitidos por tamaño, permisos o enlace simbólico: ' . $skipped . '.',
                'La comprobación de sintaxis no cubre esos archivos.',
                array('Revisa permisos y tamaño de los archivos omitidos.', 'Consulta también los registros de errores del servidor.'),
                'Repite el análisis cuando sean legibles.',
                'parcial'
            );
        }
        return CheckResult::complete($findings, $skipped > 0 ? 'parcial' : null, 'Archivos de carga examinados: ' . count($paths) . '.');
    }

    private function candidates(ScanContext $context)
    {
        $patterns = array(
            $context->root() . '/*.php',
            $context->root() . '/wp-content/plugins/*/*.php',
            $context->root() . '/wp-content/themes/*/*.php',
            $context->root() . '/wp-content/mu-plugins/*.php',
        );
        $files = array();
        foreach ($patterns as $pattern) {
            $matches = glob($pattern);
            if (!is_array($matches)) {
                continue;
            }
            foreach ($matches as $path) {
                if (!is_link($path) && is_file($path)) {
                    $files[] = ltrim(substr($path, strlen($context->root())), '/\\');
                }
            }
        }
        sort($files, SORT_STRING);
        return array_slice(array_values(array_unique($files)), 0, 250);
    }
}
