<?php
/**
 * Api.php
 *
 * @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\Functions;

use WcElectronInvoice\Admin\Settings\OptionPage;
use WcElectronInvoice\WooCommerce\Fields\GeneralFields;
use WcElectronInvoice\Xml\CreateXml;
use function WcElectronInvoice\Functions\getXmlFromOrder;
use function WcElectronInvoice\Functions\isXml;

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

/**
 * getDateTime
 *
 * @return string
 */
function getDateTime()
{
    return date_i18n('d/m/Y H:i');
}

/**
 * Get Active api data
 *
 * @return object
 */
function getActiveApi()
{
    $smtpConfigured = get_option('wsdipec_smtp_configured');
    $smtpConnected  = get_option('wsdipec_smtp_connected');

    $smtpExpiredPass = get_option('wsdipec_smtp_pass_expiration');
    $smtpHost        = get_option('wsdipec_smtp_host');
    $smtpSecure      = get_option('wsdipec_smtp_secure');
    $smtpUser        = get_option('wsdipec_smtp_user');
    $smtpPass        = get_option('wsdipec_smtp_pass');

    return (object)array(
        'smtpConfigured'  => $smtpConfigured,
        'smtpConnected'   => $smtpConnected,
        'smtpHost'        => $smtpHost,
        'smtpSecure'      => $smtpSecure,
        'smtpUser'        => $smtpUser,
        'smtpPass'        => $smtpPass,
        'smtpExpiredPass' => $smtpExpiredPass,
    );
}

/**
 * smtpSave
 *
 * @return void
 */
function smtpSave()
{
    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $smtpExpiredPass = filterInput($_POST, 'smtpExpiredPass', FILTER_SANITIZE_SPECIAL_CHARS);
    $smtpHost        = filterInput($_POST, 'smtpHost', FILTER_SANITIZE_SPECIAL_CHARS);
    $smtpSecure      = filterInput($_POST, 'smtpSecure', FILTER_SANITIZE_SPECIAL_CHARS);
    $smtpUser        = filterInput($_POST, 'smtpUser', FILTER_SANITIZE_SPECIAL_CHARS);
    $smtpPass        = filterInput($_POST, 'smtpPass', FILTER_SANITIZE_SPECIAL_CHARS);
    $nonce           = filterInput($_POST, 'nonce', FILTER_SANITIZE_SPECIAL_CHARS);

    if (! wp_verify_nonce($nonce, 'wsdipec_scripts-ajax_nonce')) {
        return;
    }

    if (! $smtpHost || ! $smtpSecure || ! $smtpUser || ! $smtpPass) {
        $response = array(
            'message' => esc_html__('Smtp Save request error: Invalid params', WP_FATT_SDI_PEC_TEXTDOMAIN),
            'code'    => 501,
        );

        wp_send_json($response);
        die();
    }

    // smtp connection to be tested again
    update_option('wsdipec_smtp_connected', false);

    // Store login check
    update_option('wsdipec_smtp_configured', true);
    update_option('wsdipec_smtp_host', "{$smtpHost}");
    update_option('wsdipec_smtp_pass_expiration', "{$smtpExpiredPass}");
    update_option('wsdipec_smtp_secure', "{$smtpSecure}");
    update_option('wsdipec_smtp_user', "{$smtpUser}");
    update_option('wsdipec_smtp_pass', "{$smtpPass}");

    // Check credentials
    $apiData = getActiveApi();

    if (! class_exists("\PHPMailer\PHPMailer\PHPMailer")) {
        require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
        require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
        require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
    }

    $validCredentials = false;
    try {
        $mail          = new \PHPMailer\PHPMailer\PHPMailer(true);
        $mail->CharSet = 'utf-8';

        $mail->isSMTP();
        $mail->Host       = $apiData->smtpHost;
        $mail->SMTPSecure = $apiData->smtpSecure;
        $mail->Port       = $apiData->smtpSecure === 'tls' ? 587 : 465;
        $mail->SMTPAuth   = true;

        $mail->Username = $apiData->smtpUser;
        $mail->Password = $apiData->smtpPass;

        $mail->SMTPOptions = array(
            "ssl" => array(
                "verify_peer"  => true,
                "cafile"       => WP_FATT_SDI_DIR . "/assets/cacert.pem",
                "verify_depth" => 5,
                "peer_name"    => $apiData->smtpHost,
            ),
        );

        $validCredentials = $mail->SmtpConnect();
    } catch (\Exception $e) {
        // just avoid the fatal.
        log(print_r([$e->getMessage(), $e->getTraceAsString(), $e->getCode()], true));
    }

    update_option('wsdipec_smtp_connected', $validCredentials);

    if ($validCredentials) {
        $responseArgs = array(
            'message' => esc_html__('Saved. Connection OK!', WP_FATT_SDI_PEC_TEXTDOMAIN),
            'code'    => 200,
        );

        do_action('woopopsdi-saved_connection_ok');
    } else {
        $responseArgs = array(
            'message' => esc_html__('Saved. Invalid credentials though, or wrong configuration.',
                WP_FATT_SDI_PEC_TEXTDOMAIN),
            'code'    => 401,
        );

        do_action('woopopsdi-saved_connection_ko');
    }

    wp_send_json($responseArgs);
    die();
}

