<?php
/**
 * Provider-misconfigured provider exception.
 *
 * Concrete {@see ProviderException} subclass that signals the provider
 * implementation cannot proceed because its own configuration is
 * incomplete or invalid: required credentials, associate tags, region
 * codes, or other admin-supplied settings are missing from the host
 * site's configuration store. The exception fires before any request
 * to the upstream API is dispatched — there is nothing to send because
 * the provider has not been wired up yet.
 *
 * Field semantics match `specifications/provider-api-contracts.md`
 * §10.2 / §7.10 exactly:
 *
 *   - {@see self::ERROR_CODE} is `provider_misconfigured`, the stable
 *     §10.2 code that REST error envelopes, log lines, and admin
 *     diagnostics views surface as the machine-readable identifier
 *     for this failure mode.
 *
 *   - The §7.10 health surface
 *     ({@see \CommerceLinkBuilder\Domain\Provider\ProviderHealthStatus})
 *     surfaces the same condition as
 *     {@see \CommerceLinkBuilder\Domain\Provider\ProviderHealthStatus::STATUS_UNCONFIGURED}.
 *     Provider implementations SHOULD raise this exception at the
 *     request boundary AND surface the `unconfigured` status from
 *     their health probe so the diagnostics surface and the runtime
 *     dispatch path agree on what is wrong.
 *
 *   - Per §10.1 the exception message MUST NOT carry secret material
 *     (credentials, signing keys, unredacted authorization headers);
 *     the provider implementation that constructs the exception is
 *     responsible for redacting before passing the summary in. The
 *     §10.1 rule binds especially tightly here because a
 *     misconfiguration message that names "missing AWS access key"
 *     is fine, but one that includes the partially-supplied key is
 *     not.
 *
 * Typical raise sites:
 *
 *   - Provider configuration loader inspects the admin settings
 *     store at request time and finds a required credential field
 *     entirely absent (PA-API access key not yet entered, 楽天
 *     application id missing, Yahoo!ショッピング client id never
 *     saved).
 *
 *   - Provider implementation requires a region / locale setting
 *     that the admin never selected; the per-region endpoint cannot
 *     be derived without it.
 *
 *   - Provider implementation requires a paired-credential set
 *     (access key + secret) and finds one half present but the
 *     other half missing — a state that almost always indicates a
 *     half-completed admin save rather than a deliberate choice.
 *
 *   - Application layer attempts to instantiate the provider before
 *     the bootstrap pipeline has loaded the relevant settings; the
 *     resulting exception lets the caller surface the "configure
 *     this provider in admin settings" prompt rather than silently
 *     failing.
 *
 * Distinct from {@see AuthenticationException}: that exception
 * fires when credentials are present but rejected by the upstream
 * API; this exception fires when credentials are missing or
 * structurally incomplete and the request never reaches the
 * upstream.
 *
 * Distinct from {@see UnsupportedFeatureException}: that exception
 * fires when the provider has a structural inability to support a
 * feature regardless of configuration; this exception fires when
 * the provider would in principle support the operation but its own
 * configuration is incomplete.
 *
 * @package CommerceLinkBuilder\Domain\Provider\Exception
 */

declare(strict_types=1);

namespace CommerceLinkBuilder\Domain\Provider\Exception;

use Throwable;

/**
 * Provider exception for missing or structurally incomplete
 * provider configuration that prevents the provider from issuing
 * any request to its upstream API.
 *
 * See the file-level docblock for the mapping to
 * `provider-api-contracts.md` §10.2 / §7.10, the alignment with
 * {@see \CommerceLinkBuilder\Domain\Provider\ProviderHealthStatus::STATUS_UNCONFIGURED},
 * and the distinctions versus {@see AuthenticationException} and
 * {@see UnsupportedFeatureException}.
 */
final class ProviderConfigurationException extends ProviderException {

	/**
	 * Stable §10.2 error code for this failure mode.
	 *
	 * Exposed as a class constant so call sites can reference the
	 * exact token without duplicating the literal, and so a future
	 * spec change has a single location to update.
	 */
	public const ERROR_CODE = 'provider_misconfigured';

	/**
	 * Construct a provider-misconfigured exception.
	 *
	 * Forwards {@see self::ERROR_CODE} to the
	 * {@see ProviderException} base so every misconfiguration raise
	 * carries the same §10.2 code regardless of which provider
	 * implementation surfaced it. The remaining cross-cutting fields
	 * (`provider`, `message`, `context`, `previous`) follow the
	 * base's contract verbatim — see {@see ProviderException} for
	 * the per-field validation rules and the secret-material rule
	 * that binds the message body.
	 *
	 * Provider implementations SHOULD populate the `context` map
	 * with a `missing_keys` entry enumerating the required setting
	 * names that were absent (without their would-be values) so the
	 * admin diagnostics surface can render a precise "configure
	 * these settings to enable this provider" prompt without parsing
	 * the message body.
	 *
	 * @param string               $provider §4 provider key that raised the exception.
	 * @param string               $message  Human-readable, non-secret summary.
	 * @param array<string, mixed> $context  Structured diagnostic context;
	 *                                       SHOULD include a `missing_keys`
	 *                                       entry enumerating the absent
	 *                                       setting names.
	 * @param Throwable|null       $previous Underlying cause, or null.
	 */
	public function __construct(
		string $provider,
		string $message = '',
		array $context = array(),
		?Throwable $previous = null
	) {
		parent::__construct( self::ERROR_CODE, $provider, $message, $context, $previous );
	}
}
