<?php

declare(strict_types=1);

namespace ShieldX\Storage;

defined('ABSPATH') || exit;

use ShieldX\Support\WordPressPaths;

/**
 * Small wrapper for host-managed filesystem writes.
 *
 * SHIELD-X still reads files directly for scanners and runtime checks, but
 * admin-triggered host-managed writes should go through WordPress' filesystem
 * layer whenever it is available so hosts can enforce their normal ownership
 * and credential model.
 */
final class FilesystemAdapter
{
    public static function fromWordPress(): self
    {
        return new self();
    }

    /**
     * @return array{ok:bool,code:string,message:string}
     */
    public function chmod(string $path, int $mode, string $root = ''): array
    {
        if ($path === '' || $mode <= 0) {
            return [
                'ok' => false,
                'code' => 'invalid_request',
                'message' => __('Invalid permission change request.', 'xerotact-shield-x-security'),
            ];
        }

        if (!self::permissionChangeAllowed($path, $root)) {
            return [
                'ok' => false,
                'code' => 'outside_site_root',
                'message' => __('Permission changes are allowed only for files and directories inside the WordPress document root.', 'xerotact-shield-x-security'),
            ];
        }

        $filesystem = $this->wordpressFilesystem('chmod');
        if (!$filesystem) {
            return [
                'ok' => false,
                'code' => 'filesystem_unavailable',
                'message' => __('WordPress filesystem access is not available for this request.', 'xerotact-shield-x-security'),
            ];
        }

        $ok = (bool) $filesystem->chmod($path, $mode, false);
        clearstatcache(false, $path);

        if ($ok) {
            return [
                'ok' => true,
                'code' => 'changed',
                'message' => '',
            ];
        }

        return [
            'ok' => false,
            'code' => 'chmod_failed',
            'message' => __('WordPress filesystem could not change this path. Review host ownership or filesystem credentials.', 'xerotact-shield-x-security'),
        ];
    }

    public static function permissionChangeAllowed(string $path, string $root = ''): bool
    {
        $root = $root !== '' ? $root : WordPressPaths::root();
        $realRoot = realpath($root);
        $realPath = realpath($path);
        if (!is_string($realRoot) || !is_string($realPath) || $realRoot === '' || $realPath === '') {
            return false;
        }

        $root = rtrim(str_replace('\\', '/', $realRoot), '/');
        $path = rtrim(str_replace('\\', '/', $realPath), '/');
        if ($root === '' || $path === '' || $path === $root) {
            return false;
        }

        return strpos($path . '/', $root . '/') === 0;
    }

    public static function nativeChmod(string $path, int $mode, string $root = ''): bool
    {
        if ($mode <= 0 || !self::permissionChangeAllowed($path, $root)) {
            return false;
        }

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- This protects legacy isolated native writes behind the same document-root boundary as the WordPress filesystem adapter.
        return @chmod($path, $mode);
    }

    /**
     * @return array{ok:bool,code:string,message:string}
     */
    public function putContents(string $path, string $contents, int $mode = 0644): array
    {
        if ($path === '') {
            return [
                'ok' => false,
                'code' => 'invalid_request',
                'message' => __('Invalid filesystem write request.', 'xerotact-shield-x-security'),
            ];
        }

        $filesystem = $this->wordpressFilesystem('put_contents');
        if (!$filesystem) {
            return [
                'ok' => false,
                'code' => 'filesystem_unavailable',
                'message' => __('WordPress filesystem access is not available for this request.', 'xerotact-shield-x-security'),
            ];
        }

        $ok = (bool) $filesystem->put_contents($path, $contents, $mode > 0 ? $mode : false);
        clearstatcache(false, $path);

        if ($ok) {
            return [
                'ok' => true,
                'code' => 'written',
                'message' => '',
            ];
        }

        return [
            'ok' => false,
            'code' => 'write_failed',
            'message' => __('WordPress filesystem could not write this path. Review host ownership or filesystem credentials.', 'xerotact-shield-x-security'),
        ];
    }

