<?php
/**
 * Api.php
 *
 * @author     alfiopiccione <alfio.piccione@gmail.com>
 * @license    http://opensource.org/licenses/gpl-2.0.php GPL v2
 *
 * Copyright (C) 2025 POP
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

namespace WooPoPToFattureInCloudStock\Functions;

use FattureInCloud\ApiException;
use FattureInCloud\Model\ModifyProductRequest;
use WooPoPToFattureInCloudStock\TimeZone;
use function WooPoPToFattureInCloud\Functions\getActiveApi;
use function WooPoPToFattureInCloud\Functions\getMaskedApi;
use function WooPoPToFattureInCloud\Functions\refreshOAuthToken;

/**
 * fattureInCloudProductsApi
 *
 * @return \FattureInCloud\Api\ProductsApi
 */
function fattureInCloudProductsApi()
{
    $apiData = \WooPoPToFattureInCloud\Functions\getActiveApi();
    $config  = \FattureInCloud\Configuration::getDefaultConfiguration()->setAccessToken($apiData->accessToken);

    // User info
    return new \FattureInCloud\Api\ProductsApi(
        new \GuzzleHttp\Client(),
        $config
    );
}

/**
 * fattureInCloudGetListProducts
 *
 * @param $page
 * @param $action
 *
 * @return false|object|void
 */
