<?php
/**
 * Zentrale Companion-Steuerung: Master-Schalter, Domain Lock und Ability-Profile.
 *
 * @package WPAgentCompanion
 */

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

define( 'WPAGENT_COMPANION_OPT_ENABLED', 'wp_agent_companion_enabled' );
define( 'WPAGENT_COMPANION_OPT_BOUND_SITE', 'wp_agent_companion_bound_site' );
define( 'WPAGENT_COMPANION_OPT_PROFILE', 'wp_agent_companion_profile' );
define( 'WPAGENT_COMPANION_OPT_DISABLED_ABILITIES', 'wp_agent_companion_disabled_abilities' );
define( 'WPAGENT_COMPANION_OPT_TRUSTED_EXTERNAL_ABILITIES', 'wp_agent_companion_trusted_external_abilities' );

/**
 * Retrieves an ability only after the registry confirms that it exists.
 *
 * WordPress deliberately emits a doing-it-wrong notice when wp_get_ability()
 * receives an unknown name. Optional provider integrations therefore must use
 * this helper for feature detection instead of probing the getter directly.
 *
 * @return WP_Ability|null
 */
function wpagent_companion_get_registered_ability( string $name ) {
	if (
		! did_action( 'init' )
		|| ! function_exists( 'wp_has_ability' )
		|| ! function_exists( 'wp_get_ability' )
		|| ! wp_has_ability( $name )
	) {
		return null;
	}

	return wp_get_ability( $name );
}

/**
 * Kanonischer Katalog aller ausgelieferten Companion-Abilities.
 *
 * Die Gruppen steuern ausschließlich die Exposition. Die serverseitigen
 * WordPress-Capability-Prüfungen jeder Ability bleiben davon unabhängig aktiv.
 *
 * `chat` ist eine ausdrückliche Freigabemarkierung für den integrierten Chat.
 * Neue Abilities bleiben dort gesperrt, bis Schemas, Berechtigungen und die
 * Semantik der Einmalfreigabe geprüft sind. Destruktive Annotationen und
 * fremde Provider bleiben unabhängig davon ausgeschlossen.
 *
 * @return array<string,array{group:string,readonly:bool,chat:bool}>
 */
