<?php

/**
 * Abstract online solution settings sync.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings;

use Exception;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstracts\Hub_API\Main_API;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstracts\Hub_API\Settings_Sync_API;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings\Models\Abstract_Online_Solution_Settings_Model;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings\Sync\Online_Solution_Sync_Payment_Methods;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings\Sync\Online_Solution_Sync_Images;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Admin\Settings\Settings_Fields_Module;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Cron\Models\Lock_Model;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Cron_Module;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Sync\Bookkeeping_Sync_Module;
defined('ABSPATH') || exit;
/**
 * Abstract_Online_Solution_Settings_Sync class.
 */
abstract class Abstract_Online_Solution_Settings_Sync
{
    /**
     * Shared instance.
     *
     * @var Abstract_Shared_Base $shared
     */
    protected Abstract_Shared_Base $shared;
    /**
     * The option name for the sync enabled setting.
     *
     * @var string
     */
    private string $enabled_option_name;
    /**
     * The option name for the last-synced setting.
     *
     * @var string
     */
    private string $last_synced_option_name;
    /**
     * The API instance.
     *
     * @var Settings_Sync_API|null $api
     */
    public ?Settings_Sync_API $api = null;
    /**
     * Sync payment methods.
     *
     * @var Online_Solution_Sync_Payment_Methods
     */
    public Online_Solution_Sync_Payment_Methods $sync_payment_methods;
    /**
     * Syncer for WP data.
     *
     * @var Online_Solution_Sync_Images
     */
    public Online_Solution_Sync_Images $sync_images;
    /**
     * The sync lock name.
     *
     * @var string
     */
    private string $sync_lock_name;
    /**
     * Construct class.
     */
    public function __construct()
    {
        $api = WP_Repository::get_current_main_account()?->api;
        if (is_a($api, Main_API::class, \true)) {
            $this->api = new Settings_Sync_API($api);
        }
        $this->shared = Abstract_Shared_Base::base_instance();
        $this->enabled_option_name = 'nms_admin_sync_settings_enabled_' . $this->shared->package_info->get_service_name_underscored();
        $this->last_synced_option_name = 'nms_admin_sync_settings_latest_' . $this->shared->package_info->get_service_name_underscored();
        $this->sync_lock_name = 'nms_admin_sync_lock_' . $this->shared->package_info->get_service_name_underscored();
        $this->sync_payment_methods = new Online_Solution_Sync_Payment_Methods();
        $this->sync_images = new Online_Solution_Sync_Images();
    }
    /**
     * Get whether the sync is enabled.
     *
     * @return bool
     */
    public function is_sync_enabled(): bool
    {
        return (bool) get_option($this->enabled_option_name, \false);
    }
    /**
     * Update the enabled setting.
     *
     * @param bool $enabled Whether the sync should be enabled.
     *
     * @return bool
     */
    public function update_sync_enabled(bool $enabled): bool
    {
        $updated = update_option($this->enabled_option_name, $enabled);
        if ($updated) {
            $cron_event = $this->shared->cron->get_cron_event(Cron_Module::CRON_EVENT_SYNC_SETTINGS);
            $cron_event->set_is_enabled($enabled);
            return \true;
        }
        return \false;
    }
    /**
     * Get the last sync time.
     *
     * @return string
     */
    public function get_last_sync_time(): string
    {
        $timestamp = get_option($this->last_synced_option_name, null);
        if (!$timestamp) {
            return __('Never', 'northmill-online-shared');
        }
        return wp_date('Y-m-d H:i:s', $timestamp);
    }
    /**
     * Get the type of settings to sync.
     *
     * @return Settings_Type
     */
    abstract protected function get_settings_type(): Settings_Type;
    /**
     * Sync the online solution settings with Hub.
     *
     * @return bool
     */
    public function sync(): bool
    {
        $lock = new Lock_Model($this->sync_lock_name);
        if (!$lock->lock()) {
            $this->log('Cron lock in place, skipping cron', array('lock' => $lock->value), Log_Level_Enum::WARNING);
            return \false;
        }
        $api = WP_Repository::get_current_main_account()?->api;
        if (!$api) {
            $this->log('Failed to get api', array(), Log_Level_Enum::ERROR);
            $lock->clear_lock();
            return \false;
        }
        $settings_api = new Settings_Sync_API($api);
        $this->log('Sync settings with Hub');
        /**
         * Single branch switch statement to allow for future expansion.
         *
         * @noinspection PhpSwitchStatementWitSingleBranchInspection
         */
        switch ($this->get_settings_type()) {
            case Settings_Type::EXPRESS:
                $result = $settings_api->get_express_settings();
                break;
        }
        if (empty($result)) {
            $lock->clear_lock();
            return \false;
        }
        if (!is_null($result->Settings)) {
            try {
                $settings_model = $this->create_model_from_hub($result->Settings);
                $this->update_from_hub($settings_model);
            } catch (Exception $e) {
                $this->log('Failed to sync settings with Hub', array('message' => $e->getMessage(), 'trace' => $e->getTrace()), Log_Level_Enum::ERROR);
                $lock->clear_lock();
                return \false;
            }
        } else {
            $this->disable_online_solution();
        }
        $this->maybe_sync_bookkeeping_settings();
        update_option($this->last_synced_option_name, time());
        $lock->clear_lock();
        return \true;
    }
    /**
     * Sync bookkeeping settings if automatic sync isn't enabled for Northmill Sync.
     *
     * @return void
     */
    private function maybe_sync_bookkeeping_settings(): void
    {
        $shared_setting_up_hub = apply_filters('northmill_shared_instance_setting_up_hub_accounts', \false);
        /**
         * Instance of Shared that is setting up Hub accounts.
         *
         * @var Abstract_Shared_Base $shared_setting_up_hub
         */
        if ($shared_setting_up_hub) {
            if (!$shared_setting_up_hub->admin->settings->get_setting(Settings_Fields_Module::AUTO_SYNC, \false)) {
                $shared_setting_up_hub->sync->get_syncer(Bookkeeping_Sync_Module::SYNCER_KEY_IAC)->get_cron_syncer()->cron_sync();
                $shared_setting_up_hub->sync->get_syncer(Bookkeeping_Sync_Module::SYNCER_KEY_VAC)->get_cron_syncer()->cron_sync();
                $shared_setting_up_hub->sync->get_syncer(Bookkeeping_Sync_Module::SYNCER_KEY_OAC)->get_cron_syncer()->cron_sync();
                $shared_setting_up_hub->sync->get_syncer(Bookkeeping_Sync_Module::SYNCER_KEY_SS)->get_cron_syncer()->cron_sync();
            }
        }
    }
    /**
     * Create a model from the settings synced from Hub.
     *
     * @param object $hub_data Data synced from Hub.
     *
     * @return Abstract_Online_Solution_Settings_Model
     */
    abstract protected function create_model_from_hub(object $hub_data): Abstract_Online_Solution_Settings_Model;
    /**
     * Create a model from the settings in WP.
     *
     * @return Abstract_Online_Solution_Settings_Model
     */
    abstract protected function create_model_from_wp(): Abstract_Online_Solution_Settings_Model;
    /**
     * Update the settings synced from Hub.
     *
     * @param Abstract_Online_Solution_Settings_Model $settings_model Data synced from Hub.
     *
     * @return void
     */
    abstract protected function update_from_hub(Abstract_Online_Solution_Settings_Model $settings_model): void;
    /**
     * Disable the online solution.
     *
     * @return bool Whether the online solution was disabled.
     */
    abstract protected function disable_online_solution(): bool;
    /**
     * Get the valid payment methods for this online solution.
     *
     * @return array
     *
     * @used Northmill Express Order
     */
    abstract public static function get_valid_payment_methods(): array;
    /**
     * Get the value to look for in income account code property PaymentSourceArg1 when syncing from Hub API.
     *
     * @return string
     */
    abstract public function get_income_account_code_sync_api_payment_source_arg_1(): string;
    /**
     * Get the order number prefix for this online solution.
     *
     * @return string
     */
    abstract public function get_order_number_prefix(): string;
    /**
     * Get a multidimensional array of preview data for the settings.
     *
     * @return array
     */
    public function get_preview_data(): array
    {
        $model = $this->create_model_from_wp();
        return $this->generate_preview_data($model);
    }
    /**
     * Generate the multidimensional array of preview data for the settings.
     *
     * @param Abstract_Online_Solution_Settings_Model $model The model to generate the preview data from.
     *
     * @return array
     */
    abstract protected function generate_preview_data(Abstract_Online_Solution_Settings_Model $model): array;
    /**
     * Log a message.
     *
     * @param string         $message Message to log.
     * @param array          $context Context to log.
     * @param Log_Level_Enum $level   Log level.
     *
     * @return void
     */
    public function log(string $message, array $context = array(), Log_Level_Enum $level = Log_Level_Enum::DEBUG): void
    {
        $this->shared->logger->log('admin-settings-sync-page', $message, $context, $level);
    }
}
