<?php
/**
 * Jobs.php
 *
 * @since      1.0.0
 * @package    POPToFattureAruba
 * @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 WooPoPToFattureAruba;

use function WooPoPToFattureAruba\Functions\refreshOAuthToken;
use function WooPoPToFattureAruba\Functions\getActiveApi;
use function WooPoPToFattureAruba\Functions\getDocumentNotify;
use function WooPoPToFattureAruba\Functions\getDocumentState;
use function WooPoPToFattureAruba\Functions\uploadInvoice;

if (! defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}

/**
 * Class Jobs
 *
 * @author alfiopiccione <alfio.piccione@gmail.com>
 */
class Jobs
{
    /**
     * Api connected
     *
     * @var object
     */
    public static object $connected;

    /**
     * Scheduled events option
     *
     * @var
     */
    private static $scheduledEventsOption;

    /**
     * Scheduled events time option
     *
     * @var
     */
    private static $scheduledEventsTimeUploadOption;

    /**
     * Scheduled events time option
     *
     * @var
     */
    private static $scheduledEventsTimeNotificationsOption;

    /**
     * Nonce
     *
     * @var false|string
     */
    private static $nonce;

    /**
     * Jobs constructor.
     */
    public function __construct()
    {
        self::$connected                              = getActiveApi();
        self::$scheduledEventsOption                  = get_option('waf_scheduled_events');
        self::$scheduledEventsTimeUploadOption        = get_option('waf_scheduled_events_time') ?: 12;
        self::$scheduledEventsTimeNotificationsOption = get_option('waf_scheduled_notifications_events_time') ?: 12;
        self::$nonce                                  = wp_create_nonce('waf_scripts-ajax_nonce');
    }

    /**
     * Job timing
     *
     * @return false|float|int
     */
    public static function jobTiming($type = null)
    {
        // Upload
        $devTime  = time() + MINUTE_IN_SECONDS + mt_rand(1, 10);
        $prodTime = time() + HOUR_IN_SECONDS * self::$scheduledEventsTimeUploadOption + mt_rand(1, 10);

        // Notifications
        if ('notify' === $type) {
            $devTime  = time() + MINUTE_IN_SECONDS * 5 + mt_rand(1, 10);
            $prodTime = time() + (HOUR_IN_SECONDS * self::$scheduledEventsTimeNotificationsOption) + mt_rand(1, 10);
        }

        // Email
        if ('email' === $type) {
            $devTime  = time() + MINUTE_IN_SECONDS + mt_rand(1, 10);
            $prodTime = time() + MINUTE_IN_SECONDS * 15 + mt_rand(1, 10);
        }

        $prodTime = apply_filters('waf-job_timing', $prodTime, $type);

        return 'dev' === WP_FATT_ARUBA ? $devTime : $prodTime;
    }

    /**
     * Scheduled
     *
     * @param string $hook       Action hook to execute when the event is run.
     * @param array  $data       Optional. Array containing arguments to pass to the
     *                           hook's callback function. Each value in the array is passed
     *                           to the callback as an individual parameter. The array keys
     *                           are ignored. Default: empty array.
     * @param int    $time       Unix timestamp (UTC) for when to next run the event.
     *
     * @return bool
     */
    public static function scheduled(string $hook, array $data, int $time): bool
    {
        $scheduled = wp_schedule_single_event(
            $time,
            $hook,
            $data
        );

        $timeZone = new TimeZone();
        $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());
        $dateTime = new \DateTime('now');
        $dateTime->setTimezone($timeZone);
        $date = $dateTime->format('d/m/Y H:i');

        if ($scheduled) {
            // Log
            \WooPoPToFattureAruba\Functions\log("Scheduled - {$hook} \nDate: {$date} \nTimeStamp: {$time}\nEvent Data: " . wp_json_encode(array($data)) . "\n");

            return true;
        }

        \WooPoPToFattureAruba\Functions\log("Scheduled Error - {$hook} \nDate: {$date} \nTimeStamp: {$time}\nEvent Data: " . wp_json_encode(array($data)) . "\n");