function wpagent_companion_ability_catalog(): array {
	$groups = array(
		'content' => array(
			'create-post-from-markdown', 'get-post', 'list-posts', 'update-post',
			'set-post-status', 'trash-post', 'restore-post', 'delete-post',
			'create-reusable-block', 'update-reusable-block', 'list-reusable-blocks',
			'delete-reusable-block', 'search-replace-content', 'undo-content-replace',
		),
		'taxonomy' => array( 'create-term', 'update-term', 'delete-term', 'get-term', 'list-terms', 'set-post-terms' ),
		'media' => array( 'upload-media', 'create-direct-media-upload', 'revoke-direct-media-upload', 'set-alt-text', 'set-featured-image', 'delete-media', 'list-media' ),
		'menus' => array( 'create-menu', 'add-menu-item', 'list-menus', 'assign-menu-location', 'delete-menu' ),
		'comments' => array( 'list-comments', 'moderate-comment', 'reply-to-comment', 'delete-comment' ),
		'administration' => array(
			'get-setting', 'update-setting', 'list-settings', 'list-users', 'get-user',
			'set-user-role', 'create-user', 'delete-user', 'list-plugins', 'activate-plugin',
			'deactivate-plugin', 'list-themes', 'switch-theme',
		),
		'seo' => array(
			'get-seo-meta', 'set-seo-meta', 'get-seo-settings', 'set-seo-settings', 'get-post-schema', 'set-post-schema', 'delete-post-schema', 'get-term-seo-meta', 'set-term-seo-meta',
			'list-seo-redirections', 'get-seo-redirection', 'create-seo-redirection',
			'update-seo-redirection', 'delete-seo-redirection', 'get-seopress-post-redirection',
			'set-seopress-post-redirection',
		),
		'design' => array(
			'upsert-pattern', 'write-theme-file', 'get-global-styles', 'set-global-styles',
			'list-global-style-variations', 'get-global-style-variation', 'apply-global-style-variation',
			'update-elementor-design-system',
			'get-astra-design', 'update-astra-design', 'get-astra-post-design', 'update-astra-post-design',
			'get-generatepress-design', 'update-generatepress-design', 'get-generatepress-post-design', 'update-generatepress-post-design',
			'get-oceanwp-design', 'update-oceanwp-design', 'get-oceanwp-post-design', 'update-oceanwp-post-design',
			'get-oceanwp-footer-customizer', 'update-oceanwp-footer-customizer', 'get-oceanwp-blog-customizer', 'update-oceanwp-blog-customizer', 'get-oceanwp-woocommerce-customizer', 'update-oceanwp-woocommerce-customizer',
			'get-oceanwp-post-free-overrides', 'list-oceanwp-free-hooks',
			'get-oceanwp-breadcrumbs-customizer', 'update-oceanwp-breadcrumbs-customizer',
			'get-oceanwp-post-format-overrides', 'update-oceanwp-post-format-overrides',
			'get-oceanwp-post-title', 'update-oceanwp-post-title',
			'get-oceanwp-post-layout-overrides', 'update-oceanwp-post-layout-overrides',
			'get-oceanwp-extra-modules', 'update-oceanwp-extra-module',
			'list-oceanwp-library-templates', 'get-oceanwp-post-library-templates', 'update-oceanwp-post-library-templates',
			'get-kadence-design', 'update-kadence-design',
			'list-site-templates', 'get-site-template', 'create-site-template', 'update-site-template', 'delete-site-template',
			'list-template-parts', 'get-template-part', 'create-template-part', 'update-template-part', 'delete-template-part',
			'list-block-navigations', 'get-block-navigation', 'create-block-navigation', 'update-block-navigation', 'delete-block-navigation',
			'export-spectra-one-design-snapshot', 'import-spectra-one-design-snapshot',
			'configure-spectra-one-transfer-secret', 'export-spectra-one-portable-snapshot', 'preflight-spectra-one-portable-snapshot', 'import-spectra-one-portable-snapshot',
		),
		'builder' => array(
			'detect-builder', 'refresh-builder-cache', 'get-builder-compatibility',
			'list-block-types', 'get-block-type', 'get-native-block-document',
			'update-spectra-block-attributes', 'get-spectra-blocks-provider', 'list-spectra-blocks-catalog', 'get-spectra-blocks-settings', 'get-spectra-blocks-global-styles', 'get-spectra-blocks-popup', 'get-spectra-blocks-separator', 'update-spectra-blocks-separator', 'update-spectra-blocks-button-text', 'update-spectra-static-heading-alignment', 'update-spectra-static-heading-colors', 'update-generateblocks-block-attributes', 'update-kadence-block-attributes', 'update-kadence-single-button',
				'get-elementor-document', 'preview-elementor-v3-v4-migration', 'get-elementor-design-system', 'get-elementor-global-classes',
				'create-elementor-global-class', 'update-elementor-global-class', 'delete-elementor-global-class',
				'set-elementor-element-global-classes', 'list-elementor-widgets', 'get-elementor-widget-schema',
				'get-elementor-element', 'create-elementor-page', 'update-elementor-document',
				'update-elementor-element', 'create-elementor-element', 'duplicate-elementor-element',
				'move-elementor-element', 'delete-elementor-element', 'restore-elementor-revision',
			'get-beaver-document', 'list-beaver-modules', 'get-beaver-module-schema',
			'create-beaver-page', 'update-beaver-node', 'create-beaver-column-group', 'create-beaver-module',
			'duplicate-beaver-node', 'move-beaver-node', 'delete-beaver-node', 'restore-beaver-revision',
			'list-beaver-user-templates', 'get-beaver-user-template', 'save-beaver-user-template',
			'save-beaver-node-template', 'delete-beaver-user-template', 'get-beaver-global-settings', 'save-beaver-global-settings',
			'list-spectra-blocks', 'get-spectra-editor-settings', 'update-spectra-editor-settings', 'set-spectra-block-status', 'list-spectra-popups',
			'get-spectra-popup', 'create-spectra-popup', 'update-spectra-popup', 'delete-spectra-popup',
		),
		'verification' => array( 'render-check', 'render-verify', 'refresh-hooks' ),
		'safety' => array( 'disable-power', 'create-browser-link', 'revoke-browser-link' ),
		'integrations' => array(
			'snapshot-content-model', 'compare-content-model-snapshots', 'migrate-content-model',
			'get-acf-fields', 'update-acf-field',
			'list-acf-field-groups', 'get-acf-field-group', 'create-acf-field-group',
			'update-acf-field-group', 'duplicate-acf-field-group', 'delete-acf-field-group',
			'list-acf-post-types', 'get-acf-post-type', 'create-acf-post-type',
			'update-acf-post-type', 'delete-acf-post-type',
			'list-acf-taxonomies', 'get-acf-taxonomy', 'create-acf-taxonomy',
			'update-acf-taxonomy', 'delete-acf-taxonomy',
			'list-pods-models', 'get-pods-model', 'create-pods-model', 'update-pods-model',
			'delete-pods-model', 'upsert-pods-field', 'delete-pods-field',
			'list-pods-settings', 'get-pods-settings', 'list-pods-field-groups', 'get-pods-field-group',
			'list-pods-object-extensions', 'get-pods-object-extension',
			'list-pods-act-records', 'get-pods-act-record', 'create-pods-act-record',
			'update-pods-act-record', 'delete-pods-act-record',
			'get-pods-field-values', 'update-pods-field-values',
			'list-cptui-post-types', 'get-cptui-post-type', 'create-cptui-post-type',
			'update-cptui-post-type', 'delete-cptui-post-type',
			'list-cptui-taxonomies', 'get-cptui-taxonomy', 'create-cptui-taxonomy',
			'update-cptui-taxonomy', 'delete-cptui-taxonomy',
			'list-acpt-post-types', 'get-acpt-post-type', 'create-acpt-post-type',
			'update-acpt-post-type', 'delete-acpt-post-type',
			'list-acpt-taxonomies', 'get-acpt-taxonomy', 'create-acpt-taxonomy',
			'update-acpt-taxonomy', 'delete-acpt-taxonomy',
			'list-acpt-meta-groups', 'get-acpt-meta-group', 'create-acpt-meta-group',
			'update-acpt-meta-group', 'delete-acpt-meta-group',
			'get-acpt-field-values', 'update-acpt-field-values',
			'list-acpt-woocommerce-product-data', 'create-acpt-woocommerce-product-data', 'update-acpt-woocommerce-product-data', 'delete-acpt-woocommerce-product-data',
			'get-meta-box-status',
			'list-meta-box-field-groups', 'get-meta-box-field-group', 'create-meta-box-field-group',
			'update-meta-box-field-group', 'delete-meta-box-field-group', 'list-meta-box-fields',
			'get-meta-box-field', 'create-meta-box-field', 'update-meta-box-field',
			'delete-meta-box-field', 'move-meta-box-field',
			'list-meta-box-post-types', 'get-meta-box-post-type', 'create-meta-box-post-type',
			'update-meta-box-post-type', 'delete-meta-box-post-type',
			'list-meta-box-taxonomies', 'get-meta-box-taxonomy', 'create-meta-box-taxonomy',
			'update-meta-box-taxonomy', 'delete-meta-box-taxonomy',
			'get-meta-box-field-value-format', 'get-meta-box-field-value',
			'update-meta-box-field-value', 'delete-meta-box-field-value',
			'list-meta-box-relationships', 'get-meta-box-relationship', 'create-meta-box-relationship',
			'update-meta-box-relationship', 'delete-meta-box-relationship', 'list-meta-box-connections',
			'add-meta-box-connection', 'delete-meta-box-connection',
			'list-meta-box-settings-pages', 'get-meta-box-settings-page', 'create-meta-box-settings-page',
			'update-meta-box-settings-page', 'delete-meta-box-settings-page',
			'get-product', 'update-product',
			'get-woocommerce-store-settings', 'list-products', 'preview-woocommerce-bulk-price-update', 'execute-woocommerce-bulk-price-update',
			'create-product', 'delete-product', 'list-product-variations',
			'upsert-product-variation', 'delete-product-variation',
			'list-product-attributes', 'get-product-attribute', 'upsert-product-attribute',
			'delete-product-attribute', 'list-product-shipping-classes', 'get-product-shipping-class',
			'upsert-product-shipping-class',
			'list-contact-forms', 'get-contact-form', 'create-contact-form', 'update-contact-form',
			'duplicate-contact-form', 'delete-contact-form',
			'list-fluent-forms', 'get-fluent-form', 'create-fluent-form', 'update-fluent-form',
			'duplicate-fluent-form', 'delete-fluent-form',
			'get-fluent-form-fields', 'upsert-fluent-form-field', 'delete-fluent-form-field',
			'get-fluent-form-delivery', 'update-fluent-form-confirmation',
			'upsert-fluent-form-notification', 'delete-fluent-form-notification',
			'get-wpforms-delivery',
			'create-wpforms-confirmation', 'update-wpforms-confirmation', 'delete-wpforms-confirmation',
			'create-wpforms-notification', 'update-wpforms-notification', 'delete-wpforms-notification',
			'list-kadence-forms', 'get-kadence-form-settings', 'update-kadence-form-settings',
			'list-gravity-forms', 'get-gravity-form', 'create-gravity-form', 'update-gravity-form',
			'duplicate-gravity-form', 'delete-gravity-form',
			'list-formidable-forms', 'get-formidable-form', 'create-formidable-form', 'update-formidable-form',
			'duplicate-formidable-form', 'delete-formidable-form',
			'preview-form-migration', 'migrate-form',
			'get-weglot-settings', 'update-weglot-settings',
			'get-ase-free-generator-tag', 'update-ase-free-generator-tag',
		),
		'entries' => array(
			'list-fluent-entries', 'get-fluent-entry', 'set-fluent-entry-status',
			'set-fluent-entry-favorite', 'delete-fluent-entry',
			'list-gravity-entries', 'get-gravity-entry', 'update-gravity-entry',
			'set-gravity-entry-status', 'delete-gravity-entry',
		),
		'orders' => array(
			'list-orders', 'get-order', 'list-order-notes', 'set-order-status', 'add-order-note',
		),
		'agent-context' => array(
			'get-site-context', 'update-site-context', 'get-system-diagnostics', 'list-skills', 'get-skill', 'upsert-skill',
			'delete-skill', 'list-memory', 'save-memory', 'delete-memory', 'get-design-profile',
			'set-design-profile', 'list-design-directions', 'get-design-direction', 'upsert-design-direction',
			'activate-design-direction', 'delete-design-direction',
		),
		'live-editor' => array(
			'get-live-editor-status', 'live-editor-read', 'live-editor-write', 'live-editor-elementor-write', 'get-live-editor-result',
		),
	);

	$catalog = array();
	$mutating = array_flip( array(
		'create-post-from-markdown', 'update-post', 'set-post-status', 'trash-post', 'restore-post', 'delete-post', 'search-replace-content', 'undo-content-replace',
		'create-reusable-block', 'update-reusable-block', 'delete-reusable-block',
		'create-term', 'update-term', 'delete-term', 'set-post-terms',
		'upload-media', 'create-direct-media-upload', 'revoke-direct-media-upload', 'set-alt-text', 'set-featured-image', 'delete-media',
		'create-menu', 'add-menu-item', 'assign-menu-location', 'delete-menu',
		'moderate-comment', 'reply-to-comment', 'delete-comment',
		'update-setting', 'set-user-role', 'create-user', 'delete-user', 'activate-plugin', 'deactivate-plugin', 'switch-theme',
		'set-seo-meta', 'set-seo-settings', 'set-post-schema', 'delete-post-schema', 'set-term-seo-meta', 'create-seo-redirection', 'update-seo-redirection', 'delete-seo-redirection', 'set-seopress-post-redirection',
		'upsert-pattern', 'write-theme-file', 'set-global-styles', 'apply-global-style-variation', 'refresh-builder-cache', 'refresh-hooks',
		'update-elementor-design-system',
		'create-site-template', 'update-site-template', 'delete-site-template',
		'create-template-part', 'update-template-part', 'delete-template-part',
		'create-block-navigation', 'update-block-navigation', 'delete-block-navigation',
		'import-spectra-one-design-snapshot',
		'configure-spectra-one-transfer-secret', 'import-spectra-one-portable-snapshot',
			'create-elementor-page', 'update-elementor-document', 'update-elementor-element',
			'create-elementor-element', 'duplicate-elementor-element', 'move-elementor-element', 'delete-elementor-element',
			'restore-elementor-revision', 'create-elementor-global-class', 'update-elementor-global-class',
			'delete-elementor-global-class', 'set-elementor-element-global-classes',
		'create-beaver-page', 'update-beaver-node', 'create-beaver-column-group', 'create-beaver-module',
		'duplicate-beaver-node', 'move-beaver-node', 'delete-beaver-node', 'restore-beaver-revision',
		'save-beaver-user-template', 'save-beaver-node-template', 'delete-beaver-user-template', 'save-beaver-global-settings',
		'update-spectra-editor-settings', 'set-spectra-block-status', 'create-spectra-popup', 'update-spectra-popup', 'delete-spectra-popup',
		'update-spectra-block-attributes', 'update-spectra-blocks-separator', 'update-spectra-blocks-button-text', 'update-spectra-static-heading-alignment', 'update-spectra-static-heading-colors', 'update-generateblocks-block-attributes', 'update-kadence-block-attributes', 'update-kadence-single-button',
		'disable-power', 'create-browser-link', 'revoke-browser-link', 'update-acf-field',
		'create-acf-field-group', 'update-acf-field-group', 'duplicate-acf-field-group', 'delete-acf-field-group',
		'migrate-content-model',
		'create-acf-post-type', 'update-acf-post-type', 'delete-acf-post-type',
		'create-acf-taxonomy', 'update-acf-taxonomy', 'delete-acf-taxonomy',
		'create-pods-model', 'update-pods-model', 'delete-pods-model', 'upsert-pods-field', 'delete-pods-field',
		'create-pods-act-record', 'update-pods-act-record', 'delete-pods-act-record',
		'update-pods-field-values',
		'create-cptui-post-type', 'update-cptui-post-type', 'delete-cptui-post-type',
		'create-cptui-taxonomy', 'update-cptui-taxonomy', 'delete-cptui-taxonomy',
		'create-acpt-post-type', 'update-acpt-post-type', 'delete-acpt-post-type',
		'create-acpt-taxonomy', 'update-acpt-taxonomy', 'delete-acpt-taxonomy',
		'create-acpt-meta-group', 'update-acpt-meta-group', 'delete-acpt-meta-group', 'update-acpt-field-values',
		'create-acpt-woocommerce-product-data', 'update-acpt-woocommerce-product-data', 'delete-acpt-woocommerce-product-data',
		'create-meta-box-field-group', 'update-meta-box-field-group', 'delete-meta-box-field-group',
		'create-meta-box-field', 'update-meta-box-field', 'delete-meta-box-field', 'move-meta-box-field',
		'create-meta-box-post-type', 'update-meta-box-post-type', 'delete-meta-box-post-type',
		'create-meta-box-taxonomy', 'update-meta-box-taxonomy', 'delete-meta-box-taxonomy',
		'update-meta-box-field-value', 'delete-meta-box-field-value',
		'create-meta-box-relationship', 'update-meta-box-relationship', 'delete-meta-box-relationship',
		'add-meta-box-connection', 'delete-meta-box-connection',
		'create-meta-box-settings-page', 'update-meta-box-settings-page', 'delete-meta-box-settings-page',
		'update-product', 'execute-woocommerce-bulk-price-update', 'create-product', 'delete-product', 'upsert-product-variation', 'delete-product-variation',
		'upsert-product-attribute', 'delete-product-attribute',
		'upsert-product-shipping-class',
		'create-contact-form', 'update-contact-form', 'duplicate-contact-form', 'delete-contact-form',
		'create-fluent-form', 'update-fluent-form', 'duplicate-fluent-form', 'delete-fluent-form',
		'upsert-fluent-form-field', 'delete-fluent-form-field',
		'update-fluent-form-confirmation', 'upsert-fluent-form-notification', 'delete-fluent-form-notification',
		'create-wpforms-confirmation', 'update-wpforms-confirmation', 'delete-wpforms-confirmation',
		'create-wpforms-notification', 'update-wpforms-notification', 'delete-wpforms-notification',
		'update-kadence-form-settings',
		'create-gravity-form', 'update-gravity-form', 'duplicate-gravity-form', 'delete-gravity-form',
		'create-formidable-form', 'update-formidable-form', 'duplicate-formidable-form', 'delete-formidable-form',
		'migrate-form',
		'update-weglot-settings',
		'update-ase-free-generator-tag',
		'update-astra-design', 'update-astra-post-design',
		'update-generatepress-design', 'update-generatepress-post-design',
		'update-oceanwp-design', 'update-oceanwp-post-design', 'update-oceanwp-post-title', 'update-oceanwp-post-layout-overrides', 'update-oceanwp-footer-customizer', 'update-oceanwp-blog-customizer', 'update-oceanwp-woocommerce-customizer', 'update-oceanwp-breadcrumbs-customizer', 'update-oceanwp-post-format-overrides',
		'update-oceanwp-extra-module', 'update-oceanwp-post-library-templates',
		'update-kadence-design',
		'update-gravity-entry', 'set-gravity-entry-status', 'delete-gravity-entry',
		'set-fluent-entry-status', 'set-fluent-entry-favorite', 'delete-fluent-entry',
		'set-order-status', 'add-order-note',
		'update-site-context', 'upsert-skill', 'delete-skill',
		'save-memory', 'delete-memory', 'set-design-profile',
		'upsert-design-direction', 'activate-design-direction', 'delete-design-direction',
		'live-editor-write', 'live-editor-elementor-write',
	) );
	$chat_allowed = array_flip( array(
		// Begrenzte Lesezugriffe für den Recherchekontext des Modells.
		'get-post', 'list-posts', 'list-media', 'get-term', 'list-terms',
		'list-menus', 'get-seo-meta', 'get-post-schema', 'list-reusable-blocks',
		'get-setting', 'list-settings', 'get-product', 'list-products',
		'list-product-variations', 'list-product-attributes', 'get-product-attribute',
		'get-acf-fields',
		'list-contact-forms', 'get-contact-form', 'detect-builder',
		'get-builder-compatibility', 'get-beaver-document', 'list-beaver-modules', 'list-beaver-user-templates',
		'get-beaver-user-template', 'get-beaver-global-settings',
		'get-beaver-module-schema', 'render-check', 'get-site-context',
		'get-design-profile', 'list-design-directions', 'get-design-direction',
		'list-skills', 'get-skill', 'list-memory', 'list-plugins', 'list-themes',
		// Reversible oder begrenzte Schreibzugriffe mit einzelner menschlicher Freigabe.
		'create-post-from-markdown', 'update-post', 'set-post-status',
		'set-post-terms', 'set-featured-image', 'set-seo-meta', 'set-post-schema',
		'set-alt-text', 'upload-media', 'create-term', 'update-term',
		'create-menu', 'add-menu-item', 'assign-menu-location',
		'create-reusable-block', 'update-reusable-block',
		'create-beaver-page', 'update-beaver-node', 'create-beaver-column-group',
		'create-beaver-module', 'duplicate-beaver-node', 'move-beaver-node',
		'upsert-pattern', 'set-global-styles', 'apply-global-style-variation',
		'update-acf-field', 'update-product', 'create-product',
		'upsert-product-variation', 'create-contact-form', 'update-contact-form',
		'duplicate-contact-form', 'reply-to-comment', 'moderate-comment',
	) );
	foreach ( $groups as $group => $slugs ) {
		foreach ( $slugs as $slug ) {
			$catalog[ 'wp-agent/' . $slug ] = array(
				'group'    => $group,
				'readonly' => ! isset( $mutating[ $slug ] ),
				'chat'     => isset( $chat_allowed[ $slug ] ),
			);
		}
	}
	return $catalog;
}

