<?php

class MeowPro_MWAI_MCP_Plugin {
  private $core = null;
  private $log_file = 'mwai.log';
  private $def_name = 'AI Engine Plugin';

  #region Initialize
  public function __construct( $core ) {
    $this->core = $core;
    add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
  }

  public function rest_api_init() {
    add_filter( 'mwai_mcp_tools', [ $this, 'register_rest_tools' ] );
    add_filter( 'mwai_mcp_callback', [ $this, 'handle_call' ], 10, 4 );
  }
  #endregion

  #region Helpers

  private function plugin_dir( string $slug ): string {
    return trailingslashit( WP_PLUGIN_DIR . '/' . sanitize_key( $slug ) );
  }

  /** Validate relative path against traversal & mwai.log; return absolute path or ''. */
  private function safe_path( string $slug, string $rel ): string {
    $rel = ltrim( str_replace( '\\', '/', $rel ), '/' );
    if ( $rel === '' ) {
      return $this->plugin_dir( $slug );
    }
    if ( strpos( $rel, '..' ) !== false || $rel === $this->log_file ) {
      return '';
    }
    return $this->plugin_dir( $slug ) . $rel;
  }

  private function empty_schema(): array {
    return [ 'type' => 'object', 'properties' => (object) [] ];
  }

  private function is_ai_plugin( string $slug ): bool {
    return file_exists( $this->plugin_dir( $slug ) . $this->log_file );
  }

  /**
  * Guard for the plugin file tools.
  *
  * Only plugins AI Engine manages are readable and writable over MCP. The marker is an
  * mwai.log file at the root of the plugin folder, which AI Engine creates for the plugins
  * it generates and appends its edits to. Existing third-party plugins are excluded on
  * purpose: MCP file writes are arbitrary PHP execution, so the boundary is opt-in.
  *
  * This is its own exception because folding the check into the "not found" conditions
  * below made a refusal indistinguishable from a missing directory. Admins reasonably
  * concluded their folder was broken when in fact AI Engine was declining to touch it.
  */
  private function assert_editable( string $slug ): void {
    if ( !$slug ) {
      throw new Exception( 'A plugin slug is required.' );
    }
    if ( !is_dir( $this->plugin_dir( $slug ) ) ) {
      throw new Exception( 'There is no plugin folder named "' . $slug . '".' );
    }
    if ( !$this->is_ai_plugin( $slug ) ) {
      throw new Exception(
        'The plugin "' . $slug . '" is not managed by AI Engine, so its files cannot be listed, '
        . 'read or written over MCP. Only plugins created through AI Engine can be, which is a '
        . 'deliberate safety boundary. To opt this one in, create an empty mwai.log file at the '
        . 'root of its folder (wp-content/plugins/' . $slug . '/mwai.log). AI Engine treats that '
        . 'file as the marker and will append a record of its own edits to it.'
      );
    }
  }

  private function log_action( string $slug, string $msg ): void {
    $path = $this->plugin_dir( $slug ) . $this->log_file;
    if ( file_exists( $path ) ) {
      file_put_contents( $path, date( 'c' ) . ' — ' . $msg . PHP_EOL, FILE_APPEND );
    }
  }

  /** Simple PCRE-pattern validator. */
  private function is_valid_regex( string $pattern ): bool {
    set_error_handler( fn () => false );
    $ok = preg_match( $pattern, '' ) !== false || preg_last_error() !== PREG_INTERNAL_ERROR;
    restore_error_handler();
    return $ok;
  }

  /** Ensure AI plugin exists, creating boilerplate & log if needed. */
  private function ensure_plugin_exists( string $slug, ?string $name = null ): bool {
    $dir = $this->plugin_dir( $slug );
    if ( is_dir( $dir ) ) {
      return $this->is_ai_plugin( $slug );
    }
    if ( !wp_mkdir_p( $dir ) ) {
      return false;
    }

    /* main plugin file */
    $main = "<?php\n";
    $main .= "/*\n";
    $main .= 'Plugin Name: ' . ( $name ?: $this->def_name ) . "\n";
    $main .= "Plugin URI:  https://meowapps.com/\n";
    $main .= "Description: Generated by AI Engine\n";
    $main .= "Version:    1.0\n";
    $main .= "*/\n";
    $main .= "if ( !defined('ABSPATH') ) exit;\n";
    file_put_contents( $dir . $slug . '.php', $main );

    /* mwai.log */
    file_put_contents( $dir . $this->log_file, '' );
    $this->log_action( $slug, 'Plugin created.' );

    return true;
  }

