<?php
/**
 * Webhooks.php
 *
 * @since      1.0.0
 * @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 WcElectronInvoice\Functions;

use WcElectronInvoice\Admin\Settings\OptionPage;
use WcElectronInvoice\Providers\OrderQuery;

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

/**
 * webHookData
 *
 * @return object
 */
function webHookData(): object
{
    $options = [
        'wc_el_inv-enable_pop_webhook',
        'wc_el_inv-pop_webhook_id',
        'wc_el_inv-automatic_sending_webhook_status',
        'wc_el_inv-enable_automatic_sending_webhook',
        'wc_el_inv-automatic_sending_webhook_time',
    ];

    $data = [];
    foreach ($options as $item) {
        $key        = str_replace('wc_el_inv-', '', $item);
        $data[$key] = get_option($item);
    }

    $level = \WcElectronInvoice\Wizard::popGetUserLevel();
    if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')) {
        $data['enable_pop_webhook'] = 'no';
        $data['enable_automatic_sending_webhook'] = 'no';
    }

    return (object)$data;
}

/**
 * usePoPWebhook
 *
 * @return false|string
 */
function usePoPWebhook()
{
    $data = webHookData();
    $id   = $data->pop_webhook_id;
    if ('yes' === $data->enable_pop_webhook && ! empty($id) && is_string($id)) {
        return $id;
    }

    return false;
}

/**
 * webHookActions
 *
 * @param        $output
 * @param        $id
 * @param        $item
 * @param string $provider
 *
 * @return string
 */
function webHookActions($output, $id, $item = null, string $provider = 'woocommerce'): string
{
    // Get provider from item data
    if ($item && ! $provider) {
        $provider = isset($item['order_provider']) ? $item['order_provider'] : null;
        $provider = ! $provider && isset($item['provider']) ? $item['provider'] : $provider;
    }

    // Default meta key and order "shop_order"
    $webhookMetaKey = "pop_webhook-response";
    $order          = OrderQuery::instance()->getProviderOrder($id, $provider);

    // Check for credit note
    if ('woocommerce' === $provider) {
        if ('shop_order_refund' === $order->get_type()) {
            // Set refund meta key
            $webhookMetaKey = "pop_webhook-response_refund-{$order->get_id()}";

            //Get parent order for save meta data
            $parentID = $order->get_parent_id();
            $order    = OrderQuery::instance()->getProviderOrder($parentID, $provider);
        }
    }

    // Get meta "$webhookMetaKey"
    $webhookResponse = $order->get_meta($webhookMetaKey) ?: null;

    if ($webhookResponse) {
        $webhookResponse = json_decode($webhookResponse);
    }

    $output         .= '<div class="pop_to_webhook_actions">';
    $output         .= sprintf('<hr><h4>%s:</h4>', __('Webhook', WC_EL_INV_TEXTDOMAIN));
    $level          = \WcElectronInvoice\Wizard::popGetUserLevel();
    $buttonText     = esc_html__('Send to Webhook', WC_EL_INV_TEXTDOMAIN);
    $buttonTextSent = esc_html__('Sent', WC_EL_INV_TEXTDOMAIN);

    if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') ||
        (is_object($webhookResponse) &&
         200 === $webhookResponse->status_code &&
         $webhookResponse->success &&
         ! empty($webhookResponse->webhook_response)
        )
    ) {
        $output .= "<a disabled='disabled' data-provider='{$provider}' data-id='{$id}' class='disabled send-webhook button button-primary' id='send_webhook_{$id}'>{$buttonTextSent}</a>";
    } else {
        $output .= "<a href='javascript:;' data-provider='{$provider}' data-id='{$id}' class='api-action send-webhook button button-primary' id='send_webhook_{$id}'>{$buttonText}</a>";
    }
    $output .= '</div>';

    return $output;
}

/**
 * sendWebhook
 *
 * @return mixed|void
 * @throws \Exception
 */