/** @return string[] */
function wpagent_companion_profiles(): array {
	return array( 'readonly', 'content', 'design', 'full', 'custom' );
}

function wpagent_companion_profile_label( string $profile ): string {
	$labels = array(
		'readonly' => __( 'Nur lesen', 'wp-agent-companion' ),
		'content'  => __( 'Inhalte', 'wp-agent-companion' ),
		'design'   => __( 'Inhalte und Design', 'wp-agent-companion' ),
		'full'     => __( 'Vollzugriff', 'wp-agent-companion' ),
		'custom'   => __( 'Benutzerdefiniert', 'wp-agent-companion' ),
	);
	return $labels[ $profile ] ?? $profile;
}

function wpagent_companion_profile_description( string $profile ): string {
	$descriptions = array(
		'readonly' => __( 'Nur lesende Abilities aus allen Bereichen. Änderungen sind vollständig gesperrt.', 'wp-agent-companion' ),
		'content'  => __( 'Inhalte, Taxonomien, Medien, Kommentare, SEO, Builder-Prüfung, Integrationen und Agent-Wissen. Design und Administration bleiben gesperrt.', 'wp-agent-companion' ),
		'design'   => __( 'Alle Inhaltsfunktionen sowie Menüs, Builder-Erkennung und begrenzte Designänderungen.', 'wp-agent-companion' ),
		'full'     => __( 'Alle Companion-Abilities werden angeboten. Die WordPress-Rollenprüfung jeder Ability bleibt zusätzlich aktiv.', 'wp-agent-companion' ),
		'custom'   => __( 'Beginnt mit allen Companion-Abilities. Deaktiviere unten gezielt alles, was der Agent nicht sehen soll.', 'wp-agent-companion' ),
	);
	return $descriptions[ $profile ] ?? '';
}