function fattureInCloudGetListProducts($page = 1, $action = '')
{
    $api = getActiveApi();
    if (! $api->active && ! $api->expireAccessToken) {
        return false;
    }

    // auto refreshOAuthToken
    if (function_exists('\\WooPoPToFattureInCloud\\Functions\\getActiveApi')) {
        if (($api->expireAccessToken || $api->expireAccessToken < time())) {
            $_POST['fcRefreshOAuthToken'] = true;

            refreshOAuthToken();

            // Log
            \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
        }
    }

    $userAndCompanies          = \WooPoPToFattureInCloud\Functions\getUserAndCompanies();
    $fattureInCloudProductsApi = fattureInCloudProductsApi();

    try {
        $lists = $fattureInCloudProductsApi->listProducts((int)$userAndCompanies->companyID,
            'id,name',
            'detailed',
            null,
            $page,
            50,
            null,
            $fattureInCloudProductsApi::contentTypes['listProducts'][0]
        );

        if ($lists->valid()) {
            return (object)array(
                'data'         => $lists->getData(),
                'ApiException' => false,
            );
        } else {
            return (object)array(
                'data'         => null,
                'message'      => json_encode($lists->listInvalidProperties()),
                'ApiException' => false,
                'code'         => 422,
            );
        }
    } catch (ApiException $e) {
        $json    = json_decode($e->getResponseBody(), true);
        $message = $e->getResponseBody();
        if (! empty($json['error']) && ! empty($json['error']['message'])) {
            $message = $json['error']['message'];
        }

        if (401 === $e->getCode()) {
            $dataException = array(
                'message'      => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'ApiException' => true,
                'e-message'    => $e->getMessage(),
                'code'         => $e->getCode(),
            );
        } else {
            $dataException = array(
                'title'        => __('Oops, something is wrong!', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'type'         => __('listProducts ApiException Message:', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'message'      => $message,
                'code'         => $e->getCode(),
                'trace'        => $e->getTraceAsString(),
                'e-body'       => $e->getResponseBody(),
                'ApiException' => true,
            );
        }

        if ('get' === $action) {
            return (object)array(
                'message'      => $message,
                'ApiException' => true,
                'code'         => $e->getCode(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * fattureInCloudGetProduct
 *
 * @param $productID
 * @param $action
 *
 * @return false|object|void
 */
function fattureInCloudGetProduct($productID, $action = '')
{
    $api = getActiveApi();
    if (! $api->active && ! $api->expireAccessToken) {
        return false;
    }

    // auto refreshOAuthToken
    if (function_exists('\\WooPoPToFattureInCloud\\Functions\\getActiveApi')) {
        if (($api->expireAccessToken || $api->expireAccessToken < time())) {
            $_POST['fcRefreshOAuthToken'] = true;

            refreshOAuthToken();

            // Log
            \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
        }
    }

    $userAndCompanies          = \WooPoPToFattureInCloud\Functions\getUserAndCompanies();
    $fattureInCloudProductsApi = fattureInCloudProductsApi();

    try {
        $product = $fattureInCloudProductsApi->getProduct((int)$userAndCompanies->companyID,
            $productID,
            '',
            'detailed',
            $fattureInCloudProductsApi::contentTypes['getProduct'][0]
        );

        if ($product->valid()) {
            return (object)array(
                'data'         => $product->getData(),
                'ApiException' => false,
            );
        } else {
            return (object)array(
                'data'         => null,
                'message'      => json_encode($product->listInvalidProperties()),
                'ApiException' => false,
                'code'         => 422,
            );
        }
    } catch (ApiException $e) {
        $json    = json_decode($e->getResponseBody(), true);
        $message = $e->getResponseBody();
        if (! empty($json['error']) && ! empty($json['error']['message'])) {
            $message = $json['error']['message'];
        }

        if (401 === $e->getCode()) {
            $dataException = array(
                'message'      => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'ApiException' => true,
                'e-message'    => $e->getMessage(),
                'code'         => $e->getCode(),
            );
        } else {

            $dataException = array(
                'title'        => __('Oops, something is wrong!', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'type'         => __('modifyProduct ApiException Message:', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'message'      => $message,
                'ApiException' => true,
                'code'         => $e->getCode(),
                'e-body'       => $e->getResponseBody(),
            );
        }

        if ('get' === $action) {
            return (object)array(
                'message'      => $message,
                'ApiException' => true,
                'code'         => $e->getCode(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * fattureInCloudModifyProduct
 *
 * @param $productID
 * @param $args
 * @param $action
 *
 * @return false|object|void
 */
function fattureInCloudModifyProduct($productID, $args, $action = '')
{
    $api = getActiveApi();

    if (! $api->active && ! $api->expireAccessToken) {
        return false;
    }

    // auto refreshOAuthToken
    if (function_exists('\\WooPoPToFattureInCloud\\Functions\\getActiveApi')) {
        if (($api->expireAccessToken || $api->expireAccessToken < time())) {
            $_POST['fcRefreshOAuthToken'] = true;

            refreshOAuthToken();

            // Log
            \WooPoPToFattureInCloud\Functions\log('refreshOAuthToken: ' . print_r(getMaskedApi(getActiveApi()), true));
        }
    }

    $userAndCompanies          = \WooPoPToFattureInCloud\Functions\getUserAndCompanies();
    $fattureInCloudProductsApi = fattureInCloudProductsApi();

    try {
        /**
         * Filter args
         */
        $args    = wp_parse_args($args, apply_filters('wfc_ms-fic_modify_product_args', array()));
        $product = $fattureInCloudProductsApi->modifyProduct((int)$userAndCompanies->companyID,
            $productID,
            new ModifyProductRequest($args),
            $fattureInCloudProductsApi::contentTypes['modifyProduct'][0]
        );

        if ($product->valid()) {
            return (object)array(
                'data'         => $product->getData(),
                'valid'        => $product->valid(),
                'ApiException' => false,
                'code'         => 200,
            );
        } else {
            return (object)array(
                'data'         => null,
                'valid'        => false,
                'message'      => json_encode($product->listInvalidProperties()),
                'ApiException' => false,
                'code'         => 422,
            );
        }
    } catch (ApiException $e) {
        $json    = json_decode($e->getResponseBody(), true);
        $message = $e->getResponseBody();
        if (! empty($json['error']) && ! empty($json['error']['message'])) {
            $message = $json['error']['message'];
        }

        if (401 === $e->getCode()) {
            $dataException = array(
                'message'      => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'ApiException' => true,
                'e-message'    => $e->getMessage(),
                'code'         => $e->getCode(),
            );
        } else {
            $dataException = array(
                'title'        => __('Oops, something is wrong!', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'type'         => __('modifyProduct ApiException Message:', WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'message'      => $message,
                'ApiException' => true,
                'body'         => $e->getResponseBody(),
                'trace'        => $e->getTraceAsString(),
                'code'         => $e->getCode(),
            );
        }

        if ('get' === $action) {
            return (object)array(
                'message'      => $message,
                'ApiException' => true,
                'body'         => $e->getResponseBody(),
                'trace'        => $e->getTraceAsString(),
                'code'         => $e->getCode(),
            );
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * updateProductsList - WooCommerce provider only
 *
 * Update FIC product list and update stock the WC product mapped
 *
 * 1. update product list
 * 2. update wc stock for product
 *
 * @hooked wp_ajax_updateProductsList
 *
 * @return array|false|mixed|void
 */
function updateProductsList($return = false, $page = null)
{
    $products              = get_option('wfc_ms_products_list') ?: array();
    $productCodes          = get_option('wfc_ms_product_codes_list') ?: array();
    $productCategoryByID   = get_option('wfc_ms_product_category_by_id_list') ?: array();
    $productCategoryByCod  = get_option('wfc_ms_product_category_by_cod_list') ?: array();
    $messageData           = array();
    $currentLastPageOption = get_option('wfc_ms_products_list_last_page') ?? 1;

    $lastPage            = $page;
    $apiException        = false;
    $apiExceptionMessage = '';
    $processedPages      = 1;

    try {
        $productsList = fattureInCloudGetListProducts($lastPage);
        // Add number page processed to counts
        $counts = is_array($productsList->data) ? count($productsList->data) + (int)$currentLastPageOption : 0;

        if ($counts > 0) {
            for ($i = $lastPage; $i <= $counts; $i++) {
                $obj = fattureInCloudGetListProducts($i + 1, 'get');
                if ($obj && $obj->ApiException) {
                    $apiException        = true;
                    $apiExceptionMessage = $obj->message;
                    break;
                }

                if (is_array($obj->data) && ! empty($obj->data) && count($obj->data) > 0) {
                    $productsList->data = array_merge($productsList->data, $obj->data);
                    $processedPages     = $i;
                    sleep(1);
                } else {
                    $lastPage = $i;
                    break;
                }
            }
        }

        $countTotals = is_array($productsList->data) && ! empty($productsList->data) ? count($productsList->data) : 0;
        if ($countTotals > 0) {
            foreach ($productsList->data as $product) {
                if ($product instanceof \FattureInCloud\Model\Product) {
                    $products[$product->getId()]       = $product->getName();
                    $productCodes[$product->getCode()] = $product->getId();
                    // Product category
                    $productCategoryByID[$product->getId()]    = $product->getCategory();
                    $productCategoryByCod[$product->getCode()] = $product->getCategory();

                    $messageData[] = array(
                        'id'            => $product->getId(),
                        'code'          => $product->getCode(),
                        'name'          => $product->getName(),
                        'category'      => $product->getCategory(),
                        'initial_stock' => $product->getStockInitial(),
                        'current_stock' => $product->getStockCurrent(),
                    );

                    // Sync product stock
                    $refProd = referenceProductExist($product->getId());
                    if (is_object($refProd)) {
                        $exist    = $refProd->exist;
                        $wcProdID = $refProd->postID;

                        if ($exist && $wcProdID) {
                            if (! empty($product) && $product instanceof \FattureInCloud\Model\Product) {
                                // Get WC product
                                $wcProduct = wc_get_product($wcProdID);
                                if ($wcProduct) {
                                    // Set stock status
                                    $inStock  = $product->getInStock() ? 'instock' : 'outofstock';
                                    $stockQty = apply_filters('wfc_ms-update_product_stock',
                                        $product->getStockCurrent(),
                                        $wcProduct->get_id());
                                    $wcProduct->set_stock_quantity($stockQty);
                                    $wcProduct->set_stock_status($inStock);
                                    $wcProduct->save();
                                }
                            }
                        }
                    }
                }
            }

            // Store products list id and codes
            update_option('wfc_ms_products_list', $products);
            update_option('wfc_ms_product_codes_list', $productCodes);
            // Store products list category
            update_option('wfc_ms_product_category_by_id_list', $productCategoryByID);
            update_option('wfc_ms_product_category_by_cod_list', $productCategoryByCod);
            // Store last page
            update_option('wfc_ms_products_list_last_page', $lastPage);
        }

        if ($products && ! $return) {
            $timeZone = new TimeZone();
            $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());

            // Set expired date time
            $dateTime = new \DateTime();
            $dateTime->setTimezone($timeZone);
            $lastUpdateDate = $dateTime->format('d-m-Y H:i');
            update_option('wfc_ms_products_last_update_time', $lastUpdateDate);

            $response = array(
                'body'            => json_encode($messageData),
                'lastPage'        => $lastPage,
                'processedCounts' => $counts,
                'productsCounts'  => count($products),
                'products'        => $products,
                'lastUpdateDate'  => $lastUpdateDate,
                'processedPages'  => $processedPages,
                'message'         => ! $apiException ?
                    __('Products downloaded successfully', WP_FATT_CLOUD_STOCK_TEXTDOMAIN) :
                    __('The products were not fully downloaded - error message: ',
                        WP_FATT_CLOUD_STOCK_TEXTDOMAIN) . $apiExceptionMessage,
                'code'            => ! $apiException ? 200 : 500,
            );

            // Log
            \WooPoPToFattureInCloud\Functions\log(print_r($response, true));

            wp_send_json($response);
            die();
        }

        return $products;
    } catch (\Exception $e) {
        if (401 === $e->getCode()) {
            $dataException = array(
                'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                    WP_FATT_CLOUD_STOCK_TEXTDOMAIN),
                'e-message' => $e->getMessage(),
                'code'      => $e->getCode(),
            );
        } else {

            $traceString   = ' Trace: ' . $e->getTraceAsString();
            $dataException = array(
                'message' => 'dev' === WP_FATT_CLOUD ?
                    esc_html('Exception when calling IssuedDocumentsApi: ' . $e->getMessage() . $traceString) :
                    esc_html('Exception when calling IssuedDocumentsApi'),
                'body'    => $e->getMessage(),
                'trace'   => $traceString,
                'code'    => $e->getCode(),
            );

            if ('dev' !== WP_FATT_CLOUD) {
                $dataException['e-message'] = $e->getMessage();
            }
        }

        // Log
        \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true));

        wp_send_json($dataException);
        die();
    }
}

/**
 * updateProductStock - WooCommerce provider only
 *
 * Sync of the stock with that of FIC
 *
 * @hooked wfc-create_invoice
 *
 * @param $issuedDocumentInv
 * @param $body
 *
 * @return void
 */
function updateProductStock($issuedDocumentInv, $body)
{
    if ($issuedDocumentInv instanceof \FattureInCloud\Model\CreateIssuedDocumentResponse) {
        if ($issuedDocumentInv->valid()) {
            $orderID = \WooPoPToFattureInCloud\Functions\filterInput($_POST, 'orderID', FILTER_UNSAFE_RAW);
            $order   = wc_get_order($orderID);
            if (is_object($order)) {
                $items = $order->get_items();
                if (! empty($items)) {
                    foreach ($items as $item) {
                        if ($item instanceof \WC_Order_Item_Product) {
                            $data = $item->get_data();
                            if (! empty($data)) {
                                $productID    = isset($data['variation_id']) && 0 !== intval($data['variation_id']) ? intval($data['variation_id']) : intval($data['product_id']);
                                $productFicID = get_post_meta($productID, '_manage_product_stock', true) ?: null;
                                if ($productFicID) {
                                    // Get FIC product
                                    $productFicData = \WooPoPToFattureInCloudStock\Functions\fattureInCloudGetProduct($productFicID);
                                    if (! empty($productFicData->data) && $productFicData->data instanceof \FattureInCloud\Model\Product) {
                                        // Get WC product
                                        $wcProduct = wc_get_product($productID);
                                        // Sync of the stock with that of FIC
                                        $inStock  = $productFicData->data->getInStock() ? 'instock' : 'outofstock';
                                        $stockQty = apply_filters('wfc_ms-update_product_stock',
                                            $productFicData->data->getStockCurrent(), $wcProduct->get_id());
                                        $wcProduct->set_stock_quantity($stockQty);
                                        $wcProduct->set_stock_status($inStock);
                                        $wcProduct->save();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

/**
 * getProductsList
 *
 * @return array|false|mixed|void
 */
function getProductsList()
{
    return get_option('wfc_ms_products_list') ?: array();
}

/**
 * getProductCodesList
 *
 * @return array|false|mixed|void
 */
function getProductCodesList()
{
    return get_option('wfc_ms_product_codes_list') ?: array();
}

/**
 * getProductCategoryByIDList
 *
 * @return array|false|mixed|void
 */
function getProductCategoryByIDList()
{
    return get_option('wfc_ms_product_category_by_id_list') ?: array();
}

/**
 * getProductCategoryByIDList
 *
 * @return array|false|mixed|void
 */
function getProductCategoryByCodList()
{
    return get_option('wfc_ms_product_category_by_cod_list') ?: array();
}

/**
 * referenceProductExist
 *
 * Check if meta_data exist (wc product mapped)
 *
 * @param $refValue
 *
 * @return object
 */
function referenceProductExist($refValue)
{
    global $wpdb;
    $refValue = sanitize_text_field($refValue);

    // Try to get cached data
    $cache_key   = 'reference_product_exist_' . md5($refValue);
    $cached_data = wp_cache_get($cache_key, 'reference_product_exist');

    if ($cached_data !== false) {
        return $cached_data;
    }

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $sql = $wpdb->prepare(
        "SELECT DISTINCT {$wpdb->postmeta}.post_id 
        FROM {$wpdb->postmeta} 
        WHERE meta_key = '_manage_product_stock' 
        AND meta_value = %s",
        $refValue
    );

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $post = $wpdb->get_results(esc_sql($sql), ARRAY_A);
    $post = reset($post);

    $result = (object)array(
        'exist'  => ! empty($post),
        'postID' => ! empty($post) ? $post['post_id'] : null,
    );

    // Cache the result
    wp_cache_set($cache_key, $result, 'reference_product_exist', 3600);

    return $result;
}