function sendWebhook()
{
    $usePoPWebhookId = usePoPWebhook();
    $licenseKey      = OptionPage::init()->getOptions('license_key') ?? null;
    $level           = \WcElectronInvoice\Wizard::popGetUserLevel();
    $provider        = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'provider') ?? '');
    $id              = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? '');
    $action          = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null;
    $method          = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null;
    $shouldRespond   = \WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob']);

    if (! \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'feCronJobUploadRequest'])) {
        if (! check_ajax_referer('wc_el_inv_ajax-ajax_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => esc_html__('Unauthorized.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }
        if (! current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => esc_html__('Forbidden.', WC_EL_INV_TEXTDOMAIN)]);
            return;
        }
    }

    if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic') || ! $usePoPWebhookId) {
        if ($shouldRespond) {
            wp_send_json_error([
                'message' => esc_html__('Action not allowed.',
                    WC_EL_INV_TEXTDOMAIN),
            ]);
        }

        return;
    }

    // I check the action parameter if it does not match I get an error
    if ('sendWebhook' !== $action) {
        if ($shouldRespond) {
            wp_send_json_error([
                'message' => esc_html__('Action not valid.',
                    WC_EL_INV_TEXTDOMAIN),
            ]);
        }

        return;
    }

    $order = OrderQuery::instance()->getProviderOrder((int)$id, $provider);

    // Default meta key and order "shop_order"
    $webhookMetaKey = "pop_webhook-response";
    if ('shop_order_refund' === $order->get_type()) {
        // Set refund meta key
        $webhookMetaKey = "pop_webhook-response_refund-{$order->get_id()}";
    }

    $webhookResponse = $order->get_meta($webhookMetaKey);
    if ($webhookResponse) {
        $webhookResponse = json_decode($webhookResponse);
    }

    if ((200 === $webhookResponse->status_code &&
         $webhookResponse->success &&
         ! empty($webhookResponse->webhook_response))
    ) {

        if ($shouldRespond) {
            wp_send_json_error([
                'message' => sprintf(esc_html__('Invalid action: Webhook has already been sent, please delete "%s" data to resend',
                    WC_EL_INV_TEXTDOMAIN),
                    $webhookMetaKey
                ),
            ]);
        }

        return;
    }

    add_filter('wc_el_inv-params_filter_before_api_request', function ($params) use ($usePoPWebhookId) {
        $params['integration'] = [
            'use' => 'pop-to-webhook',
            'id'  => $usePoPWebhookId,
        ];

        return $params;
    });

    $data     = getXmlFromOrder($order);
    $response = json_decode($data);

    if ('shop_order_refund' === $order->get_type()) {
        $order = OrderQuery::instance()->getProviderOrder($order->get_parent_id(), $provider);
    }

    if (json_last_error() !== JSON_ERROR_NONE || ! is_object($response)) {
        \WcElectronInvoice\Functions\log("Webhook Response > Invalid Json:\n" . print_r($data,
                true) . "\n");
        $order->update_meta($webhookMetaKey, wp_json_encode([
            'license_key'      => $licenseKey,
            'webhook_id'       => $usePoPWebhookId,
            'error_code'       => $response->error_code ?? null,
            'status_code'      => $response->status_code,
            'success'          => $response->success,
            'pop_webhook'      => $response->pop_webhook,
            'webhook_response' => $response->data ?? '',
            'created_at'       => current_time('mysql'),
        ]));

        if ($shouldRespond) {
            wp_send_json_error([
                'message' => esc_html__('Webhook Response > Invalid Json.',
                    WC_EL_INV_TEXTDOMAIN),
                'raw'     => $data,
            ]);
        }

        return;
    }

    $order->update_meta($webhookMetaKey, wp_json_encode([
        'license_key'      => $licenseKey,
        'webhook_id'       => $usePoPWebhookId,
        'error_code'       => $response->error_code ?? null,
        'status_code'      => $response->status_code,
        'success'          => $response->success,
        'pop_webhook'      => $response->pop_webhook,
        'response_message' => $response->message ?? '',
        'webhook_response' => $response->data ?? '',
        'created_at'       => current_time('mysql'),
    ]));

    if (! $response->success) {
        \WcElectronInvoice\Functions\log("Webhook Response > " . $response->message . "\n" . print_r($response,
                true) . "\n");

        if ($shouldRespond) {
            wp_send_json_error([
                'message' => $response->message,
                'check'   => 'success',
                'code'    => $response->status_code,
                'raw'     => $response,
            ]);
        }

        return;
    }

    if ($shouldRespond) {
        wp_send_json_success([
            'message'  => $response->message,
            'provider' => $provider,
            'order_id' => $id,
            'raw'      => $response,
        ]);

        return;
    }

    \WcElectronInvoice\Functions\log($response->message . "\n" . print_r([
            'provider' => $provider,
            'order_id' => $id,
            'raw'      => $response,
        ],
            true) . "\n");

    return $response;
}

