<?php
/**
 * Jobs.php
 *
 * @since      1.0.0
 * @package    POPToSdIPec
 * @author     POP <info@popapi.io>
 * @license    http://opensource.org/licenses/gpl-2.0.php GPL v2
 *
 * Copyright 2025 POP <info@popapi.io>
 *
 * 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 WooPoPToSdIPec;

use function WooPoPToSdIPec\Functions\getActiveApi;
use function WooPoPToSdIPec\Functions\uploadInvoice;

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

/**
 * Class Jobs
 *
 * @author POP <info@popapi.io>
 */
class Jobs
{
    /**
     * Scheduled events option
     *
     * @var
     */
    private static $scheduledEventsOption;

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

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

    /**
     * Jobs constructor.
     */
    public function __construct()
    {
        self::$scheduledEventsOption           = get_option('wsdipec_scheduled_events');
        self::$scheduledEventsTimeUploadOption = get_option('wsdipec_scheduled_events_time') ?: 12;
        self::$nonce                           = wp_create_nonce('wsdipec_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);

        // 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('woopopsdi-job_timing', $prodTime, $type);

        return 'dev' === WP_FATT_SDI_MODE ? $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
            \WooPoPToSdIPec\Functions\log("Scheduled - {$hook} \nDate: {$date} \nTimeStamp: {$time}\nEvent Data: " . wp_json_encode(array($data)) . "\n");

            return true;
        }

        \WooPoPToSdIPec\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 = \WooPoPToSdIPec\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", true);
        if ('sent' === $checkSent) {
            return false;
        }

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

            return false;
        }

        // Scheduled Upload
        $time          = self::jobTiming();
        $nextScheduled = wp_next_scheduled(
            'WooPoPToSdIPecJobsScheduledUpload',
            [$order->get_id(), $provider]
        );
        if (! $nextScheduled) {
            self::scheduled(
                'WooPoPToSdIPecJobsScheduledUpload',
                [$order->get_id(), $provider],
                $time
            );
        } else {
            \WooPoPToSdIPec\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 = \WooPoPToSdIPec\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('wsdipec_scheduled_order_status', 'completed');
        $orderStatus      = $order->get_status();
        if ($valueOrderStatus === $orderStatus) {
            // Scheduled Upload
            $time          = self::jobTiming();
            $nextScheduled = wp_next_scheduled(
                'WooPoPToSdIPecJobsScheduledUpload',
                [$order->get_id(), $provider]
            );
            if (! $nextScheduled) {
                self::scheduled(
                    'WooPoPToSdIPecJobsScheduledUpload',
                    [$order->get_id(), $provider],
                    $time
                );
            } else {
                \WooPoPToSdIPec\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->smtpConnected) {
            wp_clear_scheduled_hook('twiceDailyWooPoPToSdIPecJobsScheduledUpload');
            return;
        }

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

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

            add_action("twiceDailyWooPoPToSdIPecJobsScheduledUpload", 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);
                \WooPoPToSdIPec\Functions\log(
                    "Jobs twiceDailySdIJobsScheduledUpload orders: {$counts}\n args: " .
                    print_r($args, true) . "\n"
                );

                try {
                    if (! empty($orders)) {
                        foreach ($orders as $order) {
                            $provider = $order->get_provider() ?: 'woocommerce';
                            // it's useless but I'll check
                            $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
                    \WooPoPToSdIPec\Functions\log(
                        "Jobs twiceDailySdIJobsScheduledUpload: {$e->getCode()} - {$e->getMessage()}" . "\n"
                    );
                }
            });
        } else {
            wp_clear_scheduled_hook('twiceDailyWooPoPToSdIPecJobsScheduledUpload');
        }
    }

    /**
     * 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 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(
            'WooPoPToSdIPecJobsScheduledUpload',
            [$order->get_id(), $provider]
        );
        if ($nextScheduled->timestamp &&
            1 === (int)did_action($nextScheduled->hook) &&
            doing_action($nextScheduled->hook) &&
            $nextScheduled->hook === current_action()
        ) {
            \WooPoPToSdIPec\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);
                }

                // Set $_POST data for request
                $_POST = [
                    'ajaxAction' => 'feCronJobUploadRequest',
                    'nonce'      => self::$nonce,
                    'choiceType' => $choiceType,
                    'orderID'    => $orderID,
                    'provider'   => $provider,
                    'orderType'  => $orderType,
                ];

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

                uploadInvoice();
            } catch (\Exception $e) {
                $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();
            }
        }
    }
}