function wpagent_companion_group_label( string $group ): string {
	$labels = array(
		'content'       => __( 'Inhalte', 'wp-agent-companion' ),
		'taxonomy'      => __( 'Taxonomien', 'wp-agent-companion' ),
		'media'         => __( 'Medien', 'wp-agent-companion' ),
		'menus'         => __( 'Menüs', 'wp-agent-companion' ),
		'comments'      => __( 'Kommentare', 'wp-agent-companion' ),
		'administration' => __( 'Administration', 'wp-agent-companion' ),
		'seo'           => __( 'SEO', 'wp-agent-companion' ),
		'design'        => __( 'Design', 'wp-agent-companion' ),
		'builder'       => __( 'Builder', 'wp-agent-companion' ),
		'verification'  => __( 'Verifikation', 'wp-agent-companion' ),
		'safety'        => __( 'Sicherheit', 'wp-agent-companion' ),
		'integrations'  => __( 'Integrationen', 'wp-agent-companion' ),
		'entries'       => __( 'Eintragsdaten', 'wp-agent-companion' ),
		'orders'        => __( 'Bestelldaten', 'wp-agent-companion' ),
		'agent-context' => __( 'Agent-Wissen', 'wp-agent-companion' ),
		'live-editor'   => __( 'Live-Editor', 'wp-agent-companion' ),
	);
	return $labels[ $group ] ?? $group;
}

