<?php

namespace Formatrica\Capability\Rules;

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

use Formatrica\Capability\Capabilities;
use Formatrica\Capability\Premium_Config_Rule_Interface;

/**
 * P-WH2: gate the Make.com API-key auth (x-make-apikey header) behind
 * integrations.webhook_auth. Two modes on Free:
 *
 *   1. No existing key stored → strip any attempt to set one.
 *   2. An existing (grandfathered) key → the whole Make block is locked
 *      except the `enabled` toggle (disabling is the legitimate exit hatch);
 *      any other change hard-reverts to the stored block.
 *
 * Make is the only vendor here with a documented receive-side API-key header;
 * Zapier has no analogous convention, so no api_key field exists for it.
 * The api_key uses the write-only convention: an empty payload value with an
 * existing stored blob means "keep", not "clear".
 */
final class Webhook_Auth_Rule implements Premium_Config_Rule_Interface
{
    /** Fields locked when a grandfathered key exists (enabled excluded). */
    private const LOCKED_FIELDS = ['webhook_url', 'api_key', 'remove_api_key'];

    public function capability(): string
    {
        return Capabilities::INTEGRATIONS_WEBHOOK_AUTH;
    }

    public function enforce(array &$config, array $existing): array
    {
        $rejected = [];

        foreach (['make'] as $integration) {
            if (!isset($config['options']['integrations'][$integration]) || !is_array($config['options']['integrations'][$integration])) {
                continue;
            }

            $stored = is_array($existing['options']['integrations'][$integration] ?? null)
                ? $existing['options']['integrations'][$integration]
                : [];
            $incoming = &$config['options']['integrations'][$integration];

            $has_preserved_key = !empty($stored['api_key']);

            if (!$has_preserved_key) {
                // Mode 1: prevent setting a key on Free. The sanitizer has
                // already encrypted a POSTed plaintext into api_key by now, so
                // a non-empty value here IS a new-key attempt.
                if (!empty($incoming['api_key']) || !empty($incoming['api_key_set'])) {
                    $incoming['api_key'] = '';
                    $incoming['api_key_set'] = false;
                    $rejected[] = [
                        'key' => 'options.integrations.' . $integration . '.api_key',
                        'reason' => __('Webhook API-key authentication is a Pro feature.', 'formatrica'),
                        'capability' => $this->capability(),
                        'feature' => 'webhook_auth',
                    ];
                }
                continue;
            }

            // Mode 2: grandfathered key — lock the block except `enabled`.
            $user_enabled_choice = !empty($incoming['enabled']);
            $changed = [];
            foreach (self::LOCKED_FIELDS as $field) {
                $old_val = (string) ($stored[$field] ?? '');
                $new_val = (string) ($incoming[$field] ?? '');

                // Write-only secret: empty incoming + existing blob = preserve.
                // The sanitizer already applied that, so equal blobs compare
                // equal here; this guard keeps the semantic explicit.
                if ($field === 'api_key' && $new_val === $old_val) {
                    continue;
                }
                if ($old_val !== $new_val) {
                    $changed[] = $field;
                }
            }

            if ($changed !== []) {
                $incoming = $stored;
                $incoming['enabled'] = $user_enabled_choice;

                $rejected[] = [
                    'key' => 'options.integrations.' . $integration,
                    'reason' => __('The Make webhook settings are locked while its Pro API key is preserved; only the on/off toggle can change on Free.', 'formatrica'),
                    'capability' => $this->capability(),
                    'feature' => 'webhook_auth',
                ];
            }
        }

        return $rejected;
    }
}
