<?php

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

trait Niwa_AI_Tools_File_Ops {

    private function get_file_guard() {
        if ( null === $this->file_guard ) {
            if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_File_Guard' ) ) {
                $guard_file = NIWA_AI_DIR . 'includes/admin-autonomy/class-admin-autonomy-file-guard.php';
                if ( file_exists( $guard_file ) ) {
                    require_once $guard_file;
                }
            }

            $this->file_guard = class_exists( 'Niwa_AI_Admin_Autonomy_File_Guard' )
                ? new Niwa_AI_Admin_Autonomy_File_Guard()
                : null;
        }

        return $this->file_guard;
    }

    private function build_runtime_reload_metadata( $relative_path ) {
        $relative_path = wp_normalize_path( ltrim( (string) $relative_path, '/' ) );
        $requires_reload = false !== strpos( $relative_path, 'wp-content/plugins/' )
            || false !== strpos( $relative_path, 'wp-content/mu-plugins/' );
        $requires_reload = $requires_reload && (bool) preg_match( '/\.php$/i', $relative_path );

        return array(
            'runtime_reload_required' => $requires_reload,
            'runtime_reload_scope'    => $requires_reload ? 'next_request' : 'none',
            'runtime_reload_reason'   => $requires_reload ? 'Patched local plugin PHP runtime. New or changed capabilities become available on the next request, not later in this same request.' : '',
        );
    }

    private function get_staged_file_transaction() {
        if ( ! class_exists( 'Niwa_AI_Admin_Autonomy_Staged_File_Transaction' ) ) {
            $file = NIWA_AI_DIR . 'includes/admin-autonomy/class-admin-autonomy-staged-file-transaction.php';
            if ( file_exists( $file ) ) {
                require_once $file;
            }
        }

        $workspace_root = '';
        if ( ! empty( $this->runtime_context['workspace_root'] ) ) {
            $workspace_root = (string) $this->runtime_context['workspace_root'];
        }

        return class_exists( 'Niwa_AI_Admin_Autonomy_Staged_File_Transaction' )
            ? new Niwa_AI_Admin_Autonomy_Staged_File_Transaction( $workspace_root )
            : null;
    }

    private function has_core_code_double_confirmation( $args ) {
        $args     = is_array( $args ) ? $args : array();
        $approval = isset( $args['core_code_approval'] ) && is_array( $args['core_code_approval'] ) ? $args['core_code_approval'] : array();
        return ! empty( $approval['user_confirmed_core_risk'] );
    }

    private function validate_core_code_approval_for_write( $relative_path, $args ) {
        $guard = $this->get_file_guard();
        if ( ! $guard || ! method_exists( $guard, 'is_wordpress_core_code_path' ) || ! $guard->is_wordpress_core_code_path( $relative_path ) ) {
            return array( 'success' => true );
        }

        $args     = is_array( $args ) ? $args : array();
        $approval = isset( $args['core_code_approval'] ) && is_array( $args['core_code_approval'] ) ? $args['core_code_approval'] : array();
        if ( empty( $approval['user_confirmed_core_risk'] ) ) {
            return array(
                'success'                      => false,
                'requires_double_confirmation' => true,
                'error'                        => 'WordPress core edit blocked. This can break the whole site and can be overwritten by the next WordPress update. Explain the exact file, exact change, staged backup/rollback path, and ask the founder for a second explicit confirmation before retrying with core_code_approval.user_confirmed_core_risk=true.',
            );
        }

        $confirmed_path = isset( $approval['confirmed_relative_path'] ) ? wp_normalize_path( ltrim( (string) $approval['confirmed_relative_path'], '/' ) ) : '';
        $relative_path  = wp_normalize_path( ltrim( (string) $relative_path, '/' ) );
        if ( '' !== $confirmed_path && $confirmed_path !== $relative_path ) {
            return array(
                'success' => false,
                'error'   => 'Core code approval path does not match the target file.',
            );
        }

        return array( 'success' => true );
    }

    private function build_write_safety_strategy( $relative_path, $operation, $args ) {
        $guard = $this->get_file_guard();
        if ( $guard && method_exists( $guard, 'classify_write_safety' ) ) {
            return $guard->classify_write_safety( $relative_path, $operation, $args );
        }

        return array(
            'risk_level'             => $guard ? $guard->classify_risk( $relative_path ) : 'medium',
            'strategy'               => 'fast_safe',
            'requires_staged_safety' => false,
            'reasons'                => array(),
        );
    }

    private function execute_staged_file_content_write( $operation, $path, $relative_path, $content, $rollback_data, $expected_hash, $safety_strategy, $executed_action = array() ) {
        $transaction = $this->get_staged_file_transaction();
        if ( ! $transaction ) {
            return array( 'success' => false, 'error' => 'Staged file transaction service is not available.' );
        }

        $result = $transaction->commit_content(
            array(
                'operation'      => $operation,
                'target_path'    => $path,
                'relative_path'  => $relative_path,
                'content'        => $content,
                'rollback_entry' => $rollback_data,
                'expected_hash'  => $expected_hash,
                'risk'           => $safety_strategy,
            )
        );

        if ( empty( $result['success'] ) ) {
            return $result;
        }

        $executed_action = is_array( $executed_action ) ? $executed_action : array();
        $executed_action = array_merge(
            array(
                'type'          => $operation,
                'path'          => $path,
                'staged_job_id' => isset( $result['staged_job_id'] ) ? (string) $result['staged_job_id'] : '',
                'safety'        => 'staged_safety',
            ),
            $executed_action
        );

        $runtime_meta = $this->build_runtime_reload_metadata( $relative_path );

        return array(
            'success'                 => true,
            'summary'                 => 'Staged safety deploy zavrsen za fajl: ' . $relative_path,
            'executed_actions'        => array( $executed_action ),
            'rollback_entries'        => array( $rollback_data ),
            'staged_safety'           => true,
            'staged_job_id'           => isset( $result['staged_job_id'] ) ? (string) $result['staged_job_id'] : '',
            'verification'            => isset( $result['verification'] ) ? $result['verification'] : array(),
            'post_check'              => isset( $result['post_check'] ) ? $result['post_check'] : array(),
            'runtime_reload_required' => ! empty( $runtime_meta['runtime_reload_required'] ),
            'runtime_reload_scope'    => isset( $runtime_meta['runtime_reload_scope'] ) ? (string) $runtime_meta['runtime_reload_scope'] : 'none',
            'runtime_reload_reason'   => isset( $runtime_meta['runtime_reload_reason'] ) ? (string) $runtime_meta['runtime_reload_reason'] : '',
        );
    }

    private function is_theme_code_path( $relative_path ) {
        $relative_path = wp_normalize_path( ltrim( (string) $relative_path, '/' ) );
        return (bool) preg_match( '#^wp-content/themes/[^/]+/.+#i', $relative_path );
    }

    private function validate_theme_code_approval_for_write( $relative_path, $args ) {
        // Theme work is normal senior WP development for Niwa runtime edits. Risky PHP
        // files are still protected by staged-safety rollback/post-checks, so requiring
        // a fresh founder approval for every theme file made legitimate custom themes
        // and HTML widgets impossible to build autonomously.
        return array( 'success' => true );
    }

    private function build_patch_scope_window( $contents, $patch ) {
        $contents   = (string) $contents;
        $patch      = is_array( $patch ) ? $patch : array();
        $start_line = isset( $patch['start_line'] ) ? max( 0, intval( $patch['start_line'] ) ) : 0;
        $end_line   = isset( $patch['end_line'] ) ? max( 0, intval( $patch['end_line'] ) ) : 0;

        if ( $start_line <= 0 && $end_line <= 0 ) {
            return array(
                'success'     => true,
                'prefix'      => '',
                'scope'       => $contents,
                'suffix'      => '',
                'start_line'  => 1,
                'end_line'    => max( 1, substr_count( $contents, "\n" ) + 1 ),
            );
        }

        $line_starts = array( 1 => 0 );
        if ( preg_match_all( "/\r\n|\n|\r/", $contents, $matches, PREG_OFFSET_CAPTURE ) ) {
            $line_no = 1;
            foreach ( $matches[0] as $match ) {
                $line_no++;
                $line_starts[ $line_no ] = intval( $match[1] ) + strlen( (string) $match[0] );
            }
        }

        $total_lines = max( 1, count( $line_starts ) );
        $start_line  = $start_line > 0 ? min( $start_line, $total_lines ) : 1;
        $end_line    = $end_line > 0 ? min( $end_line, $total_lines ) : $total_lines;
        if ( $end_line < $start_line ) {
            return array(
                'success' => false,
                'error'   => 'Patch line range nije validan: end_line mora biti >= start_line.',
            );
        }

        $scope_start = isset( $line_starts[ $start_line ] ) ? intval( $line_starts[ $start_line ] ) : 0;
        $scope_end   = isset( $line_starts[ $end_line + 1 ] ) ? intval( $line_starts[ $end_line + 1 ] ) : strlen( $contents );

        return array(
            'success'     => true,
            'prefix'      => (string) substr( $contents, 0, $scope_start ),
            'scope'       => (string) substr( $contents, $scope_start, $scope_end - $scope_start ),
            'suffix'      => (string) substr( $contents, $scope_end ),
            'start_line'  => $start_line,
            'end_line'    => $end_line,
        );
    }

    private function replace_first_occurrence( $haystack, $needle, $replacement ) {
        $pos = strpos( (string) $haystack, (string) $needle );
        if ( false === $pos ) {
            return (string) $haystack;
        }

        return substr( (string) $haystack, 0, $pos ) . (string) $replacement . substr( (string) $haystack, $pos + strlen( (string) $needle ) );
    }

    private function build_patch_excerpt( $text, $needle, $limit = 140 ) {
        $text   = (string) $text;
        $needle = (string) $needle;
        $limit  = max( 40, intval( $limit ) );

        if ( '' === $text ) {
            return '';
        }

        $pos = '' !== $needle ? strpos( $text, $needle ) : false;
        if ( false === $pos ) {
            return $this->truncate_text( preg_replace( '/\s+/', ' ', $text ), $limit );
        }

        $start = max( 0, intval( $pos ) - 40 );
        $slice = substr( $text, $start, $limit );

        return $this->truncate_text( preg_replace( '/\s+/', ' ', (string) $slice ), $limit );
    }

    private function truncate_text( $text, $limit = 140 ) {
        $text  = trim( (string) $text );
        $limit = max( 20, intval( $limit ) );
        if ( strlen( $text ) <= $limit ) {
            return $text;
        }

        return rtrim( substr( $text, 0, $limit - 3 ) ) . '...';
    }

    private function apply_single_text_patch( $scope, $patch, $idx ) {
        $scope    = (string) $scope;
        $patch    = is_array( $patch ) ? $patch : array();
        $find     = isset( $patch['find'] ) ? (string) $patch['find'] : '';
        $replace  = isset( $patch['replace'] ) ? (string) $patch['replace'] : '';
        $after    = isset( $patch['insert_after'] ) ? (string) $patch['insert_after'] : '';
        $before   = isset( $patch['insert_before'] ) ? (string) $patch['insert_before'] : '';
        $once     = ! empty( $patch['replace_once'] );
        $expected = array_key_exists( 'expected_matches', $patch ) ? intval( $patch['expected_matches'] ) : null;

        if ( null !== $expected && $expected < 0 ) {
            return array(
                'success' => false,
                'error'   => 'Patch #' . ( $idx + 1 ) . ': expected_matches mora biti >= 0.',
            );
        }

        if ( '' !== $find ) {
            $match_count = substr_count( $scope, $find );
            if ( 0 === $match_count ) {
                return array(
                    'success' => false,
                    'error'   => 'Patch #' . ( $idx + 1 ) . ': find tekst nije pronadjen u ciljnom opsegu fajla.',
                );
            }
            if ( null !== $expected && $match_count !== $expected ) {
                return array(
                    'success' => false,
                    'error'   => 'Patch #' . ( $idx + 1 ) . ': expected_matches=' . $expected . ', stvarno pogodaka=' . $match_count . '.',
                );
            }
            if ( $match_count > 1 && ! $once && null === $expected ) {
                return array(
                    'success' => false,
                    'error'   => 'Patch #' . ( $idx + 1 ) . ': find tekst postoji ' . $match_count . ' puta. Dodaj replace_once=true, expected_matches, ili line scope.',
                );
            }

            $updated = $once ? $this->replace_first_occurrence( $scope, $find, $replace ) : str_replace( $find, $replace, $scope );

            return array(
                'success'      => true,
                'updated_scope' => $updated,
                'preview'      => array(
                    'op'           => '' === $replace ? 'delete' : 'replace',
                    'match_count'  => $match_count,
                    'replace_once' => $once ? 1 : 0,
                    'before'       => $this->build_patch_excerpt( $scope, $find ),
                    'after'        => $this->build_patch_excerpt( $updated, '' !== $replace ? $replace : $find ),
                ),
            );
        }

        $anchor    = '' !== $after ? $after : $before;
        $match_count = '' !== $anchor ? substr_count( $scope, $anchor ) : 0;
        if ( '' === $anchor || 0 === $match_count ) {
            return array(
                'success' => false,
                'error'   => 'Patch #' . ( $idx + 1 ) . ': anchor tekst nije pronadjen u ciljnom opsegu fajla.',
            );
        }
        if ( null !== $expected && $match_count !== $expected ) {
            return array(
                'success' => false,
                'error'   => 'Patch #' . ( $idx + 1 ) . ': expected_matches=' . $expected . ', stvarno anchor pogodaka=' . $match_count . '.',
            );
        }
        if ( $match_count > 1 && ! $once && null === $expected ) {
            return array(
                'success' => false,
                'error'   => 'Patch #' . ( $idx + 1 ) . ': anchor postoji ' . $match_count . ' puta. Dodaj replace_once=true, expected_matches, ili line scope.',
            );
        }

        if ( '' !== $after ) {
            $updated = $once ? $this->replace_first_occurrence( $scope, $after, $after . $replace ) : str_replace( $after, $after . $replace, $scope );
            $op      = 'insert_after';
        } else {
            $updated = $once ? $this->replace_first_occurrence( $scope, $before, $replace . $before ) : str_replace( $before, $replace . $before, $scope );
            $op      = 'insert_before';
        }

        return array(
            'success'       => true,
            'updated_scope' => $updated,
            'preview'       => array(
                'op'           => $op,
                'match_count'  => $match_count,
                'replace_once' => $once ? 1 : 0,
                'before'       => $this->build_patch_excerpt( $scope, $anchor ),
                'after'        => $this->build_patch_excerpt( $updated, $anchor ),
            ),
        );
    }


    private function restore_file_from_snapshot( $entry ) {
        $path     = isset( $entry['path'] ) ? (string) $entry['path'] : '';
        $snapshot = isset( $entry['snapshot_path'] ) ? (string) $entry['snapshot_path'] : '';

        if ( '' === $path || '' === $snapshot || ! is_file( $snapshot ) ) {
            return array( 'success' => false, 'error' => 'Snapshot fajl za rollback ne postoji.' );
        }

        if ( ! @copy( $snapshot, $path ) ) {
            return array( 'success' => false, 'error' => 'Rollback copy iz snapshot-a nije uspeo.' );
        }

        @unlink( $snapshot );
        return array( 'success' => true );
    }

    private function rollback_file_delete( $entry ) {
        $path = isset( $entry['path'] ) ? (string) $entry['path'] : '';
        if ( '' === $path || ! is_file( $path ) ) {
            return array( 'success' => true );
        }

        if ( ! @unlink( $path ) ) {
            return array( 'success' => false, 'error' => 'Ne mogu da obrisem fajl tokom rollback-a: ' . $path );
        }

        return array( 'success' => true );
    }

    /* ================================================================
     * FILE READ — read file contents (uses file guard for security)
     * ============================================================= */

    private function preview_file_read( $args ) {
        $guard = $this->get_file_guard();
        if ( ! $guard ) {
            return array( 'success' => false, 'error' => 'File guard not loaded.' );
        }

        $path = isset( $args['relative_path'] ) ? (string) $args['relative_path'] : '';
        if ( '' === $path ) {
            return array( 'success' => false, 'error' => 'relative_path is required.' );
        }

        $resolved = $guard->validate_read( $path );
        if ( empty( $resolved['success'] ) ) {
            return $resolved;
        }

        $abs_path = (string) $resolved['path'];
        if ( ! file_exists( $abs_path ) ) {
            return array( 'success' => false, 'error' => 'File does not exist: ' . $path );
        }

        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'file_read',
                'label'            => 'Read File',
                'summary'          => 'Read file: ' . $resolved['relative_path'],
                'rollback_plan'    => 'Read-only, no rollback needed.',
                'rollback_support' => 'none',
                'targets'          => array(),
                'args'             => array(
                    'path'          => $abs_path,
                    'relative_path' => $resolved['relative_path'],
                    'tail_lines'    => isset( $args['tail_lines'] ) ? max( 0, min( 500, intval( $args['tail_lines'] ) ) ) : 0,
                ),
            ),
        );
    }

    public function execute_file_read( $args ) {
        $guard = $this->get_file_guard();
        if ( ! $guard ) {
            return array( 'success' => false, 'error' => 'File guard not loaded.' );
        }

        $path = isset( $args['relative_path'] ) ? (string) $args['relative_path'] : ( isset( $args['path'] ) ? (string) $args['path'] : '' );
        if ( '' === $path ) {
            return array( 'success' => false, 'error' => 'relative_path is required.' );
        }

        // If absolute path provided, use it; otherwise resolve via guard.
        $abs_path = $path;
        if ( 0 !== strpos( $path, ABSPATH ) && 0 !== strpos( $path, '/' ) ) {
            $resolved = $guard->validate_read( $path );
            if ( empty( $resolved['success'] ) ) {
                return $resolved;
            }
            $abs_path = (string) $resolved['path'];
        } else {
            $resolved = $guard->validate_read( $path );
            if ( empty( $resolved['success'] ) ) {
                return $resolved;
            }
            $abs_path = (string) $resolved['path'];
        }

        if ( ! file_exists( $abs_path ) ) {
            return array( 'success' => false, 'error' => 'File does not exist.' );
        }

        $relative_path = isset( $resolved['relative_path'] ) ? (string) $resolved['relative_path'] : ltrim( str_replace( ABSPATH, '', $abs_path ), '/\\' );
        $tail_lines    = isset( $args['tail_lines'] ) ? max( 0, min( 500, intval( $args['tail_lines'] ) ) ) : 0;
        $size          = @filesize( $abs_path );
        $max           = defined( 'Niwa_AI_Admin_Autonomy_File_Guard::MAX_READ_BYTES' )
            ? Niwa_AI_Admin_Autonomy_File_Guard::MAX_READ_BYTES
            : 2097152; // 2MB default

        if ( $tail_lines > 0 ) {
            $tail = $this->read_tail_lines_for_file_tool( $abs_path, $tail_lines );
            if ( empty( $tail['success'] ) ) {
                return $tail;
            }

            $lines   = $this->redact_sensitive_file_tool_lines( (array) $tail['lines'], $relative_path );
            $content = implode( "\n", $lines );

            return array(
                'success'     => true,
                'summary'     => 'File tail read: ' . $relative_path . ' (' . count( $lines ) . ' of ' . intval( $tail['line_count'] ) . ' lines, ' . round( intval( $size ) / 1024, 1 ) . ' KB)',
                'file_path'   => $relative_path,
                'file_size'   => false !== $size ? $size : 0,
                'line_count'  => intval( $tail['line_count'] ),
                'start_line'  => intval( $tail['start_line'] ),
                'tail'        => true,
                'truncated'   => false,
                'redacted'    => true,
                'content'     => $content,
            );
        }

        if ( false !== $size && $size > $max ) {
            return array( 'success' => false, 'error' => 'File too large (' . round( $size / 1024 ) . ' KB). Max: ' . round( $max / 1024 ) . ' KB.' );
        }

        $content = @file_get_contents( $abs_path );
        if ( false === $content ) {
            return array( 'success' => false, 'error' => 'Failed to read file.' );
        }

        // Detect binary files.
        if ( ! mb_check_encoding( $content, 'UTF-8' ) ) {
            return array(
                'success'   => true,
                'summary'   => 'Binary file (' . round( $size / 1024, 1 ) . ' KB). Content not returned.',
                'file_path' => $path,
                'file_size' => $size,
                'binary'    => true,
            );
        }

        $line_count = substr_count( $content, "\n" ) + 1;

        // Truncate if very large.
        $max_chars = 200000;
        $truncated = false;
        if ( mb_strlen( $content ) > $max_chars ) {
            $content   = mb_substr( $content, 0, $max_chars );
            $truncated = true;
        }

        $content = implode( "\n", $this->redact_sensitive_file_tool_lines( preg_split( '/\r\n|\r|\n/', $content ), $relative_path ) );

        return array(
            'success'    => true,
            'summary'    => 'File read: ' . $relative_path . ' (' . $line_count . ' lines, ' . round( intval( $size ) / 1024, 1 ) . ' KB)',
            'file_path'  => $relative_path,
            'file_size'  => false !== $size ? $size : 0,
            'line_count' => $line_count,
            'truncated'  => $truncated,
            'redacted'   => true,
            'content'    => $content,
        );
    }

    private function read_tail_lines_for_file_tool( $path, $tail_lines ) {
        $tail_lines = max( 1, min( 500, intval( $tail_lines ) ) );
        $handle     = @fopen( $path, 'r' );
        if ( ! $handle ) {
            return array( 'success' => false, 'error' => 'Failed to open file for tail read.' );
        }

        $lines = array();
        $total = 0;
        while ( false !== ( $line = fgets( $handle ) ) ) {
            $total++;
            $lines[] = rtrim( (string) $line, "\r\n" );
            if ( count( $lines ) > $tail_lines ) {
                array_shift( $lines );
            }
        }
        fclose( $handle );

        return array(
            'success'    => true,
            'lines'      => $lines,
            'line_count' => $total,
            'start_line' => max( 1, $total - count( $lines ) + 1 ),
        );
    }

    private function redact_sensitive_file_tool_lines( $lines, $relative_path = '' ) {
        $safe = array();
        foreach ( (array) $lines as $line ) {
            $safe[] = $this->redact_sensitive_file_tool_line( (string) $line, $relative_path );
        }

        return $safe;
    }

    private function redact_sensitive_file_tool_line( $line, $relative_path = '' ) {
        $line          = (string) $line;
        $relative_path = strtolower( (string) $relative_path );

        $line = preg_replace(
            "/(define\\(\\s*['\\\"](?:DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|NONCE_KEY|AUTH_SALT|SECURE_AUTH_SALT|LOGGED_IN_SALT|NONCE_SALT|WP_CACHE_KEY_SALT|OPENAI_API_KEY|ANTHROPIC_API_KEY)['\\\"]\\s*,\\s*)['\\\"][^'\\\"]*['\\\"](\\s*\\))/i",
            '$1\'[redacted]\'$2',
            $line
        );

        $line = preg_replace(
            "/((?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|client[_-]?secret|consumer[_-]?secret|authorization)\\s*['\\\"]?\\s*(?:=>|=|:)\\s*['\\\"]?)[^'\\\"\\s,;]+/i",
            '$1[redacted]',
            $line
        );

        $line = preg_replace( '/(Bearer\\s+)[A-Za-z0-9._~+\\/=:-]+/i', '$1[redacted]', $line );
        $line = preg_replace( '/sk-[A-Za-z0-9_-]{16,}/', 'sk-[redacted]', $line );

        if ( false !== strpos( $relative_path, '.env' ) ) {
            $line = preg_replace(
                '/^([A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASS|PWD)[A-Z0-9_]*\\s*=\\s*).+$/i',
                '$1[redacted]',
                $line
            );
        }

        return (string) $line;
    }

    /* ================================================================
     * WP OPTION GET — read any WordPress option from the database
     * ============================================================= */

    private function preview_wp_option_get( $args ) {
        $name = isset( $args['option_name'] ) ? sanitize_key( (string) $args['option_name'] ) : '';
        if ( '' === $name ) {
            return array( 'success' => false, 'error' => 'option_name is required.' );
        }
        return array(
            'success' => true,
            'action'  => array(
                'type'             => 'wp_option_get',
                'label'            => 'Get WordPress Option',
                'summary'          => 'Read option: ' . $name,
                'rollback_plan'    => 'Read-only, no rollback needed.',
                'rollback_support' => 'none',
                'targets'          => array(),
                'args'             => array( 'option_name' => $name ),
            ),
        );
    }

    public function execute_wp_option_get( $args ) {
        $name = isset( $args['option_name'] ) ? (string) $args['option_name'] : '';
        if ( '' === $name ) {
            return array( 'success' => false, 'error' => 'option_name is required.' );
        }

        // Security: block reading sensitive options.
        $blocked = array( 'auth_key', 'secure_auth_key', 'logged_in_key', 'nonce_key', 'auth_salt', 'secure_auth_salt', 'logged_in_salt', 'nonce_salt' );
        if ( in_array( strtolower( $name ), $blocked, true ) ) {
            return array( 'success' => false, 'error' => 'Reading this option is blocked for security reasons.' );
        }

        $value = get_option( $name, '__NIWA_NOT_FOUND__' );
        if ( '__NIWA_NOT_FOUND__' === $value ) {
            return array( 'success' => false, 'error' => 'Option "' . $name . '" does not exist.' );
        }

        // Serialize complex values for display.
        $display_value = $value;
        if ( is_array( $value ) || is_object( $value ) ) {
            $display_value = wp_json_encode( $value, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
        }

        return array(
            'success'      => true,
            'summary'      => 'Option "' . $name . '" = ' . mb_substr( is_string( $display_value ) ? $display_value : (string) $display_value, 0, 200 ),
            'option_name'  => $name,
            'option_value' => $value,
            'value_type'   => gettype( $value ),
        );
    }
}