function wpagent_companion_diagnostic_label( string $name ): string {
	$labels = array(
		'license'       => __( 'Lizenz', 'wp-agent-companion' ),
		'master-switch' => __( 'Hauptschalter', 'wp-agent-companion' ),
		'domain-lock'   => __( 'Domain Lock', 'wp-agent-companion' ),
		'rest-api'      => __( 'REST-API', 'wp-agent-companion' ),
		'permalinks'    => __( 'Permalinks', 'wp-agent-companion' ),
		'https'         => __( 'HTTPS', 'wp-agent-companion' ),
		'profile'       => __( 'Profil', 'wp-agent-companion' ),
	);
	return $labels[ $name ] ?? $name;
}

function wpagent_companion_profile(): string {
	$profile = (string) get_option( WPAGENT_COMPANION_OPT_PROFILE, 'content' );
	return in_array( $profile, wpagent_companion_profiles(), true ) ? $profile : 'content';
}

/** @return string[] */
function wpagent_companion_profile_groups( string $profile ): array {
	$content = array( 'content', 'taxonomy', 'media', 'comments', 'seo', 'builder', 'verification', 'safety', 'integrations', 'agent-context', 'live-editor' );
	if ( 'content' === $profile ) {
		return $content;
	}
	if ( 'design' === $profile ) {
		return array_merge( $content, array( 'design', 'menus' ) );
	}
	return array_values( array_unique( array_column( wpagent_companion_ability_catalog(), 'group' ) ) );
}