  /** Recursive copy dir → dir. */
  private function copy_dir( string $src, string $dst ): bool {
    $src = rtrim( $src, '/' );
    $dst = rtrim( $dst, '/' );
    if ( !is_dir( $src ) || !wp_mkdir_p( $dst ) ) {
      return false;
    }
    $it = new RecursiveIteratorIterator(
      new RecursiveDirectoryIterator( $src, FilesystemIterator::SKIP_DOTS ),
      RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ( $it as $f ) {
      $dest = $dst . substr( $f->getPathname(), strlen( $src ) );
      $f->isDir() ? wp_mkdir_p( $dest ) : copy( $f->getPathname(), $dest );
    }
    return true;
  }

  /** Recursive delete. */
  private function delete_dir( string $dir ): bool {
    if ( !is_dir( $dir ) ) {
      return true;
    }
    $it = new RecursiveIteratorIterator(
      new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
      RecursiveIteratorIterator::CHILD_FIRST
    );
    foreach ( $it as $f ) {
      $f->isDir() ? rmdir( $f->getPathname() ) : unlink( $f->getPathname() );
    }
    return rmdir( $dir );
  }
  #endregion

  #region Tools
  private function tools(): array {
    return [

      /* ───────── Plugins overview ───────── */
      'wp_list_plugins_detailed' => [
        'name' => 'wp_list_plugins_detailed',
        'description' => 'List installed plugins (slug, name, version, active?, editable?). Only plugins created by AI Engine are editable: the other file tools refuse anything else. An admin can opt an existing plugin in by adding an empty mwai.log file to its folder.',
        'inputSchema' => $this->empty_schema(),
        'accessLevel' => 'admin',
      ],

      'wp_activate_plugin' => [
        'name' => 'wp_activate_plugin',
        'description' => 'Activate any installed plugin by slug.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [ 'slug' => [ 'type' => 'string' ] ],
          'required' => [ 'slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_deactivate_plugin' => [
        'name' => 'wp_deactivate_plugin',
        'description' => 'Deactivate an active plugin by slug.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [ 'slug' => [ 'type' => 'string' ] ],
          'required' => [ 'slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      /* ───────── Plugin CRUD ───────── */
      'wp_create_plugin' => [
        'name' => 'wp_create_plugin',
        'description' => 'Create a new AI Engine plugin (slug required, optional name).',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'name' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_copy_plugin' => [
        'name' => 'wp_copy_plugin',
        'description' => 'Duplicate an existing plugin into a new AI Engine plugin.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'source_slug' => [ 'type' => 'string' ],
            'new_slug' => [ 'type' => 'string' ],
            'new_name' => [ 'type' => 'string' ],
          ],
          'required' => [ 'source_slug', 'new_slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_rename_plugin' => [
        'name' => 'wp_rename_plugin',
        'description' => 'Rename an AI Engine plugin. If the renamed plugin was active, it is re-activated under its new slug.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'old_slug' => [ 'type' => 'string' ],
            'new_slug' => [ 'type' => 'string' ],
          ],
          'required' => [ 'old_slug', 'new_slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_delete_plugin' => [
        'name' => 'wp_delete_plugin',
        'description' => 'Delete an AI Engine plugin; if active, WordPress deactivates it first.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [ 'slug' => [ 'type' => 'string' ] ],
          'required' => [ 'slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      /* ───────── Directory ops ───────── */
      'wp_plugin_mkdir' => [
        'name' => 'wp_plugin_mkdir',
        'description' => 'Create a directory (and parents) inside an AI Engine plugin.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'dir' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug', 'dir' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_plugin_list_dir' => [
        'name' => 'wp_plugin_list_dir',
        'description' => 'List dirs/files inside a directory of an AI Engine plugin (omit dir for root).',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'dir' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_plugin_delete_path' => [
        'name' => 'wp_plugin_delete_path',
        'description' => 'Delete a file or directory (recursively) inside an AI Engine plugin. IMPORTANT: irreversible.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'path' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug', 'path' ],
        ],
        'accessLevel' => 'admin',
      ],

      /* ───────── File ops ───────── */
      'wp_plugin_get_file' => [
        'name' => 'wp_plugin_get_file',
        'description' => 'Get raw contents of a file in an AI Engine plugin.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'file' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug', 'file' ],
        ],
        'accessLevel' => 'admin',
      ],

      'wp_plugin_put_file' => [
        'name' => 'wp_plugin_put_file',
        'description' => 'Create or overwrite any file inside an AI Engine plugin\'s directory. Keep the main plugin file minimal and avoid including PHP files that do not exist yet.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'file' => [ 'type' => 'string' ],
            'content' => [ 'type' => 'string' ],
          ],
          'required' => [ 'slug', 'file', 'content' ],
        ],
        'accessLevel' => 'admin',
      ],

      /* ───────── Alter file ───────── */
      'wp_plugin_alter_file' => [
        'name' => 'wp_plugin_alter_file',
        'description' => 'Search-and-replace inside a single file of an AI Engine plugin. Args: slug, file, search, replace, regex (bool). Regex must be valid PHP-PCRE.',
        'inputSchema' => [
          'type' => 'object',
          'properties' => [
            'slug' => [ 'type' => 'string' ],
            'file' => [ 'type' => 'string' ],
            'search' => [ 'type' => 'string' ],
            'replace' => [ 'type' => 'string' ],
            'regex' => [ 'type' => 'boolean' ],
          ],
          'required' => [ 'slug', 'file', 'search', 'replace' ],
        ],
        'accessLevel' => 'admin',
      ],
    ];
  }
  #endregion

  #region Register
  public function register_rest_tools( array $prev ): array {
    $tools = $this->tools();

    // Add category and annotations to each tool
    foreach ( $tools as &$tool ) {
      if ( !isset( $tool['category'] ) ) {
        $tool['category'] = 'AI Engine (Plugins)';
      }

      // Add MCP tool annotations
      if ( !isset( $tool['annotations'] ) ) {
        $name = $tool['name'];

        // Read-only tools
        $is_readonly = (
          $name === 'wp_list_plugins_detailed' ||
          strpos( $name, '_get_' ) !== false ||
          strpos( $name, '_list_' ) !== false
        );

        // Destructive tools
        $is_destructive = (
          strpos( $name, '_delete_' ) !== false ||
          $name === 'wp_plugin_alter_file'
        );

        $tool['annotations'] = [
          'readOnlyHint' => $is_readonly,
          'destructiveHint' => !$is_readonly && $is_destructive,
          'openWorldHint' => false,
        ];
      }
    }

    $merged = array_merge( $prev, array_values( $tools ) );
    return $merged;
  }
  #endregion

  #region Callback
  public function handle_call( $prev, string $tool, array $args, ?int $id ) {
    if ( !empty( $prev ) || !isset( $this->tools()[ $tool ] ) ) {
      return $prev; // handled elsewhere or unknown
    }
    if ( !current_user_can( 'administrator' ) ) {
      wp_set_current_user( 1 );
    }
    return $this->dispatch( $tool, $args, $id );
  }
  #endregion

  #region Dispatcher
  private function dispatch( string $tool, array $a, ?int $id ) {

    switch ( $tool ) {

      /* ───────── Plugins overview ───────── */
      case 'wp_list_plugins_detailed':
        if ( !function_exists( 'get_plugins' ) ) {
          require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        $active = get_option( 'active_plugins', [] );
        $list = [];
        foreach ( get_plugins() as $path => $p ) {
          $slug = dirname( $path ) !== '.' ? dirname( $path ) : basename( $path, '.php' );
          $list[] = [
            'slug' => $slug,
            'name' => $p['Name'],
            'version' => $p['Version'],
            'active' => in_array( $path, $active, true ),
            'editable' => $this->is_ai_plugin( $slug ),
          ];
        }
        return $list;
        break;

      case 'wp_activate_plugin':
        if ( !function_exists( 'activate_plugin' ) ) {
          require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        $slug = sanitize_key( $a['slug'] ?? '' );
        $path = $slug . '/' . $slug . '.php';
        if ( !$slug || !file_exists( $this->plugin_dir( $slug ) . $slug . '.php' ) ) {
          throw new Exception( 'Unknown plugin slug' );
        }
        $result = activate_plugin( $path );
        if ( is_wp_error( $result ) ) {
          throw new Exception( $result->get_error_message() );
        }
        return 'Plugin activated.';
        break;

      case 'wp_deactivate_plugin':
        if ( !function_exists( 'deactivate_plugins' ) ) {
          require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        $slug = sanitize_key( $a['slug'] ?? '' );
        $path = $slug . '/' . $slug . '.php';
        if ( !$slug || !file_exists( $this->plugin_dir( $slug ) . $slug . '.php' ) ) {
          throw new Exception( 'Unknown plugin slug' );
        }
        deactivate_plugins( [ $path ] );
        return 'Plugin deactivated.';
        break;

        /* ───────── Create / copy / rename / delete plugin ───────── */
      case 'wp_create_plugin':
        $slug = sanitize_key( $a['slug'] ?? '' );
        if ( !$slug || is_dir( $this->plugin_dir( $slug ) ) ) {
          throw new Exception( 'Invalid or existing slug' );
        }
        if ( !$this->ensure_plugin_exists( $slug, $a['name'] ?? null ) ) {
          throw new Exception( 'Creation failed' );
        }
        return 'Plugin "' . $slug . '" created.';
        break;

      case 'wp_copy_plugin':
        $src = sanitize_key( $a['source_slug'] ?? '' );
        $dst = sanitize_key( $a['new_slug'] ?? '' );
        if ( !$src || !$dst ) {
          throw new Exception( 'source_slug & new_slug required' );
        }
        if ( !is_dir( $this->plugin_dir( $src ) ) || is_dir( $this->plugin_dir( $dst ) ) ) {
          throw new Exception( 'Invalid source or destination slug' );
        }
        if ( !$this->copy_dir( $this->plugin_dir( $src ), $this->plugin_dir( $dst ) ) ) {
          throw new Exception( 'Copy failed' );
        }
        file_put_contents( $this->plugin_dir( $dst ) . $this->log_file, '' );
        $this->log_action( $dst, 'Plugin forked from ' . $src . '.' );
        if ( !empty( $a['new_name'] ) && file_exists( $this->plugin_dir( $dst ) . $dst . '.php' ) ) {
          $content = file_get_contents( $this->plugin_dir( $dst ) . $dst . '.php' );
          $content = preg_replace( '/Plugin Name:\s*.+/i', 'Plugin Name: ' . $a['new_name'], $content, 1 );
          file_put_contents( $this->plugin_dir( $dst ) . $dst . '.php', $content );
        }
        return 'Plugin "' . $src . '" copied to "' . $dst . '".';
        break;

      case 'wp_rename_plugin':
        $old = sanitize_key( $a['old_slug'] ?? '' );
        $new = sanitize_key( $a['new_slug'] ?? '' );
        if ( !$old || !$new || !$this->is_ai_plugin( $old ) || is_dir( $this->plugin_dir( $new ) ) ) {
          throw new Exception( 'Invalid slugs or target exists' );
        }
        if ( !rename( $this->plugin_dir( $old ), $this->plugin_dir( $new ) ) ) {
          throw new Exception( 'Rename failed' );
        }
        if ( file_exists( $this->plugin_dir( $new ) . $old . '.php' ) ) {
          rename( $this->plugin_dir( $new ) . $old . '.php', $this->plugin_dir( $new ) . $new . '.php' );
        }
        $this->log_action( $new, 'Plugin renamed from ' . $old . '.' );

        $was_active = is_plugin_active( $old . '/' . $old . '.php' );
        if ( $was_active ) {
          deactivate_plugins( [ $old . '/' . $old . '.php' ] );
          if ( !function_exists( 'activate_plugin' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
          }
          activate_plugin( $new . '/' . $new . '.php' );
        }

        return 'Plugin renamed to ' . $new . ( $was_active ? ' and activated.' : '.' );
        break;

      case 'wp_delete_plugin':
        if ( !function_exists( 'delete_plugins' ) ) {
          require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        $slug = sanitize_key( $a['slug'] ?? '' );
        $path = $slug . '/' . $slug . '.php';
        if ( !$slug || !$this->is_ai_plugin( $slug ) ) {
          throw new Exception( 'Unknown AI Engine plugin' );
        }
        if ( is_plugin_active( $path ) ) {
          deactivate_plugins( [ $path ] );
        }
        $ok = delete_plugins( [ $path ] );
        if ( is_wp_error( $ok ) ) {
          throw new Exception( $ok->get_error_message() );
        }
        return 'Plugin "' . $slug . '" deleted.';
        break;

        /* ───────── Directory operations ───────── */
      case 'wp_plugin_mkdir':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $dir = $a['dir'] ?? '';
        $dest = $this->safe_path( $slug, $dir );
        if ( !$dir || !$dest ) {
          throw new Exception( 'Invalid dir' );
        }
        if ( !wp_mkdir_p( $dest ) ) {
          throw new Exception( 'mkdir failed' );
        }
        $this->log_action( $slug, 'Created dir ' . $dir . '.' );
        return 'Directory created.';
        break;

      case 'wp_plugin_list_dir':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $dir = $a['dir'] ?? '';
        $path = $this->safe_path( $slug, $dir );
        if ( !$path || !is_dir( $path ) ) {
          throw new Exception( 'Dir not found' );
        }
        $dirs = [];
        $files = [];
        foreach ( scandir( $path ) as $item ) {
          if ( $item === '.' || $item === '..' || $item === $this->log_file ) {
            continue;
          }
          $rel = ltrim( ( $dir ? $dir . '/' : '' ) . $item, '/' );
          is_dir( $path . '/' . $item ) ? $dirs[] = $rel : $files[] = $rel;
        }
        return [ 'dirs' => $dirs, 'files' => $files ];
        break;

      case 'wp_plugin_delete_path':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $rel = $a['path'] ?? '';
        $abs = $this->safe_path( $slug, $rel );
        if ( !$rel || !$abs || !file_exists( $abs ) ) {
          throw new Exception( 'Path not found' );
        }
        $ok = is_dir( $abs ) ? $this->delete_dir( $abs ) : unlink( $abs );
        if ( !$ok ) {
          throw new Exception( 'Delete failed' );
        }
        $this->log_action( $slug, 'Deleted ' . $rel . '.' );
        return $rel . ' deleted.';
        break;

        /* ───────── File operations ───────── */
      case 'wp_plugin_get_file':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $file = $a['file'] ?? '';
        $abs = $this->safe_path( $slug, $file );
        if ( !$file || !$abs || !is_file( $abs ) ) {
          throw new Exception( 'File not found' );
        }
        return file_get_contents( $abs );
        break;

      case 'wp_plugin_put_file':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $file = $a['file'] ?? '';
        $cnt = $a['content'] ?? null;
        $abs = $this->safe_path( $slug, $file );
        if ( !$file || !isset( $cnt ) || !$abs ) {
          throw new Exception( 'Invalid input' );
        }
        wp_mkdir_p( dirname( $abs ) );
        if ( file_put_contents( $abs, $cnt ) === false ) {
          throw new Exception( 'Write failed' );
        }
        $this->log_action( $slug, 'Saved ' . $file . '.' );
        return $file . ' saved.';
        break;

        /* ───────── Alter file ───────── */
      case 'wp_plugin_alter_file':
        $slug = sanitize_key( $a['slug'] ?? '' );
        $this->assert_editable( $slug );
        $file = $a['file'] ?? '';
        $search = $a['search'] ?? null;
        $replace = $a['replace'] ?? '';
        $regex = (bool) ( $a['regex'] ?? false );
        $abs = $this->safe_path( $slug, $file );

        if ( !$file || !isset( $search ) || !$abs || !is_file( $abs ) ) {
          throw new Exception( 'Invalid input or file not found' );
        }
        if ( $regex && !$this->is_valid_regex( $search ) ) {
          throw new Exception( 'Invalid regex pattern' );
        }

        $contents = file_get_contents( $abs );
        $count = 0;
        if ( $regex ) {
          $new = preg_replace( $search, $replace, $contents, -1, $count );
          if ( $new === null ) {
            throw new Exception( 'Regex error' );
          }
        }
        else {
          $new = str_replace( $search, $replace, $contents, $count );
        }

        if ( $count === 0 ) {
          return 'No occurrences found; file unchanged.';
        }
        if ( file_put_contents( $abs, $new ) === false ) {
          throw new Exception( 'Write failed' );
        }
        $this->log_action( $slug, "Altered $file ($count replacement" . ( $count === 1 ? '' : 's' ) . ').' );
        return "$count replacement" . ( $count === 1 ? '' : 's' ) . ' applied.';
        break;

      default:
        throw new Exception( 'Unknown tool' );
    }
  }
  #endregion
}
