<?php

declare(strict_types=1);

namespace SiteFort\Modules\Firewall\ExtendedProtection;

use SiteFort\Includes\FilesystemMutation;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Deploys the must-use fallback loader for hosts that do not honor
 * user-managed auto_prepend_file directives.
 */
final class MuPluginLoader {

    public const FILENAME       = '00-sitefort-early-loader.php';
    public const VERSION        = 1;
    public const VERSION_MARKER = 'SITEFORT_EARLY_LOADER_VERSION';

    public static function get_directory(): string {
        if ( defined( 'WPMU_PLUGIN_DIR' ) ) {
            return (string) WPMU_PLUGIN_DIR;
        }

        return ( defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : ABSPATH . 'wp-content' ) . '/mu-plugins';
    }

    public static function get_path(): string {
        return self::get_directory() . '/' . self::FILENAME;
    }

    /**
     * True if a SiteFort-owned loader file is present on disk.
     *
     * Detection looks for the SiteFort version marker so we never
     * mistake an unrelated mu-plugin for our own.
     */
    public static function is_installed(): bool {
        $path = self::get_path();
        if ( ! is_file( $path ) ) {
            return false;
        }

        $contents = self::read_existing_contents( $path );
        return $contents !== null && strpos( $contents, self::VERSION_MARKER ) !== false;
    }

    /**
     * True when the deployed loader is missing or older than the bundled version.
     */
    public static function needs_update( string $bootstrap_path ): bool {
        $path = self::get_path();
        if ( ! is_file( $path ) ) {
            return true;
        }

        $contents = self::read_existing_contents( $path );
        if ( $contents === null ) {
            return true;
        }

        $expected = self::build_contents( $bootstrap_path );
        return ! hash_equals( sha1( $expected ), sha1( $contents ) );
    }

    /**
     * Write the loader to wp-content/mu-plugins atomically.
     *
     * @return array{success: bool, error?: string}
     */
    public static function deploy( string $bootstrap_path ): array {
        $directory = self::get_directory();
        if ( ! FilesystemMutation::ensure_directory( $directory ) ) {
            return [
                'success' => false,
                'error'   => 'Could not create mu-plugins directory: ' . $directory,
            ];
        }

        $path = self::get_path();

        if ( is_file( $path ) ) {
            $existing = self::read_existing_contents( $path );
            if ( $existing === null ) {
                return [
                    'success' => false,
                    'error'   => 'SiteFort cannot inspect the existing ' . self::FILENAME . ' file safely.',
                ];
            }

            if ( strpos( $existing, self::VERSION_MARKER ) === false ) {
                return [
                    'success' => false,
                    'error'   => 'A different mu-plugin already owns ' . self::FILENAME . '. Rename it before enabling SiteFort early-loader fallback.',
                ];
            }
        }

        $contents = self::build_contents( $bootstrap_path );
        $mode = defined( 'FS_CHMOD_FILE' ) ? (int) FS_CHMOD_FILE : 0644;
        $temp = $path . '.tmp.' . FilesystemMutation::temporary_suffix();

        if ( ! FilesystemMutation::write_bytes_to_file( $temp, $contents, $mode ) ) {
            return [
                'success' => false,
                'error'   => 'SiteFort could not write the Early Loader fallback file.',
            ];
        }

        if ( ! FilesystemMutation::move_path( $temp, $path ) ) {
            FilesystemMutation::delete_path( $temp );
            return [
                'success' => false,
                'error'   => 'SiteFort could not replace the Early Loader fallback file safely.',
            ];
        }

        if ( ! FilesystemMutation::verify_written_contents( $path, $contents ) ) {
            return [
                'success' => false,
                'error'   => 'SiteFort early-loader was written but verification failed.',
            ];
        }

        return [ 'success' => true ];
    }

    /**
     * Remove the loader file when the WAF is uninstalled.
     *
     * Refuses to remove a file that does not carry the SiteFort version
     * marker so unrelated mu-plugins cannot be deleted by accident.
     */
    public static function remove(): array {
        $path = self::get_path();
        if ( ! is_file( $path ) ) {
            return [ 'success' => true, 'removed' => false ];
        }

        $contents = self::read_existing_contents( $path );
        if ( $contents === null || strpos( $contents, self::VERSION_MARKER ) === false ) {
            return [
                'success' => false,
                'error'   => 'Refusing to remove ' . self::FILENAME . ' because it is not owned by SiteFort.',
            ];
        }

        if ( ! FilesystemMutation::delete_path( $path ) ) {
            return [
                'success' => false,
                'error'   => 'Could not remove SiteFort early-loader at ' . $path . '.',
            ];
        }

        return [ 'success' => true, 'removed' => true ];
    }

    /**
     * @return string|null File contents, or null when the file cannot be read.
     */
    private static function read_existing_contents( string $path ) {
        $contents = FilesystemMutation::read_file_contents( $path );
        return is_string( $contents ) ? $contents : null;
    }

    /**
     * Generate the loader source.
     *
     * Uses sprintf with var_export literals so the embedded path is
     * always a valid PHP string regardless of contents.
     */
    private static function build_contents( string $bootstrap_path ): string {
        $version           = (int) self::VERSION;
        $marker            = self::VERSION_MARKER;
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export -- Generating a quoted PHP path literal for embedding in the must-use plugin source is the intended use of var_export().
        $bootstrap_literal = var_export( $bootstrap_path, true );

        $template = <<<'PHP'
<?php
/**
 * Plugin Name: SiteFort Early Loader (Must-Use)
 * Description: Loads the SiteFort firewall on hosts that do not allow user-managed auto_prepend_file directives. Managed automatically by SiteFort, do not edit by hand.
 * Author: SiteFort
 * Version: %1$d
 */

if ( ! defined( 'ABSPATH' ) ) {
    return;
}

if ( ! defined( '%2$s' ) ) {
    define( '%2$s', %1$d );
}

if ( defined( 'SITEFORT_WAF_LOADED' ) ) {
    return;
}

$sitefort_early_loader_bootstrap = %3$s;

if ( is_string( $sitefort_early_loader_bootstrap )
    && $sitefort_early_loader_bootstrap !== ''
    && is_file( $sitefort_early_loader_bootstrap )
    && is_readable( $sitefort_early_loader_bootstrap )
) {
    include_once $sitefort_early_loader_bootstrap;
}

unset( $sitefort_early_loader_bootstrap );
PHP;

        return sprintf( $template, $version, $marker, $bootstrap_literal ) . "\n";
    }
}
