<?php

/**
 * Provides methods to retrieve and sanitize settings fields and options.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Admin\Settings;

use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Envs;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Hub_Account_Setup;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Helpers\Hook_Helpers;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Abstract_Module;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Admin\Pages\Abstract_Admin_Page;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Cron_Module;
defined('ABSPATH') || exit;
/**
 * Class Settings_Fields
 */
class Settings_Fields_Module extends Abstract_Module
{
    /**
     * SECTION NAMES
     */
    public const SECTION_AUTOMATIC_SYNC = 'section_automatic_sync';
    public const SECTION_SYNC_SETTINGS = 'section_sync_settings';
    public const SECTION_RECEIPT = 'section_receipt';
    public const SECTION_ADVANCED = 'section_advanced';
    /**
     * Automatic sync.
     */
    public const AUTO_SYNC = 'automatic_sync';
    public const SYNC_INTERVAL = 'automatic_sync_interval';
    /**
     * Sync settings.
     */
    public const SYNC_PRODUCT_TAX_CLASS = 'sync_product_tax_class';
    /**
     * Receipts
     */
    public const CREATE_RECEIPTS = 'create_hub_receipts';
    /**
     * Advanced
     */
    public const DEBUG_LOG = 'debug_log_enabled';
    public const HUB_ORDER_PREFIX = 'hub_order_number_prefix';
    /**
     * Fields.
     *
     * @var array
     */
    private array $fields = array();
    /**
     * Module setup.
     *
     * @return void
     */
    public function setup(): void
    {
        $has_fields = $this->shared->admin->settings_fields->maybe_set_fields();
        if ($has_fields) {
            $this->shared->admin->pages->set_create_settings_page(\true);
        }
    }
    /**
     * Get all registered fields.
     *
     * @return array
     */
    public function get_all_fields(): array
    {
        return $this->fields ?? array();
    }
    /**
     * Get the default value for a field.
     *
     * @param string $name The name of the field.
     *
     * @return mixed
     */
    public function get_field_default(string $name): mixed
    {
        $fields = $this->get_all_fields();
        foreach ($fields as $field) {
            if (($field['name'] ?? '') === $name) {
                return $field['default'];
            }
        }
        return null;
    }
    /**
     * Get fields.
     *
     * @param string $target      The target to filter fields by.
     * @param string $target_name The target name to filter fields by.
     *
     * @return array Array of fields that match the given target and target name.
     */
    public function get_fields(string $target, string $target_name): array
    {
        return array_values(array_filter($this->fields, function ($field) use ($target, $target_name) {
            return $field['target'] === $target && $field['target_name'] === $target_name;
        }));
    }
    /**
     * Get field.
     *
     * @param string $id Field id.
     *
     * @return mixed
     */
    public function get_field(string $id): mixed
    {
        $fields = array_filter($this->fields, function ($field) use ($id) {
            return $field['id'] === $id;
        });
        return array_values($fields)[0];
    }
    /**
     * Get option.
     *
     * @param int|string $post_id Post id or target name.
     * @param string     $key The option key.
     * @param bool       $single Optional. Whether to return a single value or an array of values. Default is true.
     *
     * @return array
     */
    public function get_option(int|string $post_id, string $key, bool $single = \true): array
    {
        if (empty($this->fields)) {
            return array();
        }
        $target_values = is_numeric($post_id) ? get_post_meta($post_id, $key, $single) : get_option($key, null);
        if (!is_array($target_values)) {
            $target_values = array();
        }
        return $this->get_option_internal($this->fields, $target_values);
    }
    /**
     * Update option.
     *
     * @param int|string $post_id Post id or target name.
     * @param string     $key The option key.
     * @param mixed      $values The option values.
     *
     * @return bool Whether the option was updated.
     */
    public function update_option(int|string $post_id, string $key, mixed $values): bool
    {
        if (empty($this->fields)) {
            return \false;
        }
        $target_values = $this->get_option_internal($this->fields, $values, 'edit');
        if (is_numeric($post_id)) {
            return (bool) update_post_meta($post_id, $key, $target_values);
        } else {
            return update_option($key, $target_values);
        }
    }
    /**
     * Retrieve the option values from an array of fields and values.
     *
     * @param array  $fields The array of fields.
     * @param array  $values The array of values.
     * @param string $context The context in which the values are being retrieved. Can be 'view' or 'edit'.
     *
     * @return array The new array of option values.
     */
    private function get_option_internal(array $fields, array $values, string $context = 'view'): array
    {
        $new_values = array();
        foreach ($fields as $field) {
            if ('section' === $field['type']) {
                // Do nothing.
                continue;
            } elseif ('hub_accounts' === $field['type']) {
                if (!empty($values[Envs::SETTING_NAME])) {
                    $new_values[Envs::SETTING_NAME] = $values[Envs::SETTING_NAME];
                }
            } elseif ('repeater' === $field['type']) {
                $repeater_values = array();
                foreach ($values as $key => $value) {
                    $matches = array();
                    if (1 !== preg_match('/^' . $field['name'] . '_(.+)_([\d+])$/', $key, $matches)) {
                        continue;
                    }
                    $repeater_values[intval($matches[2])][$matches[1]] = $value;
                }
                if (empty($repeater_values)) {
                    continue;
                }
                foreach ($repeater_values as $repeater_key => $repeater_value) {
                    if ($repeater_key > $field['max_fields'] - 1) {
                        continue;
                    }
                    if ('edit' === $context) {
                        foreach ($repeater_value as $sub_field_name => $sub_field_value) {
                            foreach ($field['fields'] as $sub_field) {
                                if ($sub_field['name'] === $sub_field_name) {
                                    $new_values[$field['name'] . '_' . $sub_field_name . '_' . $repeater_key] = $this->sanitize_out_field($sub_field_value, $sub_field['type']);
                                    break;
                                }
                            }
                        }
                    } else {
                        $new_values[$field['name']][$repeater_key] = $this->get_option_internal($field['fields'], $repeater_value);
                    }
                }
            } elseif (!isset($values[$field['name']])) {
                $new_values[$field['name']] = $this->sanitize_out_field($field['default'], $field['type']);
            } else {
                $new_values[$field['name']] = $this->sanitize_out_field($values[$field['name']], $field['type']);
            }
        }
        return $new_values;
    }
    /**
     * Maybe set fields.
     *
     * @return bool Whether any fields were set.
     */
    public function maybe_set_fields(): bool
    {
        $shared_fields = $this->get_shared_settings_fields();
        $all_fields = Hook_Helpers::apply_scoped_filters('northmill_online_shared_settings_fields', $shared_fields);
        if (!empty($all_fields)) {
            $this->set_fields($all_fields);
            return \true;
        }
        return \false;
    }
    /**
     * Get shared settings fields.
     *
     * @return array
     */
    private function get_shared_settings_fields(): array
    {
        $shared = $this->shared;
        $fields = array();
        if (Hub_Account_Setup::is_setting_up_hub_accounts()) {
            $fields = array_merge($fields, array(array('type' => 'hub_accounts'), array('type' => 'section', 'name' => self::SECTION_AUTOMATIC_SYNC, 'label' => function () {
                return __('Automatic sync', 'northmill-online-shared');
            }, 'priority' => 1), array(
                'type' => 'checkbox',
                'name' => self::AUTO_SYNC,
                // mf_sync_status.
                'label' => function () {
                    return __('Automatic sync', 'northmill-online-shared');
                },
                'default' => \false,
                'section' => self::SECTION_AUTOMATIC_SYNC,
                'priority' => 10,
            ), array(
                'type' => 'select',
                'name' => self::SYNC_INTERVAL,
                // mf_sync_articles_interval.
                'label' => function () {
                    return __('Syncing interval', 'northmill-online-shared');
                },
                'options' => function () {
                    return $this->get_available_cron_intervals();
                },
                'default' => Cron_Module::SCHEDULE_FIVE_MINUTES,
                'section' => self::SECTION_AUTOMATIC_SYNC,
                'description' => function () {
                    return __('How often the automatic sync of recent changes should run.', 'northmill-online-shared');
                },
                'priority' => 20,
            ), array('type' => 'section', 'name' => self::SECTION_SYNC_SETTINGS, 'label' => function () {
                return __('Sync settings', 'northmill-online-shared');
            }, 'priority' => 2), array(
                'type' => 'checkbox',
                'name' => self::SYNC_PRODUCT_TAX_CLASS,
                // is_tax_class_active.
                'label' => function () {
                    return __('Sync article tax classes', 'northmill-online-shared');
                },
                'default' => \true,
                // Enabled by default.
                'section' => self::SECTION_SYNC_SETTINGS,
                'description' => function () {
                    return __('When enabled, article tax classes are synced.', 'northmill-online-shared');
                },
                'priority' => 90,
            ), array('type' => 'section', 'name' => self::SECTION_RECEIPT, 'label' => function () {
                return __('Receipts', 'northmill-online-shared');
            }, 'priority' => 3), array(
                'type' => 'checkbox',
                'name' => self::CREATE_RECEIPTS,
                // is_receipt_active.
                'label' => function () {
                    return __('Create receipts in Hub', 'northmill-online-shared');
                },
                'default' => \false,
                'disabled_callback' => array($this->shared->sync->receipts, 'get_receipt_select_input_disabled_status'),
                'section' => self::SECTION_RECEIPT,
                'description' => function () {
                    return __('When enabled, receipts are created in Hub for new orders with payment methods that are considered to handle the transaction on order.', 'northmill-online-shared');
                },
                'extra_html_callback' => array($this->shared->sync->receipts, 'get_receipts_enabled_field_extra_html'),
                'priority' => 10,
            ), array('type' => 'section', 'name' => self::SECTION_ADVANCED, 'label' => function () {
                return __('Advanced', 'northmill-online-shared');
            }, 'priority' => 90), array(
                'type' => 'checkbox',
                'name' => self::DEBUG_LOG,
                // debug_log_enabled.
                'label' => function () {
                    return __('Debug log enabled', 'northmill-online-shared');
                },
                'description' => function () {
                    return __('This creates an additional log file with much more detailed log information.', 'northmill-online-shared') . '<p>' . Abstract_Shared_Base::base_instance()->logger->get_wc_logs_a_tag() . '</p>';
                },
                'default' => \false,
                'section' => self::SECTION_ADVANCED,
                'priority' => 10,
            ), array(
                'type' => 'text',
                'name' => self::HUB_ORDER_PREFIX,
                // hub_order_number_prefix.
                'label' => function () {
                    return __('Hub order number prefix', 'northmill-online-shared');
                },
                'description' => function () use ($shared) {
                    $message = sprintf(
                        // translators: %s is the package name.
                        __('If you have %s in multiple WordPress installations, connecting to the same Hub account, this has to be set uniquely for each installation. This is to prevent order number collisions. Use only letters, numbers and dashes.', 'northmill-online-shared'),
                        $shared->package_info->get_service_name()
                    );
                    $message .= '<br>';
                    $message .= __('Do NOT edit this if you do not know what you are doing. It will affect your order numbers, and might cause order collisions.', 'northmill-online-shared');
                    return $message;
                },
                'default' => '',
                'section' => self::SECTION_ADVANCED,
                'priority' => 40,
            )));
        }
        return $fields;
    }
    /**
     * Get available cron intervals.
     *
     * @return array
     */
    private function get_available_cron_intervals(): array
    {
        $wp_schedules = Cron_Module::cron_schedules(array());
        return array_map(function ($value) {
            return $value['display'];
        }, $wp_schedules);
    }
    /**
     * Load the fields for the settings.
     *
     * @param array $fields Field data.
     */
    public function set_fields(array $fields): void
    {
        foreach ($fields as &$field) {
            $defaults = $this->get_field_defaults($field);
            // $field is passed by reference.
            $field = wp_parse_args($field, $defaults);
            if ('repeater' === $field['type']) {
                foreach ($field['fields'] as &$sub_field) {
                    $sub_field = wp_parse_args($sub_field, array('default' => '', 'description' => ''));
                }
            }
        }
        usort($fields, function ($a, $b) {
            return $a['priority'] <=> $b['priority'];
        });
        $this->fields = $fields;
    }
    /**
     * Get field defaults.
     *
     * @param array $field Field (passed by reference).
     *
     * @return array
     */
    private function get_field_defaults(array &$field): array
    {
        $defaults = array('id' => '', 'target' => 'setting', 'target_name' => Abstract_Admin_Page::get_settings_id(), 'default' => '', 'description' => '', 'priority' => 50);
        if ('hub_accounts' === $field['type']) {
            return $this->get_hub_accounts_field_defaults($defaults);
        }
        if (isset($field['name'])) {
            $underscored_name = str_replace('-', '_', $field['name']);
            $defaults['id'] = Abstract_Shared_Base::base_instance()->package_info->get_service_name_underscored() . '_' . $underscored_name;
        }
        if (isset($field['section']) && !isset($field['id'])) {
            $defaults['section'] = Abstract_Shared_Base::base_instance()->package_info->get_service_name_underscored() . '_' . $field['section'];
            unset($field['section']);
            // Note that we remove the section to have it set from default.
        }
        return $defaults;
    }
    /**
     * Get hub accounts field defaults.
     *
     * @param array $defaults Defaults.
     *
     * @return array
     */
    private function get_hub_accounts_field_defaults(array $defaults): array
    {
        $defaults['id'] = Abstract_Shared_Base::base_instance()->package_info->get_service_name_underscored() . '_hub_accounts';
        $defaults['name'] = 'hub_accounts';
        $defaults['label'] = function () {
            return __('Hub Accounts', 'northmill-online-shared');
        };
        $defaults['priority'] = -1;
        return $defaults;
    }
    /**
     * Sanitizes output based on the type of field
     *
     * @param mixed  $value Value to sanitize.
     * @param string $type  Type of field.
     *
     * @return mixed Sanitized value
     */
    private function sanitize_out_field(mixed $value, string $type = 'text'): mixed
    {
        if ('number' === $type) {
            return intval($value);
        }
        return $value;
    }
}
