<?php

namespace GriffinForms\Includes\Integrations;

use GriffinForms\Config;
use GriffinForms\Log;
use GriffinForms\Traits\Singleton;
use WP_Error;

/**
 * Manages per-form integration settings storage.
 *
 * Stores a JSON envelope in the forms table to avoid meta proliferation.
 */
class FormSettingsManager
{
    use Singleton;

    private const MAX_SETTINGS_BYTES = 262144; // 256KB guardrail per form

    private $log;

    private function __construct()
    {
        $this->log = Log::getInstance();
    }

    /**
     * Return decoded settings array for a form.
     */
    public function getFormSettings(int $form_id): array
    {
        global $wpdb;

        if ($form_id <= 0) {
            return [];
        }

        $table = Config::getInstance()->getTable('form');
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT integration_settings FROM %i WHERE id = %d",
                $table,
                $form_id
            )
        );

        if (!$row || empty($row->integration_settings)) {
            return [];
        }

        $decoded = json_decode($row->integration_settings, true);
        if (!is_array($decoded)) {
            $throttle_key = 'gf_intg_decode_warn_' . $form_id;
            if (!get_transient($throttle_key)) {
                set_transient($throttle_key, 1, DAY_IN_SECONDS);
                $category = sprintf('Integration (%s)', $integration_id ?? 'unknown');
                $this->log->add('warning', 'form', $form_id, get_current_user_id(), sprintf('Failed to decode integration settings for form %d', $form_id), $category);
            }
            return [];
        }

        return $decoded;
    }

    /**
     * Get settings for a single integration on a form.
     */
    public function getIntegrationSettings(int $form_id, string $integration_id): ?array
    {
        $all = $this->getFormSettings($form_id);
        return $all[$integration_id] ?? null;
    }

    /**
     * Persist integration settings for a form.
     *
     * @param int $form_id
     * @param string $integration_id
     * @param array $payload schema: [mode => inherit|custom|disabled, settings => array, updated_at, updated_by]
     * @return true|WP_Error
     */
    public function saveIntegrationSettings(int $form_id, string $integration_id, array $payload)
    {
        global $wpdb;

        if ($form_id <= 0) {
            return new WP_Error('invalid_form', __('Invalid form ID', 'griffinforms-form-builder'));
        }

        if (empty($integration_id)) {
            return new WP_Error('invalid_integration', __('Invalid integration ID', 'griffinforms-form-builder'));
        }

        $settings = $this->getFormSettings($form_id);
        $settings[$integration_id] = $payload;

        $json = wp_json_encode($settings);

        if ($json === false) {
            $category = sprintf('Integration (%s)', $integration_id);
            $this->log->add('error', 'form', $form_id, get_current_user_id(), sprintf('Failed to encode integration settings for form %d', $form_id), $category);
            return new WP_Error('encode_error', __('Failed to encode integration settings.', 'griffinforms-form-builder'));
        }

        if (strlen($json) > self::MAX_SETTINGS_BYTES) {
            $category = sprintf('Integration (%s)', $integration_id);
            $this->log->add('error', 'form', $form_id, get_current_user_id(), sprintf('Settings payload too large for form %d', $form_id), $category);
            return new WP_Error(
                'settings_too_large',
                __('Form integration settings exceed the allowed size.', 'griffinforms-form-builder')
            );
        }

        $table = Config::getInstance()->getTable('form');
        $updated = $wpdb->update(
            $table,
            ['integration_settings' => $json],
            ['id' => $form_id],
            ['%s'],
            ['%d']
        );

        if ($updated === false) {
            $category = sprintf('Integration (%s)', $integration_id);
            $this->log->add('error', 'form', $form_id, get_current_user_id(), sprintf('Failed to save integration settings for form %d', $form_id), $category);
            return new WP_Error('db_error', __('Failed to save integration settings.', 'griffinforms-form-builder'));
        }

        $category = sprintf('Integration (%s)', $integration_id);
        $this->log->add('info', 'form', $form_id, get_current_user_id(), sprintf('Integration %s settings saved for form %d', $integration_id, $form_id), $category);
        return true;
    }

    /**
     * Merge global config with per-form settings based on mode.
     *
     * - inherit: return global as-is
     * - custom: overlay form settings on top of global (non-destructive)
     * - disabled: return marker to skip execution
     *
     * @param array $global_config
     * @param array|null $form_settings
     * @param bool $allow_override
     * @return array
     */
    public function mergeSettings(array $global_config, ?array $form_settings, bool $allow_override): array
    {
        if (!$allow_override || empty($form_settings)) {
            return $global_config;
        }

        $mode = $form_settings['mode'] ?? 'inherit';
        $settings = $form_settings['settings'] ?? [];

        if ($mode === 'disabled') {
            return ['mode' => 'disabled'];
        }

        if ($mode !== 'custom') {
            return $global_config;
        }

        if (!is_array($settings)) {
            $category = sprintf('Integration (%s)', $form_settings['id'] ?? 'unknown');
            $this->log->add('error', 'form', 0, get_current_user_id(), 'Settings payload not array; using global config.', $category);
            return $global_config;
        }

        // Custom mode: overlay additional settings onto global without discarding global keys
        return array_replace_recursive($global_config, $settings);
    }
}
