<?php

namespace CleantalkSP\SpbctWP\Scanner\FileOfPluginCheckerModule;

/**
 * Scanner for WordPress plugin source files to extract plugin information.
 *
 * This class analyzes files within a WordPress plugins directory to identify
 * plugin files and extract metadata such as plugin slug, version, and root file.
 */
class FileOfPluginChecker
{
    /**
     * Path to the WordPress plugins directory.
     *
     * @var string
     */
    private $plugins_dir = '';

    /**
     * @var FileOfPluginCheckerVerdict
     */
    private $verdict;

    /**
     * Constructor for the plugin scanner.
     *
     * @param string $expected_plugins_dir Path to the WordPress plugins directory.
     */
    public function __construct($expected_plugins_dir)
    {
        $this->verdict = new FileOfPluginCheckerVerdict();
        if (
            empty($expected_plugins_dir) ||
            ! is_string($expected_plugins_dir) ||
            ! is_dir($expected_plugins_dir) ||
            ! is_readable($expected_plugins_dir)
        ) {
            $this->verdict->init_error = true;
            return;
        }

        $this->plugins_dir = $this->normalizePath($expected_plugins_dir);
        $this->verdict->setPluginsDir($this->plugins_dir);
    }

    /**
     * Determine if a file belongs to a WordPress plugin and extract plugin information.
     *
     * @param string $file_path Path to the file to analyze.
     *
     * @return FileOfPluginCheckerVerdict Verdict
     */
    public function getVerdict($file_path)
    {
        if ( $this->verdict->init_error ) {
            return $this->verdict;
        }

        $normalized_path = $this->normalizePath($file_path);

        if ( ! $this->isInPluginsDirectory($normalized_path) ) {
            $this->verdict->addMessage('Not in plugins directory ', $normalized_path);
            return $this->verdict;
        }

        $slug = $this->extractPluginSlug($normalized_path);
        if ( is_null($slug) ) {
            $this->verdict->addMessage('Can not extract slug.', $normalized_path);
            return $this->verdict;
        }

        $this->verdict->setSlug($slug);

        $root_file = $this->findPluginRootFile($this->plugins_dir . '/' . $slug, $slug);
        if ( is_null($root_file) ) {
            $this->verdict->addMessage('Can not find root file in slug ' . $slug, $normalized_path);
            return $this->verdict;
        }

        $this->verdict->setRootFile($root_file);

        $version = $this->getPluginVersion($this->plugins_dir . '/' . $slug . '/' . $root_file);

        if (is_null($version)) {
            $this->verdict->addMessage('Can not get root file version.', $normalized_path);
            return $this->verdict;
        }

        $this->verdict->setVersion($version);

        return $this->verdict->success();
    }

    /**
     * Normalize file path by converting backslashes to forward slashes and trimming.
     *
     * @param string $path The path to normalize.
     *
     * @return string Normalized path.
     */
    private function normalizePath($path)
    {
        return rtrim(str_replace('\\', '/', $path), '/\\');
    }

    /**
     * Check if a normalized path is within the plugins directory.
     *
     * @param string $normalized_path Normalized file path.
     *
     * @return bool True if path is in plugins directory, false otherwise.
     */
    private function isInPluginsDirectory($normalized_path)
    {
        return strpos($normalized_path, $this->plugins_dir) === 0;
    }

    /**
     * Extract the plugin slug from a normalized file path.
     *
     * @param string $normalized_path Normalized file path.
     *
     * @return string|null Plugin slug or null if unable to extract.
     */
    private function extractPluginSlug($normalized_path)
    {
        // Is of plugins dir?
        if (strpos($normalized_path, $this->plugins_dir) !== 0) {
            return null;
        }

        $relative_path = substr($normalized_path, strlen($this->plugins_dir));
        $relative_path = ltrim($relative_path, '/');

        $parts = explode('/', $relative_path);

        return empty($parts[0]) ? null : $parts[0];
    }

    /**
     * Find the root plugin file by scanning for WordPress plugin headers.
     *
     * @param string $plugin_dir Full path to the plugin directory.
     * @param string $slug Plugin slug used for priority matching.
     *
     * @return string|null Name of the root plugin file or null if not found.
     */
    private function findPluginRootFile($plugin_dir, $slug)
    {
        if ( ! is_dir($plugin_dir) || ! is_readable($plugin_dir) ) {
            return null;
        }

        $potential_files = [];
        $files           = scandir($plugin_dir);

        foreach ( $files as $file ) {
            if ( ! $this->isRootPhpFile($plugin_dir, $file) ) {
                continue;
            }

            if ( $this->hasPluginHeader($plugin_dir . '/' . $file) ) {
                $potential_files[] = $file;
            }
        }

        return $this->selectRootFile($potential_files, $slug);
    }

    /**
     * Check if a file is a PHP file in the plugin's root directory.
     *
     * @param string $plugin_dir Plugin directory path.
     * @param string $file File name to check.
     *
     * @return bool True if file is a PHP file in root directory.
     */
    private function isRootPhpFile($plugin_dir, $file)
    {
        if ($file === '.' || $file === '..') {
            return false;
        }

        $file_path = $plugin_dir . '/' . $file;

        return is_file($file_path) && pathinfo($file, PATHINFO_EXTENSION) === 'php';
    }

    /**
     * Check if a PHP file contains WordPress plugin headers.
     *
     * @param string $file_path Full path to the file to check.
     *
     * @return bool True if file contains plugin headers.
     */
    private function hasPluginHeader($file_path)
    {
        if (!file_exists($file_path) || !is_readable($file_path)) {
            return false;
        }

        // First 8 kb
        $content = file_get_contents($file_path, false, null, 0, 8192);

        if ($content === false) {
            return false;
        }

        return preg_match('/\*?\s*Plugin Name:\s*.+/i', $content) === 1;
    }

    /**
     * Select the most appropriate root file from potential candidates.
     *
     * @param array $potential_files Array of potential root file names.
     * @param string $slug Plugin slug for priority matching.
     *
     * @return string|null Selected root file name or null if no candidates.
     */
    private function selectRootFile($potential_files, $slug)
    {
        if ( empty($potential_files) ) {
            return null;
        }

        // Priority 1: file with name exactly matching slug
        foreach ( $potential_files as $file ) {
            if ( pathinfo($file, PATHINFO_FILENAME) === $slug ) {
                return $file;
            }
        }

        // Priority 2: file with name containing slug
        foreach ( $potential_files as $file ) {
            if ( strpos(pathinfo($file, PATHINFO_FILENAME), $slug) !== false ) {
                return $file;
            }
        }

        // Priority 3: first PHP file with plugin header
        return $potential_files[0];
    }

    /**
     * Extract plugin version from the plugin's root file headers.
     *
     * @param string $file_path Path to the plugin's root file.
     *
     * @return string|null Version string or null if not found.
     */
    private function getPluginVersion($file_path)
    {
        if ( ! file_exists($file_path) || !is_readable($file_path) ) {
            return null;
        }

        // Only start of file
        $content = file_get_contents($file_path, false, null, 0, 8192);

        if ( preg_match('/^\s*[\/*#@\s]*Version:\s*([^\r\n]+)$/mi', $content, $match) ) {
            return trim(preg_replace('/^\s*[\/*#@\s]+/', '', $match[1]));
        }

        return null;
    }
}
