settings = $settings; $this->http = $http; } public function register(): void { \add_action( 'admin_post_' . self::REFRESH_ACTION, [ $this, 'handleRefresh' ] ); } public function handleRefresh(): void { if ( ! \current_user_can( SettingsPage::CAPABILITY ) ) { \wp_die( \esc_html__( 'Permission denied.', 'agent001-support-assistant-for-woocommerce' ) ); } \check_admin_referer( self::REFRESH_ACTION ); \delete_transient( self::AGENTS_TRANSIENT ); $url = \add_query_arg( [ 'page' => SettingsPage::PAGE_SLUG, 'agents_refreshed' => '1', ], \admin_url( 'admin.php' ) ); \wp_safe_redirect( $url ); exit; } /** * @param array $ids * @return string|null */ public function saveSelection( array $ids ): ?string { if ( ! $this->settings->hasApiKey() ) { return \__( 'Connect to the backend before selecting agents.', 'agent001-support-assistant-for-woocommerce' ); } $clean = AgentId::normalizeList( $ids ); if ( count( $clean ) > 1 ) { return \__( 'Select one agent for this store.', 'agent001-support-assistant-for-woocommerce' ); } $body = \wp_json_encode( [ 'agent_ids' => $clean ] ); if ( $body === false ) { return \__( 'Failed to encode agent selection.', 'agent001-support-assistant-for-woocommerce' ); } $primaryAgentId = $clean[0] ?? ''; $storeHost = \wp_parse_url( \home_url( '/' ), PHP_URL_HOST ); if ( $primaryAgentId !== '' && ( ! is_string( $storeHost ) || $storeHost === '' ) ) { return \__( 'Could not determine this store domain.', 'agent001-support-assistant-for-woocommerce' ); } $result = $this->http->putJson( $this->settings->agentsUrl(), $this->headers(), $body ); if ( ! $result->isSuccess() ) { \delete_transient( self::AGENTS_TRANSIENT ); return sprintf( /* translators: %s: error from remote */ \__( 'Backend rejected the agent selection (%s).', 'agent001-support-assistant-for-woocommerce' ), $result->error() ); } $decoded = json_decode( $result->body(), true ); if ( ! is_array( $decoded ) || ! isset( $decoded['agents'] ) || ! is_array( $decoded['agents'] ) ) { \delete_transient( self::AGENTS_TRANSIENT ); return \__( 'The backend returned an invalid agent selection response. Please try again.', 'agent001-support-assistant-for-woocommerce' ); } $normalized = $this->normalize( $decoded ); $selectedAgent = array_filter( $normalized['agents'], static function ( array $agent ) use ( $primaryAgentId ): bool { return $agent['id'] === $primaryAgentId && $agent['embed_token'] !== ''; } ); if ( $primaryAgentId !== '' && ( $selectedAgent === [] || ! in_array( $primaryAgentId, $normalized['selected_ids'], true ) ) ) { \delete_transient( self::AGENTS_TRANSIENT ); return \__( 'The backend did not confirm the selected agent and its widget token. Please try again.', 'agent001-support-assistant-for-woocommerce' ); } if ( $primaryAgentId !== '' ) { $domainBody = \wp_json_encode( [ 'domain' => $storeHost ] ); if ( $domainBody === false ) { return \__( 'Failed to prepare the store domain for the selected agent.', 'agent001-support-assistant-for-woocommerce' ); } $domainResult = $this->http->postJson( $this->settings->agentDomainUrl( $primaryAgentId ), $this->headers(), $domainBody ); if ( ! $domainResult->isSuccess() ) { \delete_transient( self::AGENTS_TRANSIENT ); return sprintf( /* translators: %s: error from remote */ \__( 'Could not add this store domain to the selected agent (%s).', 'agent001-support-assistant-for-woocommerce' ), $domainResult->error() ); } } $this->settings->setSelectedAgentIds( $clean ); $this->settings->setAccountName( $normalized['account_name'] ); \set_transient( self::AGENTS_TRANSIENT, $normalized, self::AGENTS_TTL ); $this->persistSelectedToken( $normalized, $primaryAgentId ); return null; } /** * @return array{agents: list, selected_ids: list, account_name: string}|null */ public function fetchAgents( bool $forceRefresh = false ): ?array { if ( ! $this->settings->hasApiKey() ) { return null; } if ( ! $forceRefresh ) { $cached = \get_transient( self::AGENTS_TRANSIENT ); $cacheHasAgentStatus = is_array( $cached ) && isset( $cached['agents'] ) && is_array( $cached['agents'] ) && array_filter( $cached['agents'], static fn( $agent ): bool => ! is_array( $agent ) || ! array_key_exists( 'active', $agent ) ) === []; if ( is_array( $cached ) && isset( $cached['agents'] ) && $cacheHasAgentStatus ) { return $cached; } } $result = $this->http->getJson( $this->settings->agentsUrl(), $this->headers() ); if ( ! $result->isSuccess() ) { return null; } $decoded = json_decode( $result->body(), true ); if ( ! is_array( $decoded ) || ! isset( $decoded['agents'] ) || ! is_array( $decoded['agents'] ) ) { return null; } $normalized = $this->normalize( $decoded ); if ( $normalized['account_name'] !== '' ) { $this->settings->setAccountName( $normalized['account_name'] ); } \set_transient( self::AGENTS_TRANSIENT, $normalized, self::AGENTS_TTL ); $primary = $normalized['selected_ids'][0] ?? ''; if ( $primary !== '' ) { $this->persistSelectedToken( $normalized, $primary ); } return $normalized; } public function setAgentActive( string $agentId, bool $active ): ?string { $agentId = AgentId::normalize( $agentId ); if ( $agentId === '' || ! $this->settings->hasApiKey() ) { return \__( 'Could not update the agent status.', 'agent001-support-assistant-for-woocommerce' ); } $body = \wp_json_encode( [ 'active' => $active ] ); if ( $body === false ) { return \__( 'Failed to prepare the agent status update.', 'agent001-support-assistant-for-woocommerce' ); } $result = $this->http->postJson( $this->settings->agentActiveUrl( $agentId ), $this->headers(), $body ); if ( ! $result->isSuccess() ) { return sprintf( /* translators: %s: error from remote */ \__( 'Could not update the agent status (%s).', 'agent001-support-assistant-for-woocommerce' ), $result->error() ); } $decoded = json_decode( $result->body(), true ); if ( ! is_array( $decoded ) || AgentId::normalize( $decoded['agent_id'] ?? null ) !== $agentId || ! isset( $decoded['active'] ) || $decoded['active'] !== $active ) { return \__( 'The backend did not confirm the agent status update.', 'agent001-support-assistant-for-woocommerce' ); } if ( in_array( $agentId, $this->settings->selectedAgentIds(), true ) ) { $this->settings->setSelectedAgentActive( $active ); } $cached = \get_transient( self::AGENTS_TRANSIENT ); if ( is_array( $cached ) && isset( $cached['agents'] ) && is_array( $cached['agents'] ) ) { foreach ( $cached['agents'] as &$agent ) { if ( is_array( $agent ) && AgentId::normalize( $agent['id'] ?? null ) === $agentId ) { $agent['active'] = $active; } } unset( $agent ); \set_transient( self::AGENTS_TRANSIENT, $cached, self::AGENTS_TTL ); } return null; } /** * @param array $payload * @return array{agents: list, selected_ids: list, account_name: string} */ private function normalize( array $payload ): array { $agents = []; if ( isset( $payload['agents'] ) && is_array( $payload['agents'] ) ) { foreach ( $payload['agents'] as $entry ) { if ( ! is_array( $entry ) ) { continue; } $id = AgentId::normalize( $entry['id'] ?? null ); $name = isset( $entry['name'] ) && is_scalar( $entry['name'] ) ? \sanitize_text_field( (string) $entry['name'] ) : ''; $token = isset( $entry['embed_token'] ) && is_scalar( $entry['embed_token'] ) ? (string) $entry['embed_token'] : ''; if ( $id !== '' && $name !== '' ) { $agents[] = [ 'id' => $id, 'name' => $name, 'embed_token' => $token, 'active' => ! array_key_exists( 'active', $entry ) || (bool) $entry['active'], ]; } } } $selected = isset( $payload['selected_ids'] ) && is_array( $payload['selected_ids'] ) ? AgentId::normalizeList( $payload['selected_ids'] ) : []; return [ 'agents' => $agents, 'selected_ids' => $selected, 'account_name' => isset( $payload['account_name'] ) && is_string( $payload['account_name'] ) ? \sanitize_text_field( $payload['account_name'] ) : '', ]; } /** * @param array{agents: list, selected_ids: list} $payload */ private function persistSelectedToken( array $payload, string $primarySelectedId ): void { if ( $primarySelectedId === '' ) { $this->settings->setSelectedAgentEmbedToken( '' ); $this->settings->setSelectedAgentActive( true ); return; } foreach ( $payload['agents'] as $agent ) { if ( $agent['id'] === $primarySelectedId ) { $this->settings->setSelectedAgentEmbedToken( $agent['embed_token'] ); $this->settings->setSelectedAgentActive( $agent['active'] ); return; } } $this->settings->setSelectedAgentEmbedToken( '' ); $this->settings->setSelectedAgentActive( true ); } /** * @return array */ private function headers(): array { return [ 'Content-Type' => 'application/json', 'X-API-Key' => $this->settings->apiKey(), 'Accept' => 'application/json', 'User-Agent' => 'Agent001/' . ( \defined( 'AGENT001_VERSION' ) ? \AGENT001_VERSION : 'dev' ), ]; } }