        return false;
    }

    /**
     * WooCommerce change status trigger
     *
     * @param int    $orderID The order id
     * @param string $from    The from state
     * @param string $to      The to state
     * @param string $provider
     *
     * @return bool
     * @throws \Exception
     */
    public static function changeStatus(int $orderID, string $from, string $to, string $provider = 'woocommerce'): bool
    {
        if ('on' !== self::$scheduledEventsOption) {
            return false;
        }

        $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($orderID, $provider);

        // Check if order exist.
        $wcOrderClass = \WooPoPToFattureAruba\Functions\wcOrderClassName($order, '\WC_Order');

        if (! is_subclass_of($order, '\WcElectronInvoice\Providers\Order') &&
            ! $order instanceof $wcOrderClass) {
            return false;
        }

        // Zero total
        if (floatval(0) === floatval($order->get_total())) {
            return false;
        }

        // Check if the invoice was already sent
        $checkSent = $order->get_meta("_invoice_sent");
        if ('sent' === $checkSent) {
            return false;
        }

        // FROM completed or FROM equal TO or TO not completed return false
        $valueOrderStatus = get_option('waf_scheduled_order_status', 'completed');
        if ($valueOrderStatus === $from || $from === $to || $valueOrderStatus !== $to) {
            $nextScheduled = wp_next_scheduled(
                'WooPoPToFattureArubaJobsScheduledUpload',
                [$order->get_id(), $provider]
            );
            // Clear scheduled
            if ($nextScheduled) {
                wp_clear_scheduled_hook('WooPoPToFattureArubaJobsScheduledUpload', [$order->get_id(), $provider]);
            }

            return false;
        }

        // Scheduled Upload
        $time          = self::jobTiming();
        $nextScheduled = wp_next_scheduled(
            'WooPoPToFattureArubaJobsScheduledUpload',
            [$order->get_id(), $provider]
        );
        if (! $nextScheduled) {
            self::scheduled(
                'WooPoPToFattureArubaJobsScheduledUpload',
                [$order->get_id(), $provider],
                $time
            );
        } else {
            \WooPoPToFattureAruba\Functions\log("Event already scheduled for order {$order->get_id()} and provider {$provider} to the " . date('Y-m-d H:i:s', $nextScheduled));
        }

        return true;
    }

    /**
     * Auto complete trigger
     *
     * @param int    $id The order id
     * @param string $provider
     *
     * @return bool
     * @throws \Exception
     */
    public static function autoCompleted(int $id, string $provider = 'woocommerce'): bool
    {
        if ('on' !== self::$scheduledEventsOption) {
            return false;
        }

        $order     = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
        $checkSent = \WcElectronInvoice\Functions\getPostMeta("_invoice_sent", null, $id, true, 'order', $provider);

        // Check if order exist.
        $wcOrderClass = \WooPoPToFattureAruba\Functions\wcOrderClassName($order, '\WC_Order');

        if (! is_subclass_of($order, '\WcElectronInvoice\Providers\Order') &&
            ! $order instanceof $wcOrderClass) {
            return false;
        }

        // Zero total
        if (floatval(0) === floatval($order->get_total())) {
            return false;
        }

        // Sent
        if ('sent' === $checkSent) {
            return false;
        }

        // Check status.
        $valueOrderStatus = get_option('waf_scheduled_order_status', 'completed');
        $orderStatus      = $order->get_status();
        if ($valueOrderStatus === $orderStatus) {
            // Scheduled Upload
            $time          = self::jobTiming();
            $nextScheduled = wp_next_scheduled(
                'WooPoPToFattureArubaJobsScheduledUpload',
                [$order->get_id(), $provider]
            );
            if (! $nextScheduled) {
                self::scheduled(
                    'WooPoPToFattureArubaJobsScheduledUpload',
                    [$order->get_id(), $provider],
                    $time
                );
            } else {
                \WooPoPToFattureAruba\Functions\log("Event already scheduled for order {$order->get_id()} and provider {$provider} to the " . date('Y-m-d H:i:s', $nextScheduled));
            }

            return true;
        }

        return false;
    }

    /**
     * Twice daily Upload Invoice
     *
     * @return void
     * @throws \Exception
     */
    public static function twiceDailyUploadInvoice()
    {
        $data = getActiveApi();
        if (! $data->active) {
            wp_clear_scheduled_hook('twiceDailyWooPoPToFattureArubaJobsScheduledUpload');
            return;
        }

        $valueCronJobTwiceDaily = get_option('waf_scheduled_twicedaily_events');
        if ('on' === $valueCronJobTwiceDaily) {

            if (! wp_next_scheduled('twiceDailyWooPoPToFattureArubaJobsScheduledUpload')) {
                wp_schedule_event(time(),
                    'twicedaily',
                    'twiceDailyWooPoPToFattureArubaJobsScheduledUpload'
                );
            }

            add_action("twiceDailyWooPoPToFattureArubaJobsScheduledUpload", function () {
                $timeZone = new TimeZone();
                $timeZone = new \DateTimeZone($timeZone->getTimeZone()->getName());
                $now      = new \DateTime('now');
                $now->setTimezone($timeZone);
                $daysAgo = $now->modify('-7 days');
                $out     = $daysAgo->getTimestamp();
                $args    = array(
                    'status'         => array('completed'),
                    'limit'          => -1,
                    'orderby'        => 'date',
                    'order'          => 'DESC',
                    // search meta _invoice_sent
                    // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
                    'meta_key'       => '_invoice_sent',
                    // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
                    'meta_value'     => 'sent',
                    'meta_compare'   => '!=',
                    // Completed last 7 days
                    'date_completed' => ">{$out}",
                    'return'         => 'ids',
                );

                $orders = \WcElectronInvoice\Providers\OrderQuery::instance()->getProvidersOrders($args) ?: [];

                // Log
                $counts = count($orders);
                \WooPoPToFattureAruba\Functions\log(
                    "Jobs twiceDailyArubaJobsScheduledUpload orders: {$counts}\n args: " .
                    print_r($args, true) . "\n"
                );

                try {
                    if (! empty($orders)) {
                        foreach ($orders as $order) {
                            $provider  = $order->get_provider() ?: 'woocommerce';
                            $checkSent = \WcElectronInvoice\Functions\getPostMeta("_invoice_sent", null,
                                $order->get_id(), true,
                                'order', $provider);
                            if ('sent' === $checkSent) {
                                continue;
                            }

                            self::uploadInvoice((int)$order->get_id(), $provider);
                        }
                    }
                } catch (\Exception $e) {
                    // Log
                    \WooPoPToFattureAruba\Functions\log(
                        "Jobs twiceDailyArubaJobsScheduledUpload: {$e->getCode()} - {$e->getMessage()}" . "\n"
                    );
                }
            });
        } else {
            wp_clear_scheduled_hook('twiceDailyWooPoPToFattureArubaJobsScheduledUpload');
        }
    }

    /**
     * Create Invoice
     *
     * @param int    $id The order ID
     * @param string $provider
     *
     * @return void
     * @throws \Exception
     */
    public static function uploadInvoice(int $id, string $provider = 'woocommerce')
    {
        $order      = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider);
        $orderType  = $order->get_type();
        $orderID    = $order->get_id();
        $choiceType = $order->get_billing_choice_type() ?: 'invoice';

        if (floatval(0) === floatval($order->get_total())) {
            return;
        }

        // Return if document exist
        $documentFileName = \WcElectronInvoice\Functions\getPostMeta("waf-upload_file_name", null, $id, true, 'order',
            $provider);
        if ($documentFileName) {
            return;
        }

        // Return if document is send
        $checkSent = \WcElectronInvoice\Functions\getPostMeta("_invoice_sent", null, $id, true, 'order', $provider);
        if ('sent' === $checkSent) {
            return;
        }

        // Check scheduled running
        $nextScheduled = wp_get_scheduled_event(
            'WooPoPToFattureArubaJobsScheduledUpload',
            [$order->get_id(), $provider]
        );
        if ($nextScheduled && $nextScheduled->timestamp &&
            1 === (int)did_action($nextScheduled->hook) &&
            doing_action($nextScheduled->hook) &&
            $nextScheduled->hook === current_action()
        ) {
            \WooPoPToFattureAruba\Functions\log(
                "OrderID {$id} - Duplicate event!" . "\nScheduled event: " . print_r($nextScheduled, true) . "\n");

            return;
        }

        if ('invoice' === $choiceType) {
            try {
                // Set all post parameters to be passed to simulate the AJAX request
                if (! defined('DOING_AJAX')) {
                    define('DOING_AJAX', true);
                }

                // Refreshing access token or Force SignIn
                $forceSignIn = refreshOAuthToken(true);

                // Log
                \WooPoPToFattureAruba\Functions\log("Jobs OrderID: {$id} - refreshOAuthToken: {$forceSignIn}" . "\n");

                // Force Aruba oauth SignIn
                if ($forceSignIn) {
                    // Set $_POST data for request
                    $_POST = [
                        'ajaxAction'     => 'feCronJobUploadRequest',
                        'nonce'          => self::$nonce,
                        'choiceType'     => $choiceType,
                        'orderID'        => $orderID,
                        'provider'       => $provider,
                        'orderType'      => $orderType,
                        'jobForceSignIn' => $forceSignIn,
                    ];

                    // Log
                    \WooPoPToFattureAruba\Functions\log("Jobs uploadInvoice POST data:" . print_r($_POST, true) . "\n");

                    $data = uploadInvoice();
                    if (is_object($data) && property_exists($data, 'fileName')) {
                        $time          = self::jobTiming('notify');
                        $nextScheduled = wp_next_scheduled(
                            'WooPoPToFattureArubaJobsScheduledNotify',
                            [$order->get_id(), $data->fileName, $provider]
                        );
                        if (! $nextScheduled) {
                            self::scheduled(
                                'WooPoPToFattureArubaJobsScheduledNotify',
                                [$order->get_id(), $data->fileName, $provider],
                                $time
                            );
                        }
                    }
                }
            } catch (\Exception $e) {
                if (401 === $e->getCode()) {
                    $dataException = array(
                        'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                            WP_FATT_ARUBA_TEXTDOMAIN),
                        'e-message' => $e->getMessage(),
                        'e-trace'   => $e->getTraceAsString(),
                        'code'      => $e->getCode(),
                    );
                } else {
                    $dataException = array(
                        'message'   => esc_html('Exception Upload invoice: ' . $e->getMessage()),
                        'e-message' => $e->getMessage(),
                        'e-trace'   => $e->getTraceAsString(),
                        'code'      => $e->getCode(),
                    );
                }

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

    /**
     * getDocumentNotify
     *
     * @param int    $orderID  The order ID
     * @param string $fileName The invoice uploaded file name
     * @param string $provider
     *
     * @return void
     */
    public static function getDocumentNotify(int $orderID, string $fileName, string $provider = 'woocommerce')
    {
        try {
            // Set all post parameters to be passed to simulate the AJAX request
            if (! defined('DOING_AJAX')) {
                define('DOING_AJAX', true);
            }

            // Refreshing access token or Force SignIn
            if ($forceSignIn = refreshOAuthToken(true)) {
                // Set $_POST data for request
                $_POST = [
                    'ajaxAction'     => 'feCronJobDocumentNotifyRequest',
                    'nonce'          => self::$nonce,
                    'docName'        => $fileName,
                    'orderID'        => $orderID,
                    'provider'       => $provider,
                    'jobForceSignIn' => $forceSignIn,
                ];

                getDocumentNotify();
            }
        } catch (\Exception $e) {
            if (401 === $e->getCode()) {
                $dataException = array(
                    'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                        WP_FATT_ARUBA_TEXTDOMAIN),
                    'e-message' => $e->getMessage(),
                    'e-trace'   => $e->getTraceAsString(),
                    'code'      => $e->getCode(),
                );
            } else {
                $dataException = array(
                    'message'   => esc_html('Exception Notify: ' . $e->getMessage()),
                    'e-message' => $e->getMessage(),
                    'e-trace'   => $e->getTraceAsString(),
                    'code'      => $e->getCode(),
                );
            }

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

    /**
     * sendProformaInvoicePdf
     *
     * @param int    $orderID  The order ID
     * @param string $fileName The invoice uploaded file name
     * @param string $provider
     *
     * @return void
     */
    public static function sendProformaInvoicePdf(int $orderID, string $fileName, string $provider = 'woocommerce')
    {
        try {
            // Set all post parameters to be passed to simulate the AJAX request
            if (! defined('DOING_AJAX')) {
                define('DOING_AJAX', true);
            }

            // Has the email with the invoice been sent?
            $sendingProforma = \WcElectronInvoice\Functions\getPostMeta("waf-sending_proforma", null, $orderID, true,
                'order', $provider) ?: false;

            // Refreshing access token or Force SignIn
            if ($forceSignIn = refreshOAuthToken(true) && false === $sendingProforma) {
                // Set $_POST data for request
                $_POST = [
                    'ajaxAction'     => 'feCronJobSendProformaInvoiceRequest',
                    'nonce'          => self::$nonce,
                    'docName'        => $fileName,
                    'orderID'        => $orderID,
                    'provider'       => $provider,
                    'includePdfFile' => true,
                    'jobForceSignIn' => $forceSignIn,
                ];

                getDocumentState();
            } else {
                // Slack notify
                \WooPoPToFattureAruba\Functions\slack(
                    WP_FATT_ARUBA_NAME . ' ' .
                    __('The PDF invoice has already been sent manually', WP_FATT_ARUBA_TEXTDOMAIN) . "\n",
                    false,
                    ':information_source:'
                );
            }
        } catch (\Exception $e) {
            if (401 === $e->getCode()) {
                $dataException = array(
                    'message'   => esc_html__('Unauthorized: the Token seems to be invalid, update it!',
                        WP_FATT_ARUBA_TEXTDOMAIN),
                    'e-message' => $e->getMessage(),
                    'e-trace'   => $e->getTraceAsString(),
                    'code'      => $e->getCode(),
                );
            } else {
                $dataException = array(
                    'message'   => esc_html('Exception Send ProForma: ' . $e->getMessage()),
                    'e-message' => $e->getMessage(),
                    'e-trace'   => $e->getTraceAsString(),
                    'code'      => $e->getCode(),
                );
            }

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

}