/** @return string[] */
function wpagent_companion_disabled_abilities(): array {
	$disabled = get_option( WPAGENT_COMPANION_OPT_DISABLED_ABILITIES, array() );
	if ( ! is_array( $disabled ) ) {
		return array();
	}
	$catalog = wpagent_companion_ability_catalog();
	$disabled = array_filter( $disabled, 'is_string' );
	return array_values(
		array_filter(
			array_unique( $disabled ),
			static fn( string $name ): bool => isset( $catalog[ $name ] )
		)
	);
}

/**
 * Liefert fremde WordPress-Abilities, die bewusst einzeln freigegeben werden
 * können. Eigene, Power- und Adapter-Infrastruktur bleibt aus diesem Katalog.
 * Die Provider-Permission jeder Ability wird bei der Ausführung weiterhin vom
 * WordPress Abilities API geprüft.
 *
 * @return array<string,array{label:string,description:string,provider:string,readonly:bool,destructive:bool}>
 */
function wpagent_companion_external_ability_catalog(): array {
	if ( ! function_exists( 'wp_get_abilities' ) ) {
		return array();
	}
	$catalog = array();
	foreach ( wp_get_abilities() as $name => $ability ) {
		if ( ! is_string( $name ) || preg_match( '#^(?:wp-agent|wp-agent-power|mcp-adapter)/#', $name ) ) {
			continue;
		}
		if ( ! is_object( $ability ) || ! method_exists( $ability, 'get_label' ) || ! method_exists( $ability, 'get_description' ) ) {
			continue;
		}
		$meta        = method_exists( $ability, 'get_meta' ) ? $ability->get_meta() : array();
		$annotations = is_array( $meta ) && isset( $meta['annotations'] ) && is_array( $meta['annotations'] ) ? $meta['annotations'] : array();
		$provider    = strstr( $name, '/', true );
		$catalog[ $name ] = array(
			'label'       => wp_html_excerpt( sanitize_text_field( (string) $ability->get_label() ), 200, '' ),
			'description' => wp_html_excerpt( sanitize_text_field( (string) $ability->get_description() ), 2000, '' ),
			'provider'    => false === $provider ? 'external' : sanitize_key( $provider ),
			'readonly'    => ! empty( $annotations['readonly'] ),
			'destructive' => ! empty( $annotations['destructive'] ),
		);
	}
	ksort( $catalog, SORT_STRING );
	return wpagent_companion_filter_external_ability_tool_collisions( $catalog );
}

