<?php
/**
 * filters.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.
 */

use POPxPMPro\Functions\Utils;
use WcElectronInvoice\WooCommerce\Fields\GeneralFields;

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

//
//  array(
//      'filter'        => '',
//      'callback'      => '',
//      'priority'      => '',
//      'accepted_args' => '',
//  )
//

$filters = array(
    'inc' => array(
        'action' => array(
            /**
             * Save the invoice number
             */
            array(
                'filter'   => array('pmpro_added_order', 'pmpro_updated_order'),
                'callback' => function (\MemberOrder $order) {
                    if (empty($order->id)) {
                        return;
                    }

                    $orderId = (int) $order->id;
                    if (! \POPxPMPro\Functions\Utils::acquirePmproOrderProcessingLock($orderId, 'pmpro_order_persistence')) {
                        return;
                    }

                    try {
                        $currentFormat   = \WcElectronInvoice\Functions\filterInput($_GET, 'format', FILTER_UNSAFE_RAW);
                        $currentProvider = sanitize_key((string)\WcElectronInvoice\Functions\filterInput($_GET, 'provider', FILTER_UNSAFE_RAW));
                        $currentOrderId  = (int)\WcElectronInvoice\Functions\filterInput($_GET, 'shop_order', FILTER_VALIDATE_INT);
                        if (
                            in_array($currentFormat, array('xml', 'pdf'), true)
                            && 'pmpro' === $currentProvider
                            && $currentOrderId === $orderId
                        ) {
                            return;
                        }

                        $orderProvider = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($order->id, 'pmpro');
                        if (! $orderProvider instanceof POPxPMPro\Providers\Order) {
                            return;
                        }

                        // Re-read PMPro order status from DB to avoid stale status objects in hook payloads.
                        $freshPmproOrder = new \MemberOrder($order->id);
                        $currentStatus   = (string)($freshPmproOrder->status ?: $order->status);

                        if ('success' === $currentStatus) {
                            $invoiceMetaKeys = [
                                'billing_choice_type',
                                'billing_invoice_type',
                                'billing_sdi_type',
                                'billing_vat_number',
                                'billing_company',
                                'billing_tax_code',
                            ];
                            $resolveInvoiceMetaValue = static function (string $metaKey) use ($order, $freshPmproOrder) {
                                $postedValue = \WcElectronInvoice\Functions\filterInput($_POST, $metaKey, FILTER_UNSAFE_RAW);
                                if (is_string($postedValue)) {
                                    $postedValue = trim(wp_unslash($postedValue));
                                }
                                if (! empty($postedValue)) {
                                    return sanitize_text_field($postedValue);
                                }

                                if ('billing_company' === $metaKey && isset($freshPmproOrder->billing->company)) {
                                    $company = trim((string)$freshPmproOrder->billing->company);
                                    if ('' !== $company) {
                                        return sanitize_text_field($company);
                                    }
                                }

                                $userID = (int)($order->user_id ?? 0);
                                if ($userID > 0) {
                                    $userValue = get_user_meta($userID, $metaKey, true);
                                    if (is_string($userValue)) {
                                        $userValue = trim($userValue);
                                    }
                                    if ('' !== (string)$userValue) {
                                        return sanitize_text_field((string)$userValue);
                                    }
                                }

                                if ('billing_choice_type' === $metaKey) {
                                    return 'invoice';
                                }

                                return '';
                            };

                            foreach ($invoiceMetaKeys as $metaKey) {
                                $currentValue = get_pmpro_membership_order_meta($order->id, $metaKey, true);
                                if (is_string($currentValue)) {
                                    $currentValue = trim($currentValue);
                                }
                                if ('' !== (string)$currentValue) {
                                    continue;
                                }

                                $resolvedValue = $resolveInvoiceMetaValue($metaKey);
                                if ('' === $resolvedValue) {
                                    continue;
                                }

                                update_pmpro_membership_order_meta($order->id, $metaKey, $resolvedValue);

                                if ((int)($order->user_id ?? 0) > 0 && '' === (string)get_user_meta((int)$order->user_id, $metaKey, true)) {
                                    update_user_meta((int)$order->user_id, $metaKey, $resolvedValue);
                                }
                            }

                            $existingInvoiceDate = $orderProvider->get_meta('order_date_invoice', true) ?: '';
                            $postedInvoiceDate   = null;

                        // Respect a manually provided invoice date when saving the order.
                        if (isset($_POST['order_date_invoice'], $_POST['order_hours_invoice'], $_POST['order_minutes_invoice'])) {
                            $rawInvoiceDate   = sanitize_text_field(wp_unslash($_POST['order_date_invoice']));
                            $rawInvoiceHours  = sanitize_text_field(wp_unslash($_POST['order_hours_invoice']));
                            $rawInvoiceMinute = sanitize_text_field(wp_unslash($_POST['order_minutes_invoice']));

                            if ('' !== $rawInvoiceDate && '' !== $rawInvoiceHours && '' !== $rawInvoiceMinute) {
                                $invoiceHours   = max(0, min(23, (int)$rawInvoiceHours));
                                $invoiceMinutes = max(0, min(59, (int)$rawInvoiceMinute));
                                $invoiceString  = sprintf('%s %02d:%02d', $rawInvoiceDate, $invoiceHours,
                                    $invoiceMinutes);

                                try {
                                    $postedInvoiceDate = (new \DateTime($invoiceString))->format('Y-m-d H:i:s');
                                } catch (\Exception $exception) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement
                                    // Ignore invalid date formats.
                                }
                            }
                        }

                            if ($postedInvoiceDate && $existingInvoiceDate !== $postedInvoiceDate) {
                                update_pmpro_membership_order_meta($order->id, 'order_date_invoice',
                                    $postedInvoiceDate);
                            } elseif ('' === $existingInvoiceDate) {
                                update_pmpro_membership_order_meta($order->id, 'order_date_invoice',
                                    current_time('mysql'));
                            }

                            $invoiceNumber = $orderProvider->get_meta('order_number_invoice', true) ?: null;
                            if (empty($invoiceNumber)) {
                                $numerationType = $orderProvider->get_billing_choice_type() ?: 'invoice';
                                $number         = \WcElectronInvoice\Functions\getNextInvoiceNumber("number_next_{$numerationType}");

                                // Set invoice number and formatted invoice number.
                                if ($number) {
                                    $orderProvider->update_meta('order_number_invoice', $number);
                                    \WcElectronInvoice\Functions\setFormattedInvoiceNumber($orderProvider,
                                        $numerationType);
                                }
                            }

                            \WcElectronInvoice\Xml\CreateXml::createXMLOnProviderOrderCompleted($order->id, 'pmpro');

                            return;
                        }

                        if ('refunded' === $currentStatus) {
                            // Use the dedicated PMPro credit note helper.
                            // It will:
                            // - check that the original invoice was issued
                            // - use the correct credit_note / credit_note_receipt series
                            // - save deterministic refund date
                            // - generate the credit note XML
                            Utils::pmProGenerateCreditNote($order->id, 'pmpro');

                            // Save editable credit note fields from POST (if provided)
                            $orderProvider = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($order->id, 'pmpro');
                            if ($orderProvider instanceof \POPxPMPro\Providers\Order) {
                                // Credit note number
                                $postedRefundNumber = \WcElectronInvoice\Functions\filterInput($_POST, 'refund_number_invoice', FILTER_UNSAFE_RAW);
                                if (is_string($postedRefundNumber) && '' !== trim($postedRefundNumber)) {
                                    $orderProvider->update_meta("refund_number_invoice-{$order->id}", sanitize_text_field($postedRefundNumber));
                                }

                                // Credit note date
                                if (isset($_POST['refunded_date'], $_POST['refund_hours'], $_POST['refund_minutes'])) {
                                    $rawRefundDate   = sanitize_text_field(wp_unslash($_POST['refunded_date']));
                                    $rawRefundHours  = sanitize_text_field(wp_unslash($_POST['refund_hours']));
                                    $rawRefundMinutes = sanitize_text_field(wp_unslash($_POST['refund_minutes']));

                                    if ('' !== $rawRefundDate && '' !== $rawRefundHours && '' !== $rawRefundMinutes) {
                                        $refundHours   = max(0, min(23, (int)$rawRefundHours));
                                        $refundMinutes = max(0, min(59, (int)$rawRefundMinutes));
                                        $refundString  = sprintf('%s %02d:%02d', $rawRefundDate, $refundHours, $refundMinutes);

                                        try {
                                            $refundDateFormatted = (new \DateTime($refundString))->format('Y-m-d H:i:s');
                                            $orderProvider->update_meta('refunded_date', $refundDateFormatted);
                                        } catch (\Exception $exception) {
                                            // Ignore invalid date formats.
                                        }
                                    }
                                }

                                // Credit note sent status
                                $postedRefundSent = \WcElectronInvoice\Functions\filterInput($_POST, 'refund_invoice_sent', FILTER_UNSAFE_RAW);
                                if (is_string($postedRefundSent)) {
                                    $orderProvider->update_meta('refund_invoice_sent', sanitize_text_field($postedRefundSent));
                                }

                                return;
                            }
                        }
                    } finally {
                        \POPxPMPro\Functions\Utils::releasePmproOrderProcessingLock($orderId, 'pmpro_order_persistence');
                    }
                },
                'priority' => 100,
            ),

            /**
             * New renewal order
             */
            array(
                'filter'   => 'pmpro_subscription_payment_completed',
                'callback' => function ( $order ) {
                    $debugTag = 'POP_PMPRO_RENEWAL_DEBUG';

                    if ( ! $order->id ) {
                        \WcElectronInvoice\Functions\log( "{$debugTag} callback invoked with empty order id" );
                        return;
                    }

                    \WcElectronInvoice\Functions\log(
                        sprintf(
                            '%s callback start order_id=%d subscription_transaction_id=%s total=%s subtotal=%s tax=%s',
                            $debugTag,
                            (int) $order->id,
                            (string) ( $order->subscription_transaction_id ?? '' ),
                            (string) ( $order->total ?? '' ),
                            (string) ( $order->subtotal ?? '' ),
                            (string) ( $order->tax ?? '' )
                        )
                    );

                    $isPeppolContext = \WcElectronInvoice\Integrations::isPeppolContext();

                    global $wpdb;

                    // Data to sync on renewal
                    $meta_keys_group1 = [ 'billing_address1', 'billing_address2', 'billing_city', 'billing_postcode', 'billing_country', 'billing_state' ];
                    $meta_keys_group2 = [ 'billing_choice_type', 'billing_invoice_type', 'billing_sdi_type', 'billing_vat_number', 'billing_company', 'billing_tax_code' ];

                    // Try to fetch previous order for this subscription (may provide missing data)
                    $previous_order_id = $wpdb->get_var( $wpdb->prepare( "
		            SELECT id FROM {$wpdb->pmpro_membership_orders}
		            WHERE subscription_transaction_id = %s
		              AND id <> %d
		            ORDER BY id DESC
		            LIMIT 1
		        ", $order->subscription_transaction_id, $order->id ) );
                    $previous_order   = $previous_order_id ? \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder( $previous_order_id, 'pmpro' ) : null;

                    // Helper to pick the best available value: current order (if already set), user meta, previous order meta
                    $pick_value = function ( $meta_key, $order_prop = null ) use ( $order, $previous_order, $previous_order_id ) {
                        $current = '';
                        if ( $order_prop && isset( $order->billing->$order_prop ) ) {
                            $current = $order->billing->$order_prop;
                        } else {
                            $current = get_pmpro_membership_order_meta( $order->id, $meta_key, true );
                        }

                        if ( ! empty( $current ) ) {
                            return $current;
                        }

                        $user_value = $order->user_id ? get_user_meta( $order->user_id, $meta_key, true ) : '';
                        if ( ! empty( $user_value ) ) {
                            return $user_value;
                        }

                        if ( $previous_order_id ) {
                            // Prefer explicit meta; fallback to billing property if available
                            $prev = get_pmpro_membership_order_meta( $previous_order_id, $meta_key, true );
                            if ( empty( $prev ) && $previous_order && $order_prop && isset( $previous_order->billing->$order_prop ) ) {
                                $prev = $previous_order->billing->$order_prop;
                            }

                            if ( ! empty( $prev ) ) {
                                return $prev;
                            }
                        }

                        return '';
                    };

                    // Sync address data (fill blanks from user meta or previous order)
                    $billing_changed = false;
                    if ( ! isset( $order->billing ) ) {
                        $order->billing = new stdClass();
                    }

                    foreach ( $meta_keys_group1 as $meta_key ) {
                        $order_prop_name = str_replace( 'billing_', '', $meta_key );
                        $best_value      = $pick_value( $meta_key, $order_prop_name );

                        if ( $best_value !== '' && ( ! isset( $order->billing->$order_prop_name ) || $order->billing->$order_prop_name !== $best_value ) ) {
                            $order->billing->$order_prop_name = $best_value;
                            $billing_changed                 = true;
                        }
                    }

                    if ( $billing_changed ) {
                        $order->saveOrder();
                        \WcElectronInvoice\Functions\log(
                            sprintf( '%s order billing synced and saved order_id=%d', $debugTag, (int) $order->id )
                        );
                    }

                    // Sync fiscal extras (invoice type, VAT, CF, etc.)
                    foreach ( $meta_keys_group2 as $meta_key ) {
                        $current_order_value = get_pmpro_membership_order_meta( $order->id, $meta_key, true );
                        $best_value          = $pick_value( $meta_key );

                        if ( $best_value !== '' && $current_order_value !== $best_value ) {
                            update_pmpro_membership_order_meta( $order->id, $meta_key, $best_value );
                        }
                    }

                    $renewalInvoiceType = get_pmpro_membership_order_meta( $order->id, 'billing_invoice_type', true ) ?: '';
                    if ( $isPeppolContext && 'private' === $renewalInvoiceType ) {
                        update_pmpro_membership_order_meta( $order->id, 'billing_company', '' );
                        if ( $order->user_id ) {
                            update_user_meta( $order->user_id, 'billing_company', '' );
                        }
                    }

                    // Maybe calculate fees/taxes
                    $enable_tax_calculation = get_option( 'pop_pmpro_enable_tax_calculation' );
                    if ( $enable_tax_calculation ) {
                        \WcElectronInvoice\Functions\log(
                            sprintf( '%s renewal tax calculation enabled order_id=%d', $debugTag, (int) $order->id )
                        );
                        Utils::pmProAdjustRenewalTaxes( $order );
                    } else {
                        \WcElectronInvoice\Functions\log(
                            sprintf(
                                '%s renewal tax calculation disabled order_id=%d is_italian_shop=%s',
                                $debugTag,
                                (int) $order->id,
                                GeneralFields::isItalianShop() ? 'yes' : 'no'
                            )
                        );
                        if ( GeneralFields::isItalianShop() ) {
                            // todo calcolare a ritroso... Bollo se attivo... Rivalsa se libero professionista e se è attiva.
                            Utils::pmProSaveFeesRenewal( $order );
                        }
                    }

                    // Calculate natura / rif normativa
                    $country             = $order->billing->country ?: false;
                    $vatNumber           = get_pmpro_membership_order_meta( $order->id, 'billing_vat_number', true ) ?: '';
                    $customerInvoiceType = get_pmpro_membership_order_meta( $order->id, 'billing_invoice_type', true ) ?: '';
                    $maskedVat           = '';
                    if ( ! empty( $vatNumber ) ) {
                        $maskedVat = strlen( $vatNumber ) > 4 ? str_repeat( '*', max( 0, strlen( $vatNumber ) - 4 ) ) . substr( $vatNumber, -4 ) : '****';
                    }

                    \WcElectronInvoice\Functions\log(
                        sprintf(
                            '%s before natura/rif order_id=%d country=%s invoice_type=%s vat=%s',
                            $debugTag,
                            (int) $order->id,
                            (string) $country,
                            (string) $customerInvoiceType,
                            (string) $maskedVat
                        )
                    );

                    Utils::pmProSaveOrderNaturaRifNorm( $order, $country, $vatNumber, $customerInvoiceType );

                    \WcElectronInvoice\Functions\log(
                        sprintf(
                            '%s callback end order_id=%d total=%s subtotal=%s tax=%s',
                            $debugTag,
                            (int) $order->id,
                            (string) ( $order->total ?? '' ),
                            (string) ( $order->subtotal ?? '' ),
                            (string) ( $order->tax ?? '' )
                        )
                    );
                },
                'priority' => 10,
            ),
        ),
        'filter' => array(
            array(
                'filter'        => 'wc_el_inv-next_number_already_assigned',
                'callback'      => 'POPxPMPro\\Functions\\Utils::pmproNextNumberAlreadyAssigned',
                'priority'      => 20,
                'accepted_args' => 3,
            ),
            array(
                'filter'        => 'wc_el_inv-next_number_last_assigned_candidate_for_series',
                'callback'      => 'POPxPMPro\\Functions\\Utils::pmproLastAssignedCandidateForSeries',
                'priority'      => 20,
                'accepted_args' => 2,
            ),
            array(
                'filter'        => 'wc_el_inv-next_number_last_assigned_for_series',
                'callback'      => 'POPxPMPro\\Functions\\Utils::pmproLastAssignedNumberForSeries',
                'priority'      => 20,
                'accepted_args' => 3,
            ),
        ),
    ),
);

add_filter('wc_el_inv-filters', function ($filtersInc) use ($filters) {

    $filtersInc['inc']['action'] = array_merge($filtersInc['inc']['action'], $filters['inc']['action']);
    $filtersInc['inc']['filter'] = array_merge($filtersInc['inc']['filter'], $filters['inc']['filter']);

    return $filtersInc;
}, 10, 1);

return $filters;
