<?php

/**
 * Receipt_Helpers class.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Sync\Receipts;

use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Models\Hub_Account_Model;
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\Models\Receipts\Payment_Gateway_Collection;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Data\Payment_Methods;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Helpers\Hook_Helpers;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Abstract_Module;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Sync\Bookkeeping\Vat_Account_Codes\Vat_Accounts;
use WC_Payment_Gateway;
/**
 * Class Receipt_Helpers
 *
 * @noinspection PhpUnused To be extended by packages loading this composer package.
 */
class Receipt_Bookkeeping_Module extends Abstract_Module
{
    /**
     * Invalid payment gateway collections.
     *
     * @var Payment_Gateway_Collection[]
     */
    protected array $invalid_payment_gateway_collections = array();
    /**
     * Invalid VAT Hub accounts.
     *
     * @var array
     */
    protected array $invalid_vat_hub_accounts = array();
    /**
     * Get invalid payment gateway collections.
     *
     * @return Payment_Gateway_Collection[]
     *
     * @noinspection PhpUnused To be called by packages loading this composer package.
     */
    public function get_invalid_payment_gateway_collections(): array
    {
        return $this->invalid_payment_gateway_collections;
    }
    /**
     * Get invalid VAT Hub accounts.
     *
     * @return array
     *
     * @noinspection PhpUnused To be called by packages loading this composer package.
     */
    public function get_invalid_vat_hub_accounts(): array
    {
        return $this->invalid_vat_hub_accounts;
    }
    /**
     * Get Backoffice accounts.
     *
     * @return Hub_Account_Model[]
     */
    private function get_backoffice_accounts(): array
    {
        $backoffice_accounts = WP_Repository::get_all_current_accounts();
        // Return only the accounts that are instances of Hub_Account_Model.
        return array_filter($backoffice_accounts, function ($account) {
            return $account instanceof Hub_Account_Model;
        });
    }
    /**
     * Check if all enabled Checkout payment methods have an income account code set.
     *
     * @param string|null $payment_gateway Payment gateway ID. Can be supplied to only check this specific payment gateway.
     * @param bool        $log_errors      Whether to log errors or not.
     *
     * @return bool
     *
     * @noinspection PhpUnused To be called by packages loading this composer package.
     */
    public function is_required_income_account_codes_set(?string $payment_gateway = null, bool $log_errors = \true): bool
    {
        $payment_gateway_collections = $this->get_payment_methods_creating_hub_receipts();
        $accounts_to_check = $this->get_hub_accounts_to_check_income_accounts_for();
        foreach ($payment_gateway_collections as $payment_gateway_collection) {
            $payment_method = $payment_gateway_collection->payment_gateway;
            if ($payment_gateway && $payment_gateway !== $payment_method->id) {
                continue;
            }
            foreach ($accounts_to_check as $hub_account_data) {
                if (!$this->get_income_account_code_from_hub_account($payment_method->id, $hub_account_data)) {
                    Abstract_Shared_Base::base_instance()->logger->log('checkout', 'Missing income account code for payment method.', array('payment_method' => $payment_method->id, 'hub_account' => $this->get_hub_account_data_for_log($hub_account_data)), $log_errors ? Log_Level_Enum::ERROR : Log_Level_Enum::DEBUG);
                    $this->update_invalid_payment_gateway_collections($hub_account_data, $payment_gateway_collection);
                }
            }
        }
        return empty($this->invalid_payment_gateway_collections);
    }
    /**
     * Abstract method to get accounts to check.
     *
     * @return array
     */
    protected function get_hub_accounts_to_check_income_accounts_for(): array
    {
        return $this->get_backoffice_accounts();
    }
    /**
     * Abstract method to get income account code from Hub account.
     *
     * @param string $payment_method_id Payment method ID.
     * @param mixed  $hub_account_data  Hub account data. This can be empty if there is only one account to check.
     *
     * @return int|null
     */
    public function get_income_account_code_from_hub_account(string $payment_method_id, mixed $hub_account_data): ?int
    {
        if (!$hub_account_data instanceof Hub_Account_Model) {
            return null;
        }
        $account_codes = $hub_account_data->get_income_account_codes();
        $account_code = $account_codes[$payment_method_id] ?? null;
        return !is_null($account_code) ? $account_codes[$payment_method_id]->get_account_code() : null;
    }
    /**
     * Update invalid payment gateway collections.
     *
     * @param mixed                      $hub_account_data            Hub account data.
     * @param Payment_Gateway_Collection $collection                  Payment gateway collection.
     *
     * @return void
     */
    protected function update_invalid_payment_gateway_collections(mixed $hub_account_data, Payment_Gateway_Collection $collection): void
    {
        $invalid_payment_gateway_collection = clone $collection;
        if ($hub_account_data instanceof Hub_Account_Model) {
            $invalid_payment_gateway_collection->backoffice_account = $hub_account_data;
        }
        $this->invalid_payment_gateway_collections[] = $invalid_payment_gateway_collection;
    }
    /**
     * Abstract method to get Hub account data for log.
     *
     * @param mixed $hub_account_data Hub account data.
     *
     * @return array|null
     */
    protected function get_hub_account_data_for_log(mixed $hub_account_data): ?array
    {
        if (!$hub_account_data instanceof Hub_Account_Model) {
            return null;
        }
        return $hub_account_data->get_account_basics_for_logging();
    }
    /**
     * Check if all VAT account codes are set.
     * TODO: This could be updated to check which VAT accounts are enabled in Hub and only check those?
     *
     * @param bool $log_errors Whether to log errors or not.
     *
     * @return bool True if all VAT account codes are set, false otherwise.
     *
     * @noinspection PhpUnused To be called by packages loading this composer package.
     */
    public function is_required_vat_account_codes_set(bool $log_errors = \true): bool
    {
        $hub_accounts = $this->get_hub_accounts_to_check_vat_accounts_for();
        foreach ($hub_accounts as $hub_account_data) {
            $vat_account_codes = $this->get_vat_accounts_from_hub_account($hub_account_data);
            if (empty($vat_account_codes[0]) || empty($vat_account_codes[6]) || empty($vat_account_codes[12]) || empty($vat_account_codes[25])) {
                Abstract_Shared_Base::base_instance()->logger->log('checkout', 'Missing VAT account.', array('vat_account_codes' => $vat_account_codes, 'hub_account' => $this->get_hub_account_data_for_log($hub_account_data)), $log_errors ? Log_Level_Enum::ERROR : Log_Level_Enum::DEBUG);
                $this->update_invalid_vat_hub_accounts($hub_account_data);
            }
        }
        return empty($this->invalid_vat_hub_accounts);
    }
    /**
     * Abstract method to get accounts to check.
     *
     * @return array
     */
    protected function get_hub_accounts_to_check_vat_accounts_for(): array
    {
        return $this->get_backoffice_accounts();
    }
    /**
     * Abstract method to get VAT accounts from Hub account.
     *
     * @param mixed $hub_account_data Hub account data.
     *
     * @return  int[]|null[]
     */
    protected function get_vat_accounts_from_hub_account(mixed $hub_account_data): array
    {
        if (!$hub_account_data instanceof Hub_Account_Model) {
            return array();
        }
        return Vat_Accounts::get_default_account_codes($hub_account_data);
    }
    /**
     * Update array Hub accounts with invalid VAT data.
     *
     * @param mixed $hub_account_data Hub account data.
     *
     * @return void
     */
    protected function update_invalid_vat_hub_accounts(mixed $hub_account_data): void
    {
        $this->invalid_vat_hub_accounts[] = $hub_account_data;
    }
    /**
     * Get all payment methods that can be used in the Hub to create Receipts.
     *
     * @param bool $only_available Whether to only return payment methods that are available in WooCommerce.
     *
     * @return Payment_Gateway_Collection[]
     */
    public function get_payment_methods_creating_hub_receipts(bool $only_available = \true): array
    {
        /**
         * Filter to add/remove payment methods that can be used in the Hub to create Receipts.
         *
         * @param array $payment_method_map Payment methods that can be used in the Hub to create Receipts.
         *                                  The KEYS in the MAIN ARRAY should NOT be changed by the filter.
         *                                        They correspond to Income account codes in Hub.
         *                                        PaymentType as received from API.
         *                                  The filter can change the VALUES in the SUB ARRAYS.
         *                                        They correspond to payment gateway IDs in WooCommerce.
         */
        $payment_method_map = Hook_Helpers::apply_scoped_filters('northmill_online_shared_receipt_payment_gateway_map', Payment_Methods::get_iac_payment_method_map());
        $flattened_receipt_gateways = array();
        foreach ($payment_method_map as $key => $values) {
            foreach ($values as $value) {
                $flattened_receipt_gateways[$value] = $key;
            }
        }
        /**
         * Magic method okay for WC().
         *
         * @noinspection PhpUndefinedFieldInspection
         */
        $payment_gateways = $only_available ? WC()->payment_gateways->get_available_payment_gateways() : WC()->payment_gateways->payment_gateways();
        $payment_gateway_collections = array();
        foreach ($payment_gateways as $payment_gateway) {
            $payment_gateway_collections[] = $this->create_payment_gateway_collection($payment_gateway, $flattened_receipt_gateways);
        }
        return $payment_gateway_collections;
    }
    /**
     * Create a Payment_Gateway_Collection object.
     *
     * @param WC_Payment_Gateway $payment_gateway            Payment gateway object.
     * @param array              $flattened_receipt_gateways Flattened array of payment gateway IDs and their corresponding PaymentType.
     *
     * @return Payment_Gateway_Collection
     */
    protected function create_payment_gateway_collection(WC_Payment_Gateway $payment_gateway, array $flattened_receipt_gateways): Payment_Gateway_Collection
    {
        return new Payment_Gateway_Collection($payment_gateway, $flattened_receipt_gateways[$payment_gateway->id] ?? null);
    }
}
