<?php

/**
 * Sync account codes from Hub main account to WooCommerce.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Sync\Bookkeeping\Article_Account_Codes;

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\Models\Article_Account_Code_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository_Metas;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Sync\Abstract_Syncer;
use WC_Tax;
defined('ABSPATH') || exit;
/**
 * Class Sync for account codes.
 */
class Article_Account_Code_Sync extends Abstract_Syncer
{
    /**
     * Page size will not be changed by ajax (or any other) request.
     *
     * @var bool $force_page_size
     */
    protected bool $force_page_size = \true;
    /**
     * Sync request page size.
     *
     * @var int $page_size
     */
    protected int $page_size = 1000;
    /**
     * Construct class.
     *
     * @param array  $backoffice_accounts Array of Hub accounts to sync stock for.
     * @param string $type                Type of syncer. Can be 'cron' or 'ajax'.
     */
    public function __construct(array $backoffice_accounts = array(), string $type = 'cron')
    {
        if (empty($backoffice_accounts)) {
            $backoffice_accounts = WP_Repository::get_all_current_accounts();
        }
        parent::__construct($backoffice_accounts, $type);
    }
    /**
     * Sync account codes from Hub WooCommerce.
     *
     * @param array|null $args API arguments.
     * @param bool       $is_first_sync If this sync is the first page of the first Hub account.
     */
    public function sync(?array $args = null, bool $is_first_sync = \false): bool
    {
        $current_backoffice_account = $this->get_current_hub_account();
        if (!$current_backoffice_account) {
            return \false;
        }
        $data = $current_backoffice_account->api->get_account_codes($args);
        if (!$data || !isset($data->list) || !is_array($data->list)) {
            return \false;
        }
        $this->update_status('page', $data->Page);
        $this->update_status('total', $data->Total);
        $nmf_tax_classes = Factory::create_array_from_api($data->list);
        $this->update_status('update', count($nmf_tax_classes));
        WP_Repository_Metas::update_single($current_backoffice_account->wp_id, 'article_account_codes', $nmf_tax_classes);
        if ($current_backoffice_account->is_main() && Envs::get_current_env() === $current_backoffice_account->env) {
            $this->update_wc_tax_rates($nmf_tax_classes);
        }
        return \true;
    }
    /**
     * Update WooCommerce tax classes and tax rates from API response.
     * These should be updated only from the main account.
     * Used for article/order taxes. NOT for bookkeeping account codes.
     *
     * @param Article_Account_Code_Model[] $nmf_tax_classes The account codes.
     */
    private function update_wc_tax_rates(array $nmf_tax_classes): void
    {
        $current_account = $this->get_current_hub_account();
        $is_take_away_enabled = (bool) WP_Repository_Metas::get_single($current_account->wp_id, 'is_take_away_enabled');
        $existing_tax_clas_slugs = WC_Tax::get_tax_class_slugs();
        $synced_tax_class_slugs = array();
        if (!empty($nmf_tax_classes)) {
            foreach ($nmf_tax_classes as $nmf_tax_class) {
                $this->store_wc_tax_class($nmf_tax_class, $existing_tax_clas_slugs, $synced_tax_class_slugs, $is_take_away_enabled);
            }
        }
        $this->clean_tax_classes($existing_tax_clas_slugs, $synced_tax_class_slugs);
    }
    /**
     * Store tax class in WC core storage.
     *
     * @param Article_Account_Code_Model $nmf_tax_class            Northmill model for account code.
     * @param string[]                   $existing_tax_class_slugs Slugs of tax classes stored in WC.
     * @param string[]                   $synced_tax_class_slugs   Slugs of newly synced tax classes. Passed by reference.
     * @param bool                       $is_take_away_enabled     Whether take-away feature is enabled.
     *
     * @return void
     */
    private function store_wc_tax_class(Article_Account_Code_Model $nmf_tax_class, array $existing_tax_class_slugs, array &$synced_tax_class_slugs, bool $is_take_away_enabled): void
    {
        $synced_tax_class_slugs[] = $nmf_tax_class->get_slug();
        $is_class_new = \false;
        if (!in_array($nmf_tax_class->get_slug(), $existing_tax_class_slugs, \true)) {
            // Creating new tax class.
            $is_class_new = \true;
            WC_Tax::create_tax_class($nmf_tax_class->get_name());
        }
        $this->store_tax_rate($nmf_tax_class, $is_class_new);
        if ($is_take_away_enabled && $nmf_tax_class->get_take_away_vat_percentage() !== null) {
            $ta_name = $nmf_tax_class->get_name() . ' - Take Away';
            $ta_slug = sanitize_title($ta_name);
            $synced_tax_class_slugs[] = $ta_slug;
            $is_ta_new = !in_array($ta_slug, $existing_tax_class_slugs, \true);
            if ($is_ta_new) {
                WC_Tax::create_tax_class($ta_name);
            }
            $this->upsert_wc_tax_rate(array('tax_rate_country' => '*', 'tax_rate_state' => '*', 'tax_rate' => absint($nmf_tax_class->get_take_away_vat_percentage()), 'tax_rate_name' => $nmf_tax_class->get_description() . ' - Take Away', 'tax_rate_priority' => 1, 'tax_rate_compound' => 0, 'tax_rate_shipping' => 1, 'tax_rate_order' => 0, 'tax_rate_class' => $ta_slug), $ta_slug, $is_ta_new);
        }
    }
    /**
     * Store WC tax rate.
     *
     * @param Article_Account_Code_Model $nmf_tax_class Northmill model for account code.
     * @param bool                       $is_class_new  Whether storing a tax rate for a newly created tax class.
     *
     * @return void
     */
    private function store_tax_rate(Article_Account_Code_Model $nmf_tax_class, bool $is_class_new): void
    {
        $tax_rate_data = array('tax_rate_country' => '*', 'tax_rate_state' => '*', 'tax_rate' => absint($nmf_tax_class->get_vat_rate()), 'tax_rate_name' => $nmf_tax_class->get_description(), 'tax_rate_priority' => 1, 'tax_rate_compound' => 0, 'tax_rate_shipping' => 1, 'tax_rate_order' => 0, 'tax_rate_class' => $nmf_tax_class->get_slug());
        $this->upsert_wc_tax_rate($tax_rate_data, $nmf_tax_class->get_slug(), $is_class_new);
    }
    /**
     * Insert or update a WC tax rate.
     *
     * @param array  $tax_rate_data Tax rate data array for WC_Tax.
     * @param string $slug          Tax class slug.
     * @param bool   $is_class_new  Whether this is a newly created tax class.
     */
    private function upsert_wc_tax_rate(array $tax_rate_data, string $slug, bool $is_class_new): void
    {
        $tax_rates = array();
        if (!$is_class_new) {
            $tax_rates = WC_Tax::get_rates_for_tax_class($slug);
        }
        if (empty($tax_rates)) {
            $tax_rate_id = WC_Tax::_insert_tax_rate($tax_rate_data);
            WC_Tax::_update_tax_rate_postcodes($tax_rate_id, wc_clean('*'));
            WC_Tax::_update_tax_rate_cities($tax_rate_id, wc_clean('*'));
        } else {
            foreach ($tax_rates as $tax_rate_id => $tax_rate) {
                if (floatval($tax_rate_data['tax_rate']) !== floatval($tax_rate->tax_rate) || $tax_rate_data['tax_rate_name'] !== $tax_rate->tax_rate_name) {
                    WC_Tax::_update_tax_rate($tax_rate_id, $tax_rate_data);
                }
            }
        }
    }
    /**
     * Delete tax classes from WC that has been removed in Hub.
     *
     * @param string[] $existing_tax_classes Slugs of tax classes in WC.
     * @param string[] $synced_tax_classes   Slugs of newly synced tax classes.
     *
     * @return void
     */
    private function clean_tax_classes(array $existing_tax_classes, array $synced_tax_classes): void
    {
        $classes_to_remove = array_diff($existing_tax_classes, $synced_tax_classes);
        foreach ($classes_to_remove as $tax_rate_class_slug) {
            if (!str_starts_with($tax_rate_class_slug, Article_Account_Codes::get_tax_class_slug_prefix())) {
                continue;
            }
            WC_Tax::delete_tax_class_by('slug', $tax_rate_class_slug);
        }
    }
    /**
     * {@inheritDoc}
     */
    public function get_display_name(): string
    {
        return __('Article Account Codes', 'northmill-online-shared');
    }
    /**
     * {@inheritDoc}
     */
    public function get_last_synced_wp_option_name(): string
    {
        return 'nmf_sync_article_account_codes_last_updated';
    }
    /**
     * {@inheritDoc}
     */
    public function get_last_synced_api_parameter(): ?string
    {
        return null;
    }
}
