<?php
/**
 * Syncer class for income account codes.
 *
 * @package northmill-online-shared
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Modules\Sync\Bookkeeping\Income_Account_Codes;

use Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Models\Income_Account_Code_Model;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository_Metas;
use Northmill_Online_Shared\Abstracts\Hub_API\Sync\Abstract_Syncer;
use Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;

defined( 'ABSPATH' ) || exit;

/**
 * Syncer class for income account codes.
 */
class Income_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;

	/**
	 * PaymentSource for account codes to get for this sync.
	 *
	 * @var string $api_payment_source
	 */
	private static string $api_payment_source = 'MoreFloOnlineSolution';

	/**
	 * PaymentSourceArg1 for account codes to get for this sync.
	 *
	 * @var string $api_payment_source_arg_1
	 */
	private static string $api_payment_source_arg_1 = 'webshop';

	/**
	 * 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();
		}
		parent::__construct( $backoffice_accounts, $type );
	}

	/**
	 * Sync income account codes.
	 *
	 * @param array|null $args Arguments to pass to API, or null if no 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;
		}

		$result_object = $current_backoffice_account->api->get_income_account_codes( $args );

		if ( ! isset( $result_object->list[0] ) ) {
			Abstract_Shared_Base::base_instance()->logger->log(
				'cron',
				'No income account codes found',
				array(
					'backoffice_account' => $current_backoffice_account->get_account_basics_for_logging(),
					'result'             => $result_object,
				),
				Log_Level_Enum::NOTICE
			);

			return true; // This isn't necessarily an error, so we return true.
		}

		$this->update_status( 'page', $result_object->Page );
		$this->update_status( 'total', $result_object->Total );

		$income_account_codes = $result_object->list;

		/**
		 * Filter the API Payment Source to look for.
		 * Should return a string.
		 */
		$api_payment_source = apply_filters( 'nmf_sync_income_account_codes_api_payment_source', self::$api_payment_source );

		/**
		 * Whether to also check include income account codes received for PaymentSource "General"
		 * Should return a boolean.
		 */
		$include_general_payment_source = apply_filters(
			'nmf_sync_income_account_codes_api_payment_source_include_general',
			true
		);

		/**
		 * Filter the API Payment Source Arg 1 to look for.
		 * Should return a string.
		 */
		$api_payment_source_arg_1 = apply_filters( 'nmf_sync_income_account_codes_api_payment_source_arg_1', self::$api_payment_source_arg_1 );

		$receipts_helpers            = Abstract_Shared_Base::base_instance()->sync->receipts->bookkeeping;
		$payment_gateway_collections = $receipts_helpers->get_payment_methods_creating_hub_receipts( false );

		// Reset counters.
		$match_count = 0;

		// Step 1: Create an associative array with 'iac_pay_type' as the key and an array of payment methods as the value.
		$method_map = array();
		foreach ( $payment_gateway_collections as $payment_gateway_collection ) {
			$method_map[ $payment_gateway_collection->iac_pay_type ][] = $payment_gateway_collection;
		}

		// Step 2: Filter income account codes based on 'Payment source' and 'Payment argument'.
		$initial_count          = count( $income_account_codes );
		$filtered_account_codes = array_filter(
			$income_account_codes,
			function ( $account_code ) use ( $api_payment_source, $api_payment_source_arg_1, $include_general_payment_source ) {
				return (
					(
						(
							$account_code->IncomeAccountSetting->PaymentSource === $api_payment_source &&
							$account_code->IncomeAccountSetting->PaymentSourceArg1 === $api_payment_source_arg_1
						) ||
						(
							$include_general_payment_source &&
							'General' === $account_code->IncomeAccountSetting->PaymentSource
						)
					)
				);
			}
		);

		// Step 3: Find matching income account codes by 'Payment Type'.
		$matching_account_codes   = array();
		$updated_payment_gateways = array();
		foreach ( $filtered_account_codes as $account_code ) {
			if ( array_key_exists( $account_code->IncomeAccountSetting->PaymentType, $method_map ) ) {
				foreach ( $method_map[ $account_code->IncomeAccountSetting->PaymentType ] as $payment_gateway_collection ) {
					if (
						'General' === $account_code->IncomeAccountSetting->PaymentSource &&
						in_array( $payment_gateway_collection->payment_gateway->id, $updated_payment_gateways, true )
					) {
						continue;
					}

					// Step 4: Add to the result if there is a match.
					$matching_account_codes[ $payment_gateway_collection->payment_gateway->id ] = new Income_Account_Code_Model(
						array(
							'id'                => $account_code->IncomeAccountSetting->Id,
							'payment_method_id' => $payment_gateway_collection->payment_gateway->id,
							'account_code'      => $account_code->IncomeAccountSetting->AccountNumber,
						)
					);

					++$match_count;
					$updated_payment_gateways[] = $payment_gateway_collection->payment_gateway->id;
				}
			}
		}

		$filtered_out_count = $initial_count - $match_count;

		$this->update_status( 'update', $match_count );
		$this->update_status( 'ignore', $filtered_out_count );

		WP_Repository_Metas::update_single(
			$current_backoffice_account->wp_id,
			'income_account_codes',
			$matching_account_codes
		);

		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_display_name(): string {
		return __( 'Income Account Codes', 'northmill-online-shared' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_last_synced_wp_option_name(): string {
		return 'mf_sync_income_account_codes_last_updated';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_last_synced_api_parameter(): ?string {
		return null;
	}
}
