<?php
/**
 * Plugin Signature Service
 *
 * Produces lightweight signatures (mtime, size, version) for installed plugin
 * files. Consumed by the upload flow to verify that a re-upload actually
 * replaced the files on disk -- prevents the false-positive "100% installed"
 * UI when WordPress silently no-ops the replace (e.g. when overwriting the
 * currently running plugin under opcache/file locks).
 *
 * @package FleekDash\Controllers\Plugins
 * @since   2026-05
 */

namespace FleekDash\Controllers\Plugins;

class PluginSignatureService
{
    /**
     * Sanitize a caller-provided identifier (full plugin file path or folder slug)
     * and resolve it to a real plugin main file when possible.
     *
     * @return string Plugin main file (e.g. "fleekdash/fleekdash.php") or empty string.
     */
    public function resolveTargetPath(string $raw): string
    {
        $resolved = '';
        $trimmed = \trim($raw);

        $isSafe = $trimmed !== ''
            && \strpos($trimmed, '..') === false
            && \strpos($trimmed, "\0") === false;

        if ($isSafe) {
            $hasSeparator = \strpos($trimmed, '/') !== false || \strpos($trimmed, '\\') !== false;
            $resolved = $hasSeparator
                ? \str_replace('\\', '/', $trimmed)
                : $this->resolveMainFileFromSlug($trimmed);
        }

        return $resolved;
    }

    /**
     * Build a signature payload describing the current state of a plugin file
     * on disk. Returns a deterministic shape so the JS caller can compare
     * pre-upload vs post-upload reliably.
     *
     * @return array{file: string, exists: bool, mtime: int|null, size: int|null, version: string}
     */
    public function buildPayload(string $plugin_file): array
    {
        $path = \WP_PLUGIN_DIR . '/' . $plugin_file;
        $exists = $plugin_file !== '' && \file_exists($path);

        $mtime = null;
        $size = null;
        $version = '';

        if ($exists) {
            $rawMtime = @\filemtime($path);
            if ($rawMtime !== false) {
                $mtime = (int) $rawMtime;
            }
            $rawSize = @\filesize($path);
            if ($rawSize !== false) {
                $size = (int) $rawSize;
            }
            $version = $this->extractVersion($path);
        }

        return [
            'file' => $plugin_file,
            'exists' => $exists,
            'mtime' => $mtime,
            'size' => $size,
            'version' => $version,
        ];
    }

    /**
     * Find the main plugin file for a given folder slug.
     * Only checks installed plugins -- returns empty string when not found.
     */
    private function resolveMainFileFromSlug(string $slug): string
    {
        $match = '';
        if (!\function_exists('get_plugins')) {
            return $match;
        }

        $plugins = \get_plugins() ?: [];
        foreach ($plugins as $plugin_file => $_data) {
            $folder = \strtok($plugin_file, '/');
            if (\is_string($folder) && $folder === $slug) {
                $match = $plugin_file;
                break;
            }
        }
        return $match;
    }

    private function extractVersion(string $path): string
    {
        $version = '';
        if (\function_exists('get_plugin_data')) {
            $data = \get_plugin_data($path, false, false);
            if (\is_array($data) && isset($data['Version']) && \is_string($data['Version'])) {
                $version = $data['Version'];
            }
        }
        return $version;
    }
}
