<?php

/**
 * Sync module.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules;

use InvalidArgumentException;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Hub_Account_Setup;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Abstract_Syncer;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Ajax\Ajax_Syncers;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Cron\Cron_Syncers;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Helpers\Hook_Helpers;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Sync\Models\Sync_Collection_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Sync\Receipt_Sync_Module;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Sync\Bookkeeping_Sync_Module;
defined('ABSPATH') || exit;
/**
 * Sync module.
 */
class Sync_Module extends Abstract_Module
{
    /**
     * The receipts instance.
     *
     * @var Receipt_Sync_Module
     */
    public Receipt_Sync_Module $receipts;
    /**
     * The bookkeeping sync instance.
     *
     * @var Bookkeeping_Sync_Module
     */
    public Bookkeeping_Sync_Module $bookkeeping;
    /**
     * Array of syncers.
     *
     * @var Sync_Collection_Model[]
     */
    private array $syncers = array();
    /**
     * Constructor.
     *
     * @param Abstract_Shared_Base $shared Shared instance.
     */
    public function __construct(Abstract_Shared_Base $shared)
    {
        parent::__construct($shared);
        $this->receipts = new Receipt_Sync_Module($this->shared);
        $this->bookkeeping = new Bookkeeping_Sync_Module($this->shared);
    }
    /**
     * Sets up the receipts module if it is an instance of Abstract_Receipt_Sync_Module.
     *
     * @return void
     */
    public function setup(): void
    {
        $this->receipts->setup();
        $this->bookkeeping->setup();
        $this->setup_external_syncers();
    }
    /**
     * Sets up any external syncers.
     *
     * @return void
     */
    public function setup_external_syncers(): void
    {
        Hook_Helpers::do_scoped_action('northmill_online_shared_setup_external_syncers');
    }
    /**
     * Gets the syncer by key.
     *
     * @param string $key The syncer key.
     *
     * @return Sync_Collection_Model|null Returns the syncer if found, otherwise null.
     */
    public function get_syncer(string $key): ?Sync_Collection_Model
    {
        $syncers = $this->get_syncers();
        return $syncers[$key] ?? null;
    }
    /**
     * Gets the syncers.
     *
     * @return Sync_Collection_Model[] Returns the syncers.
     */
    public function get_syncers(): array
    {
        return $this->syncers ?? array();
    }
    /**
     * Sets up a syncer instance for both cron and AJAX synchronization systems based on the provided class.
     *
     * @param string $key          Identifier key for the syncer.
     * @param string $syncer_class Fully-qualified class name of the syncer, which must extend Abstract_Syncer.
     * @param int    $priority     Optional priority for the syncer within cron and manual sync. Default is 10.
     *
     * @return void
     * @throws InvalidArgumentException If the provided class doesn't exist or doesn't extend Abstract_Syncer.
     */
    public function setup_syncer(string $key, string $syncer_class, int $priority = 10): void
    {
        if (!class_exists($syncer_class) || !is_subclass_of($syncer_class, Abstract_Syncer::class)) {
            throw new InvalidArgumentException(sprintf('The provided syncer class "%s" is either not defined or does not extend Abstract_Syncer.', esc_html($syncer_class)));
        }
        $cron_syncer = new $syncer_class();
        $cron_sync_model = Cron_Syncers::add_syncer($key, $cron_syncer, $priority);
        $ajax_syncer = new $syncer_class(array(), 'ajax');
        $ajax_sync_model = Ajax_Syncers::add_syncer($key, $ajax_syncer, 'sync_' . $key, $priority);
        $this->syncers[$key] = new Sync_Collection_Model($cron_sync_model, $ajax_sync_model);
    }
}