/**
 * Upload Invoice
 *
 * @return object|void
 * @throws \Exception
 */
function uploadInvoice()
{
    $key = OptionPage::getK();
    if (! $key) {
        \WooPoPToSdIPec\Functions\log("POP secret-api-key undefined" . "\n");
        wp_send_json(array(
            'title'   => __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN),
            'body'    => null,
            'message' => esc_html__('Warning: the POP license expired', WP_FATT_SDI_PEC_TEXTDOMAIN),
            'code'    => 501,
        ));

        return;
    }

    // No ajax request ?
    if (! isAjaxRequest()) {
        return;
    }

    $nonce      = filterInput($_POST, 'nonce', FILTER_SANITIZE_SPECIAL_CHARS);
    $choiceType = filterInput($_POST, 'choiceType', FILTER_SANITIZE_SPECIAL_CHARS);
    $orderID    = filterInput($_POST, 'orderID', FILTER_SANITIZE_SPECIAL_CHARS);
    $orderType  = filterInput($_POST, 'orderType', FILTER_SANITIZE_SPECIAL_CHARS);
    $ajaxAction = filterInput($_POST, 'ajaxAction', FILTER_SANITIZE_SPECIAL_CHARS);
    $provider   = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW);

    if (! wp_verify_nonce($nonce, 'wsdipec_scripts-ajax_nonce')) {
        return;
    }

    // Return if invoice is sent
    $scheduledCronJob = get_option('wsdipec_scheduled_events');
    $checkSent        = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $orderID, true, 'order',
        $provider);
    if ('on' === $scheduledCronJob &&
        \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) &&
        'sent' === $checkSent
    ) {
        return;
    }

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

    // Get Payments method code
    $payCode = getPaymentMethodCodeByMethod($order->get_payment_method());

    /**
     * Disable by payment method
     */
    if (true === apply_filters('woopopsdi-upload-invoice_exclude_payment_method_code', false,
            $payCode,
            $order->get_payment_method()
        )
    ) {
        if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction,
                ['feCronJobUploadRequest'])) {
            wp_send_json(array(
                'title'   => __('Payment method excluded for sending.', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'body'    => null,
                'message' => sprintf('<b style="color: red">(*)</b> %s - %s (%s)',
                    __("The payment method of the order has been excluded from sending to SDI",
                        WP_FATT_SDI_PEC_TEXTDOMAIN),
                    $payCode,
                    $order->get_payment_method()
                ),
                'code'    => 501,
            ));
            die();
        } else {
            \WcElectronInvoice\Functions\log(sprintf('%s: %s (%s)',
                    __("Payment method excluded for sending", WP_FATT_SDI_PEC_TEXTDOMAIN),
                    $payCode,
                    $order->get_payment_method()) . "\n");

            return;
        }
    }

    $wcOrderClass       = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order');
    $wcOrderRefundClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order_Refund');

    if (($order instanceof $wcOrderClass ||
         $order instanceof $wcOrderRefundClass) ||
        (is_subclass_of($order, '\WcElectronInvoice\Providers\Order') ||
         is_subclass_of($order, '\WcElectronInvoice\Providers\OrderRefund'))
    ) {

        // Get POP XML
        $key = get_option('wc_el_inv-secret-api-key');
        // Order ID
        $orderID = $order->get_id();

        // Normalize type for XML (no XML format for receipt)
        if ('receipt' === $choiceType) {
            $choiceType = 'invoice';
        }

        $xml             = false;
        $isXmlFromRemoTe = false;
        $validXML        = false;
        if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction,
                ['feCronJobUploadRequest'])) {
            $url = esc_url_raw(add_query_arg([
                CreateXml::LIST_TYPE => $orderID,
                'provider'           => $provider,
                'key'                => $key,
                'format'             => 'xml',
                'choice_type'        => $choiceType,
                'addon'              => 'fatturesdi',
                'v'                  => time(),
            ], site_url('/' . \WcElectronInvoice\EndPoint\Endpoints::ENDPOINT)));
            $xml = wp_remote_get($url, \WcElectronInvoice\Functions\getSessionCookieArgs('application/xml'));
            $xml = wp_remote_retrieve_body($xml);

            // Is valid XML ?
            $isXmlFromRemoTe = isXml($xml);
            $validXML        = $isXmlFromRemoTe->valid;
        }

        // Fallback for get XML
        if (is_object($isXmlFromRemoTe) && ! $validXML || ! $isXmlFromRemoTe) {
            // Get xml from order
            $xml = getXmlFromOrder($order);
        }

        // Is valid XML ?
        $isXml = isXml($xml);
        $overquotaPayload = \WcElectronInvoice\Functions\extractPopApiOverquotaUiPayload($xml);

        // Not valid ?
        if (! $isXml->valid) {
            if (is_array($overquotaPayload)) {
                wp_send_json($overquotaPayload, (int)($overquotaPayload['status'] ?? 500));
                die();
            }

            $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'xml_invalid');
            $noXmlResponse = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'errors'  => $isXml->errors,
                'xml'     => $isXml->xml,
                'message' => __('Request error: xml not valid', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'code'    => $uploadError['code'],
            );

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                         __('Order: ', WP_FATT_SDI_PEC_TEXTDOMAIN) . '#' . $orderID . "\n" .
                         __('The invoice could not be created, we recommend that you check your order data and try again',
                             WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                         __('Message:', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" . $noXmlResponse['message'] .
                         __('Errors:', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" . print_r($noXmlResponse['errors'], true);
            sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

            // Log
            \WooPoPToSdIPec\Functions\log(print_r($noXmlResponse, true));

            wp_send_json($noXmlResponse);
            die();
        }

        if (! class_exists("\PHPMailer\PHPMailer\PHPMailer")) {
            require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
            require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
            require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
        }

        $smtpPecTo = get_option('wsdipec_smtp_pec_to') ?: 'sdi20@pec.fatturapa.it';
        if ('dev' === WP_FATT_SDI_MODE) {
            $smtpPecTo = WP_FATT_SDI_DEV_PEC_TO;
        }

        if (! \PHPMailer\PHPMailer\PHPMailer::validateAddress($apiData->smtpUser) ||
            ! \PHPMailer\PHPMailer\PHPMailer::validateAddress($smtpPecTo)
        ) {
            $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec',
                'invalid_email_recipient');
            $noSentResponse = array(
                'message' => __('Sender or recipient not valid!', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'orderID' => $orderID,
                'code'    => $uploadError['code'],
            );

            if ('dev' === WP_FATT_SDI_MODE) {
                $noSentResponse['message'] = $noSentResponse['message'] . ' - ' . __('You are in development mode set the constant WP_FATT_SDI_DEV_PEC_TO for testing',
                        WP_FATT_SDI_PEC_TEXTDOMAIN);
            }

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                         __('Order: ', WP_FATT_SDI_PEC_TEXTDOMAIN) . '#' . $orderID . "\n" .
                         sprintf(__("The invoice could not be send because sender or receiver is not a valid email addresses: %s -> %s",
                             WP_FATT_SDI_PEC_TEXTDOMAIN),
                             $apiData->smtpUser,
                             $smtpPecTo
                         );
            sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

            // Log
            \WooPoPToSdIPec\Functions\log(getDateTime() . ' ' . WP_FATT_SDI_NAME . ' ' . print_r($noSentResponse,
                    true));

            wp_send_json($noSentResponse);
            die();
        }

        $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('sdi_pec', 'upload_invoice', (int)$orderID,
            (string)$provider, (string)$choiceType);
        $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision(
            \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0),
            \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest'])
        );
        if (! $lockDecision['continue']) {
            if ($lockDecision['respond_error']) {
                $lockError = \WcElectronInvoice\Functions\mapLockBusyError('sdi_pec', 'upload_invoice');
                wp_send_json(array(
                    'message' => __('Action skipped: another upload process is already running for this order.',
                        WP_FATT_SDI_PEC_TEXTDOMAIN),
                    'orderID' => $orderID,
                    'code'    => $lockError['code'],
                ));
                die();
            }

            return;
        }

        try {
            $mail          = new \PHPMailer\PHPMailer\PHPMailer(true);
            $mail->CharSet = 'utf-8';

            $mail->isSMTP();
            $mail->Host       = $apiData->smtpHost;
            $mail->SMTPSecure = $apiData->smtpSecure;
            $mail->Port       = $apiData->smtpSecure === 'tls' ? 587 : 465;
            $mail->SMTPAuth   = true;

            $mail->Username = $apiData->smtpUser;
            $mail->Password = $apiData->smtpPass;

            $mail->SMTPOptions = array(
                "ssl" => array(
                    "verify_peer"  => true,
                    "cafile"       => WP_FATT_SDI_DIR . "/assets/cacert.pem",
                    "verify_depth" => 5,
                    "peer_name"    => $apiData->smtpHost,
                ),
            );

            $mail->setFrom($apiData->smtpUser);
            $mail->addAddress($smtpPecTo);

            $invoiceNumber = wcOrderinvoiceNumber($orderID);
            $subject       = sprintf(__("Sending Invoice n.%s", WP_FATT_SDI_PEC_TEXTDOMAIN), $invoiceNumber);
            $body          = "<p>" . sprintf(__("Invoice %s", WP_FATT_SDI_PEC_TEXTDOMAIN), $invoiceNumber) . "</p>";

            $progressiveNumber = wcOrderProgressiveFileNumber($orderID, true);
            $vatNumber = preg_replace('/^' . preg_quote(GeneralFields::getGeneralInvoiceOptionCountryState(),
                    '/') . '/',
                '', GeneralFields::getGeneralInvoiceOptionVatNumber()
            );
            // Invoice file name
            $fileName = GeneralFields::getGeneralInvoiceOptionCountryState(). $vatNumber . '_' . $progressiveNumber;

            /**
             * Filter file name
             */
            $fileName = apply_filters('woopopsdi-invoice_filename', $fileName, $vatNumber, $progressiveNumber);

            $mail->Subject = $subject;
            $mail->msgHTML($body, dirname(__FILE__), true);
            $mail->addStringAttachment($xml, $fileName . '.xml');

            if (! $mail->send()) {
                $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'upload_failed');
                $noSentResponse = array(
                    'message' => __('Invoice send error!', WP_FATT_SDI_PEC_TEXTDOMAIN),
                    'orderID' => $orderID,
                    'code'    => $uploadError['code'],
                );

                // Send error email
                $emailBody = __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                             __('Order: ', WP_FATT_SDI_PEC_TEXTDOMAIN) . '#' . $orderID . "\n" .
                             __('The invoice could not be created, we recommend that you check your order data and try again',
                                 WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                             __('Message:', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" . $mail->ErrorInfo;
                sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

                // Log
                \WooPoPToSdIPec\Functions\log(getDateTime() . ' ' . WP_FATT_SDI_NAME . ' ' . print_r($mail->ErrorInfo,
                        true));

                \WcElectronInvoice\Functions\releaseDbLock($lockKey);
                wp_send_json($noSentResponse);
                die();
            }

            if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUploadFileName($fileName)) {
                $order->update_meta('_invoice_sent', 'sent');
                $order->save();

                $responseArgs = array(
                    'message' => sprintf('%s:%s - %s',
                        __('Invoice', WP_FATT_SDI_PEC_TEXTDOMAIN),
                        $fileName,
                        __('Invoice sent successfully!', WP_FATT_SDI_PEC_TEXTDOMAIN)
                    ),
                    'orderID' => $orderID,
                    'code'    => 200,
                );
            } else {
                $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec',
                    'empty_upload_filename');
                $responseArgs = array(
                    'message' => __('Invoice send error: empty invoice file name!', WP_FATT_SDI_PEC_TEXTDOMAIN),
                    'orderID' => $orderID,
                    'code'    => $uploadError['code'],
                );
            }
        } catch (\Exception $e) {
            $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'upload_failed');
            $noSentResponse = array(
                'title'   => __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'errors'  => [$e->getMessage()],
                'message' => __('Request error: invoice could not be sent', WP_FATT_SDI_PEC_TEXTDOMAIN),
                'code'    => $uploadError['code'],
            );

            // Send error email
            $emailBody = __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                         __('Order: ', WP_FATT_SDI_PEC_TEXTDOMAIN) . '#' . $orderID . "\n" .
                         __('The invoice could not be sent because PhpMailer throws an exception',
                             WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" .
                         __('Message:', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" . $e->getMessage() .
                         __('Errors:', WP_FATT_SDI_PEC_TEXTDOMAIN) . "\n" . $e->getTraceAsString();
            sendEmailToAdminForInvoiceCreateError($emailBody, $orderID);

            // Log
            \WooPoPToSdIPec\Functions\log(print_r($noSentResponse, true));

            \WcElectronInvoice\Functions\releaseDbLock($lockKey);
            wp_send_json($noSentResponse);
            die();
        }

        /**
         * Hook upload invoice
         */
        do_action('woopopsdi-upload_invoice',
            $ajaxAction,
            $orderID,
            $fileName,
            $mail
        );

        // Slack notify
        \WooPoPToSdIPec\Functions\slack(
            getDateTime() . ' ' . WP_FATT_SDI_NAME . ' ' . $responseArgs['message'],
            false,
            ':information_source:',
            "\n\n" . "Data:" . "\n" . print_r($responseArgs, true),
        );

        // Log
        \WooPoPToSdIPec\Functions\log(getDateTime() . ' ' . WP_FATT_SDI_NAME . ' ' . print_r($responseArgs,
                true));

        // Cron Job return data
        if ('on' === $scheduledCronJob &&
            \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) &&
            'sent' !== $checkSent
        ) {
            \WcElectronInvoice\Functions\releaseDbLock($lockKey);
            return (object)array(
                'fileName' => $fileName,
                'status'   => $responseArgs['code'],
            );
        }

        \WcElectronInvoice\Functions\releaseDbLock($lockKey);
        wp_send_json($responseArgs);
        die();
    }

    $responseArgs = array(
        'body'    => null,
        'message' => __('uploadInvoice request error', WP_FATT_SDI_PEC_TEXTDOMAIN),
        'code'    => 501,
    );

    // Log
    \WooPoPToSdIPec\Functions\log(getDateTime() . ' ' . WP_FATT_SDI_NAME . ' ' . print_r($responseArgs, true));

    wp_send_json($responseArgs);
    die();
}