/**
 * Entfernt fremde Abilities, deren normalisierter MCP-Werkzeugname mit einer
 * eigenen oder einer bereits aufgenommenen Ability kollidiert. Der Adapter
 * ersetzt den Namespace-Slash durch einen Bindestrich. Ohne diese Prüfung
 * könnte ein einzelnes Fremd-Plugin die Registrierung des Servers verhindern.
 *
 * @param array<string,array<string,mixed>> $catalog Sortierter externer Katalog.
 * @return array<string,array<string,mixed>>
 */
function wpagent_companion_filter_external_ability_tool_collisions( array $catalog ): array {
	ksort( $catalog, SORT_STRING );
	$tool_names = array();
	foreach ( array_keys( wpagent_companion_ability_catalog() ) as $own_name ) {
		$tool_names[ str_replace( '/', '-', $own_name ) ] = true;
	}
	$result = array();
	foreach ( $catalog as $name => $definition ) {
		$tool_name = str_replace( '/', '-', $name );
		if ( isset( $tool_names[ $tool_name ] ) ) {
			continue;
		}
		$tool_names[ $tool_name ] = true;
		$result[ $name ] = $definition;
	}
	return $result;
}

/** @return string[] */
function wpagent_companion_trusted_external_abilities(): array {
	$trusted = get_option( WPAGENT_COMPANION_OPT_TRUSTED_EXTERNAL_ABILITIES, array() );
	if ( ! is_array( $trusted ) ) {
		return array();
	}
	$catalog = wpagent_companion_external_ability_catalog();
	$trusted = array_filter( $trusted, 'is_string' );
	$trusted = array_values(
		array_filter(
			array_unique( $trusted ),
			static fn( string $name ): bool => isset( $catalog[ $name ] )
		)
	);
	if ( function_exists( 'wpagent_companion_ability_is_mcp_public' ) ) {
		$trusted = array_values(
			array_filter(
				$trusted,
				static fn( string $name ): bool => ! str_starts_with( $name, 'wpforms/' )
					|| wpagent_companion_ability_is_mcp_public( wpagent_companion_get_registered_ability( $name ) )
			)
		);
	}
	if ( function_exists( 'wpagent_companion_ninjaforms_quarantined_ability_names' ) ) {
		$quarantined = array_flip( wpagent_companion_ninjaforms_quarantined_ability_names() );
		$trusted     = array_values(
			array_filter(
				$trusted,
				static fn( string $name ): bool => ! str_starts_with( $name, 'ninjaforms/' )
					|| ( ! isset( $quarantined[ $name ] ) && wpagent_companion_ability_is_mcp_public( wpagent_companion_get_registered_ability( $name ) ) )
			)
		);
	}
	return $trusted;
}

/** @return string[] */
function wpagent_companion_enabled_ability_names(): array {
	$catalog  = wpagent_companion_ability_catalog();
	$profile  = wpagent_companion_profile();
	$groups   = wpagent_companion_profile_groups( $profile );
	$disabled = array_flip( wpagent_companion_disabled_abilities() );
	$names    = array();
	foreach ( $catalog as $name => $definition ) {
		$in_profile = 'readonly' === $profile
			? $definition['readonly']
			: in_array( $definition['group'], $groups, true );
		if ( $in_profile && ! isset( $disabled[ $name ] ) && wpagent_companion_ability_entitled( $name ) ) {
			$names[] = $name;
		}
	}
	$external = 'paid' === wpagent_companion_access_tier()
		? wpagent_companion_trusted_external_abilities()
		: array();
	if ( 'readonly' === $profile && ! empty( $external ) ) {
		$external_catalog = wpagent_companion_external_ability_catalog();
		$external         = array_values(
			array_filter(
				$external,
				static fn( string $name ): bool => ! empty( $external_catalog[ $name ]['readonly'] )
			)
		);
	}
	return array_values( array_unique( array_merge( $names, $external ) ) );
}

function wpagent_companion_ability_enabled( string $name ): bool {
	return in_array( $name, wpagent_companion_enabled_ability_names(), true );
}

function wpagent_companion_enabled(): bool {
	return '0' !== (string) get_option( WPAGENT_COMPANION_OPT_ENABLED, '1' );
}

function wpagent_companion_bound_site(): string {
	return (string) get_option( WPAGENT_COMPANION_OPT_BOUND_SITE, '' );
}

function wpagent_companion_domain_matches(): bool {
	if ( 'free' === wpagent_companion_access_tier() ) {
		return true;
	}
	$bound = wpagent_companion_bound_site();
	return '' !== $bound && hash_equals( $bound, wpagent_companion_license_site_id() );
}

/** Bindet eine bestehende Installation einmalig an ihre aktuelle Site-ID. */
function wpagent_companion_maybe_bind_site(): void {
	if ( '' === wpagent_companion_bound_site() && wpagent_companion_license_is_valid() ) {
		update_option( WPAGENT_COMPANION_OPT_BOUND_SITE, wpagent_companion_license_site_id(), false );
	}
}
add_action( 'init', 'wpagent_companion_maybe_bind_site', 1 );

/** Die einzige Gate-Funktion für Kategorie, REST-Abilities und MCP-Server. */
function wpagent_companion_gate_open(): bool {
	return wpagent_companion_enabled()
		&& wpagent_companion_domain_matches();
}