    /**
     * @return array{ok:bool,code:string,message:string}
     */
    public function copy(string $source, string $destination, int $mode = 0644): array
    {
        if ($source === '' || $destination === '') {
            return [
                'ok' => false,
                'code' => 'invalid_request',
                'message' => __('Invalid filesystem copy request.', 'xerotact-shield-x-security'),
            ];
        }

        $filesystem = $this->wordpressFilesystem('copy');
        if (!$filesystem) {
            return [
                'ok' => false,
                'code' => 'filesystem_unavailable',
                'message' => __('WordPress filesystem access is not available for this request.', 'xerotact-shield-x-security'),
            ];
        }

        $ok = (bool) $filesystem->copy($source, $destination, true, $mode > 0 ? $mode : false);
        clearstatcache(false, $destination);

        if ($ok) {
            return [
                'ok' => true,
                'code' => 'copied',
                'message' => '',
            ];
        }

        return [
            'ok' => false,
            'code' => 'copy_failed',
            'message' => __('WordPress filesystem could not copy this path. Review host ownership or filesystem credentials.', 'xerotact-shield-x-security'),
        ];
    }

    /**
     * @return array{ok:bool,code:string,message:string}
     */
    public function move(string $source, string $destination, bool $overwrite = false): array
    {
        if ($source === '' || $destination === '') {
            return [
                'ok' => false,
                'code' => 'invalid_request',
                'message' => __('Invalid filesystem move request.', 'xerotact-shield-x-security'),
            ];
        }

        $filesystem = $this->wordpressFilesystem('move');
        if (!$filesystem) {
            return [
                'ok' => false,
                'code' => 'filesystem_unavailable',
                'message' => __('WordPress filesystem access is not available for this request.', 'xerotact-shield-x-security'),
            ];
        }

        $ok = (bool) $filesystem->move($source, $destination, $overwrite);
        clearstatcache(false, $source);
        clearstatcache(false, $destination);

        if ($ok) {
            return [
                'ok' => true,
                'code' => 'moved',
                'message' => '',
            ];
        }

        return [
            'ok' => false,
            'code' => 'move_failed',
            'message' => __('WordPress filesystem could not move this path. Review host ownership or filesystem credentials.', 'xerotact-shield-x-security'),
        ];
    }

    /**
     * @return array{ok:bool,code:string,message:string}
     */
    public function delete(string $path): array
    {
        if ($path === '') {
            return [
                'ok' => false,
                'code' => 'invalid_request',
                'message' => __('Invalid filesystem delete request.', 'xerotact-shield-x-security'),
            ];
        }

        if (!file_exists($path) && !is_link($path)) {
            return [
                'ok' => true,
                'code' => 'missing',
                'message' => '',
            ];
        }

        $filesystem = $this->wordpressFilesystem('delete');
        if (!$filesystem) {
            return [
                'ok' => false,
                'code' => 'filesystem_unavailable',
                'message' => __('WordPress filesystem access is not available for this request.', 'xerotact-shield-x-security'),
            ];
        }

        $ok = (bool) $filesystem->delete($path, false, is_dir($path) && !is_link($path) ? 'd' : 'f');
        clearstatcache(false, $path);

        if ($ok) {
            return [
                'ok' => true,
                'code' => 'deleted',
                'message' => '',
            ];
        }

        return [
            'ok' => false,
            'code' => 'delete_failed',
            'message' => __('WordPress filesystem could not delete this path. Review host ownership or filesystem credentials.', 'xerotact-shield-x-security'),
        ];
    }

    /**
     * @return object|null
     */
    private function wordpressFilesystem(string $method)
    {
        global $wp_filesystem;

        if ($method !== '' && is_object($wp_filesystem) && method_exists($wp_filesystem, $method)) {
            return $wp_filesystem;
        }

        if (!function_exists('WP_Filesystem')) {
            $filesystemApi = WordPressPaths::adminIncludesPath('file.php');
            if (is_readable($filesystemApi)) {
                require_once $filesystemApi;
            }
        }

        if (!function_exists('WP_Filesystem')) {
            return null;
        }

        WP_Filesystem(false, false, true);

        return $method !== '' && is_object($wp_filesystem) && method_exists($wp_filesystem, $method) ? $wp_filesystem : null;
    }
}