/**
 * webHookScheduled
 *
 * @param string $hook
 * @param array  $data
 * @param int    $time
 *
 * @return bool
 */
function webHookScheduled(string $hook, array $data, int $time): bool
{
    $scheduled = wp_schedule_single_event(
        $time,
        $hook,
        $data
    );

    if ($scheduled) {
        // Log
        \WcElectronInvoice\Functions\log("Scheduled - {$hook} time: {$time}\nData:\n" . wp_json_encode($data) . "\n");

        return true;
    }

    return false;
}

/**
 * webHookScheduledCron
 *
 * @param int         $orderID
 * @param string|null $from
 * @param string|null $to
 * @param string      $provider
 *
 * @return bool
 * @throws \Exception
 */
function webHookScheduledCron(
    int $orderID,
    ?string $from = null,
    ?string $to = null,
    string $provider = 'woocommerce'
): bool {
    $webHookData = webHookData();

    $devTime  = time() + MINUTE_IN_SECONDS * 5 + mt_rand(1, 10);
    $prodTime = time() + HOUR_IN_SECONDS * (int)$webHookData->automatic_sending_webhook_time + mt_rand(1, 10);
    $prodTime = apply_filters('wc_el_inv-webhook_job_timing', $prodTime);

    $cronTime = 'dev' === WC_EL_INV_ENV ? $devTime : $prodTime;

    $level = \WcElectronInvoice\Wizard::popGetUserLevel();
    if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'basic')) {
        return false;
    }

    if ('yes' !== $webHookData->enable_automatic_sending_webhook) {
        return false;
    }

    // Get order
    $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($orderID, $provider);
    // Check WC_Order instance
    $wcOrderClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order');

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

    // Return if total is 0
    if (disableInvoiceOnOrderTotalZero($order)) {
        return false;
    }

    // Check if the invoice was already sent
    $checkSent = $order->get_meta("_invoice_sent", true);
    if ('sent' === $checkSent) {
        // Log
        \WcElectronInvoice\Functions\log("Document is sent: {$checkSent}" . "\n");

        return false;
    }

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

            return false;
        }

        // Scheduled Import
        $nextScheduled = wp_next_scheduled(
            'PoPToWebhookJobsScheduledImport',
            [$order->get_id(), $provider]
        );

        if (! $nextScheduled) {
            webHookScheduled(
                'PoPToWebhookJobsScheduledImport',
                [$order->get_id(), $provider],
                $cronTime
            );
        }

        return true;
    } else {
        // Check status.
        $orderStatus = $order->get_status();
        if ($valueOrderStatus === $orderStatus) {
            // Scheduled Import
            $nextScheduled = wp_next_scheduled(
                'PoPToWebhookJobsScheduledImport',
                [$order->get_id(), $provider]
            );
            if (! $nextScheduled) {
                webHookScheduled(
                    'PoPToWebhookJobsScheduledImport',
                    [$order->get_id(), $provider],
                    $cronTime
                );
            }

            return true;
        }

        return false;
    }
}
