<?php

namespace Formatrica\Submission\Pipeline\Stages;

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

use Formatrica\Forms\Form_Repository;
use Formatrica\Submission\Exception\Submission_Exception;
use Formatrica\Submission\Pipeline\Submission_Context;
use Formatrica\Submission\Pipeline\Submission_Stage;

/**
 * Load the form, reject missing/inactive, and hydrate sensitive options
 * (secrets kept out of the builder payload are read back from stored config).
 */
final class Form_Lookup_Stage implements Submission_Stage
{
    public function __construct(private Form_Repository $forms)
    {
    }

    public function handle(Submission_Context $ctx): void
    {
        $form = $this->forms->get($ctx->form_id);

        if (null === $form) {
            throw new Submission_Exception(esc_html__('Form not found.', 'formatrica'), 404);
        }

        if (isset($form['active']) && (int) $form['active'] !== 1) {
            throw new Submission_Exception(esc_html__('This form is inactive.', 'formatrica'), 404);
        }

        $config = $form['config'];
        $config['options'] = $this->hydrate_sensitive_options($config['options'], $ctx->form_id);

        // Payment-enabled forms FAIL CLOSED when the payment capability is
        // unavailable (Free package, or Pro with a lapsed licence) — plan
        // §5.3: accepting the submission without payment would trigger
        // fulfilment side-effects (emails, integrations) for an unpaid order.
        // Neutral message; no Pro details exposed publicly.
        if (!empty($config['options']['payments']['enabled'])) {
            $gate = \Formatrica\Capability\Feature_Gate::get_default();
            if ($gate === null || !$gate->has(\Formatrica\Capability\Capabilities::PAYMENTS_BASIC)) {
                throw new Submission_Exception(
                    esc_html__('This form is temporarily unavailable. Please try again later.', 'formatrica'),
                    503
                );
            }
        }

        $ctx->form = $form;
        $ctx->config = $config;
        $ctx->options = $config['options'];
    }

    /**
     * @param array<string, mixed> $options
     * @return array<string, mixed>
     */
    private function hydrate_sensitive_options(array $options, int $form_id): array
    {
        if ($form_id <= 0) {
            return $options;
        }

        $raw = $this->forms->raw_config($form_id);
        $raw_options = isset($raw['options']) && is_array($raw['options']) ? $raw['options'] : [];

        if (
            isset($options['email_delivery']['smtp']['password']) &&
            $options['email_delivery']['smtp']['password'] === '' &&
            !empty($raw_options['email_delivery']['smtp']['password'])
        ) {
            $options['email_delivery']['smtp']['password'] = $raw_options['email_delivery']['smtp']['password'];
        }

        if (
            isset($options['webhooks']['secret']) &&
            $options['webhooks']['secret'] === '' &&
            !empty($raw_options['webhooks']['secret'])
        ) {
            $options['webhooks']['secret'] = $raw_options['webhooks']['secret'];
        }

        if (
            isset($options['captcha']['secret_key']) &&
            $options['captcha']['secret_key'] === '' &&
            !empty($raw_options['captcha']['secret_key'])
        ) {
            $options['captcha']['secret_key'] = $raw_options['captcha']['secret_key'];
        }

        // Headless API key (Pro api.headless): redacted to '' in the builder
        // GET payload; restore the stored encrypted blob so Headless_Auth_Stage
        // can decrypt and compare it. Without this the stored key stays empty
        // and every valid API key is rejected as key_invalid.
        if (
            isset($options['headless_api_key']) &&
            $options['headless_api_key'] === '' &&
            !empty($raw_options['headless_api_key'])
        ) {
            $options['headless_api_key'] = $raw_options['headless_api_key'];
        }

        $options['integrations'] = $this->hydrate_integration_secrets(
            isset($options['integrations']) && is_array($options['integrations']) ? $options['integrations'] : [],
            isset($raw_options['integrations']) && is_array($raw_options['integrations']) ? $raw_options['integrations'] : []
        );

        return $options;
    }

    /**
     * @param array<string, mixed> $integrations
     * @param array<string, mixed> $raw_integrations
     * @return array<string, mixed>
     */
    private function hydrate_integration_secrets(array $integrations, array $raw_integrations): array
    {
        foreach ($raw_integrations as $integration_id => $raw_settings) {
            if (!is_array($raw_settings)) {
                continue;
            }

            if (!isset($integrations[$integration_id]) || !is_array($integrations[$integration_id])) {
                $integrations[$integration_id] = $raw_settings;
            }

            switch ($integration_id) {
                case 'mailchimp':
                case 'brevo':
                    if (
                        isset($integrations[$integration_id]['api_key']) &&
                        $integrations[$integration_id]['api_key'] === '' &&
                        !empty($raw_settings['api_key'])
                    ) {
                        $integrations[$integration_id]['api_key'] = $raw_settings['api_key'];
                    }
                    break;
                case 'hubspot':
                    if (
                        isset($integrations[$integration_id]['access_token']) &&
                        $integrations[$integration_id]['access_token'] === '' &&
                        !empty($raw_settings['access_token'])
                    ) {
                        $integrations[$integration_id]['access_token'] = $raw_settings['access_token'];
                    }
                    break;
                default:
                    break;
            }
        }

        return $integrations;
    }
}