/** Schließt Companion und jede Power-Stufe unmittelbar. */
function wpagent_companion_emergency_disable(): bool {
	$changed = update_option( WPAGENT_COMPANION_OPT_ENABLED, '0', false );
	foreach ( array(
		'wp_agent_power_enabled',
		'wp_agent_power_stage_p1',
		'wp_agent_power_stage_p2',
		'wp_agent_power_stage_p3',
		'wp_agent_power_stage_p4',
		'wp_agent_power_stage_backup',
		'wp_agent_power_stage_restore',
	) as $option ) {
		$changed = update_option( $option, '0', false ) || $changed;
	}
	$changed = delete_option( 'wp_agent_power_prod_override' ) || $changed;
	return $changed;
}

/** Entfernt alle laut Profil oder Einzelschalter deaktivierten Abilities aus REST. */
function wpagent_companion_unregister_disabled_abilities(): void {
	$enabled = array_flip( wpagent_companion_enabled_ability_names() );
	foreach ( wpagent_companion_ability_catalog() as $name => $definition ) {
		if ( ! isset( $enabled[ $name ] ) && function_exists( 'wp_unregister_ability' ) ) {
			wp_unregister_ability( $name );
		}
	}
}

/**
 * Serverseitige, nebenwirkungsfreie Diagnose für Dashboard und REST-Status.
 *
 * @return array<int,array{name:string,ok:bool,detail:string}>
 */
function wpagent_companion_diagnostics(): array {
	$license_ok = wpagent_companion_license_is_valid();
	$profile    = wpagent_companion_profile();
	$permalink  = (string) get_option( 'permalink_structure', '' );
	$https_ok   = is_ssl() || in_array( wp_get_environment_type(), array( 'local', 'development' ), true );
	/* translators: %s: human-readable active ability profile label. */
	$profile_detail = sprintf( __( 'Aktives Ability-Profil: %s.', 'wp-agent-companion' ), wpagent_companion_profile_label( $profile ) );
	return array(
		array( 'name' => 'license', 'ok' => true, 'detail' => $license_ok ? __( 'Bezahlte Lizenz gültig.', 'wp-agent-companion' ) : __( 'Free ist aktiv. Für die vollständige Oberfläche ist eine Lizenz erforderlich.', 'wp-agent-companion' ) ),
		array( 'name' => 'master-switch', 'ok' => wpagent_companion_enabled(), 'detail' => wpagent_companion_enabled() ? __( 'Companion ist aktiviert.', 'wp-agent-companion' ) : __( 'Companion ist ausgeschaltet.', 'wp-agent-companion' ) ),
		array( 'name' => 'domain-lock', 'ok' => wpagent_companion_domain_matches(), 'detail' => 'free' === wpagent_companion_access_tier() ? __( 'Free verwendet die WordPress-Anmeldung. Der zusätzliche Domain Lock gehört zur bezahlten Lizenz.', 'wp-agent-companion' ) : ( wpagent_companion_domain_matches() ? __( 'Domain Lock stimmt mit dieser Website überein.', 'wp-agent-companion' ) : __( 'Die Website-Adresse weicht von der gebundenen Adresse ab.', 'wp-agent-companion' ) ) ),
		array( 'name' => 'rest-api', 'ok' => '' !== rest_url(), 'detail' => '' !== rest_url() ? __( 'REST-API ist konfiguriert.', 'wp-agent-companion' ) : __( 'REST-API-URL fehlt.', 'wp-agent-companion' ) ),
		array( 'name' => 'permalinks', 'ok' => '' !== $permalink, 'detail' => '' !== $permalink ? __( 'Pretty-Permalinks sind aktiv.', 'wp-agent-companion' ) : __( 'Permalinks stehen auf „Einfach“ und blockieren die Verbindung.', 'wp-agent-companion' ) ),
		array( 'name' => 'https', 'ok' => $https_ok, 'detail' => $https_ok ? __( 'Transportvoraussetzung erfüllt.', 'wp-agent-companion' ) : __( 'Remote-Verbindungen benötigen HTTPS.', 'wp-agent-companion' ) ),
		array( 'name' => 'profile', 'ok' => in_array( $profile, wpagent_companion_profiles(), true ), 'detail' => $profile_detail ),
	);
}

/**
 * Authentifiziert auslieferbarer Betriebszustand ohne Lizenzschlüssel oder
 * andere Zugangsdaten.
 *
 * @return array<string,mixed>
 */
function wpagent_companion_control_public_status(): array {
	$enabled = wpagent_companion_enabled_ability_names();
	$external = 'paid' === wpagent_companion_access_tier()
		? wpagent_companion_trusted_external_abilities()
		: array();
	return array(
		'version'               => defined( 'WPAGENT_COMPANION_VERSION' ) ? WPAGENT_COMPANION_VERSION : '',
		'access_tier'           => wpagent_companion_access_tier(),
		'gate_open'             => wpagent_companion_gate_open(),
		'master_enabled'        => wpagent_companion_enabled(),
		'domain_match'          => wpagent_companion_domain_matches(),
		'profile'               => wpagent_companion_profile(),
		'catalog_ability_count' => count( wpagent_companion_ability_catalog() ),
		'enabled_ability_count' => count( $enabled ),
		'enabled_abilities'     => $enabled,
		'external_ability_count' => count( $external ),
		'trusted_external_abilities' => $external,
		'diagnostics'           => wpagent_companion_diagnostics(),
	);
}
