<?php

namespace Devikit\Infakt;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * NIP Field Handler
 * Adds NIP field to checkout if not provided by another plugin
 */
class NipField {

    /**
     * Constructor
     */
    public function __construct() {
        add_action( 'wp', [ $this, 'maybe_add_nip_field' ] );
        add_filter( 'woocommerce_customer_meta_fields', [ $this, 'add_nip_to_user_profile' ] );
        add_filter( 'woocommerce_admin_billing_fields', [ $this, 'add_editable_nip_to_admin_billing' ], 20 );
        add_action( 'woocommerce_process_shop_order_meta', [ $this, 'save_admin_order_nip' ], 10, 2 );

        // Direct WP admin user profile NIP save.
        add_action( 'personal_options_update', [ $this, 'save_user_profile_nip' ] );
        add_action( 'edit_user_profile_update', [ $this, 'save_user_profile_nip' ] );

        // Sync NIP before admin profile is rendered.
        add_action( 'load-profile.php', [ $this, 'sync_nip_before_profile_display' ] );
        add_action( 'load-user-edit.php', [ $this, 'sync_nip_before_profile_display' ] );

        // Include NIP in the AJAX response when admin selects a customer on the order edit screen.
        add_filter( 'woocommerce_found_customer_details', [ $this, 'add_nip_to_customer_details' ], 10, 2 );

        // Admin scripts: populate NIP field when a customer is loaded on the order edit screen.
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_order_scripts' ] );

        // Own AJAX action to get customer NIP.
        add_action( 'wp_ajax_devikit_infakt_get_customer_nip', [ $this, 'ajax_get_customer_nip' ] );
    }

    /**
     * Maybe add NIP field if not exists from other plugin
     */
    public function maybe_add_nip_field() {
        // Check settings
        $settings = get_option( 'devikit_infakt_settings', [] );
        $add_nip = isset( $settings['add_nip_field'] ) ? $settings['add_nip_field'] : 'yes';
        
        if ( $add_nip !== 'yes' ) {
            return;
        }

        // Check if another NIP plugin is active
        if ( $this->is_nip_field_available() ) {
            return;
        }

        // Add our NIP field
        add_filter( 'woocommerce_billing_fields', [ $this, 'add_nip_field' ], 20 );
        add_action( 'woocommerce_checkout_update_order_meta', [ $this, 'save_nip_field' ] );
        add_filter( 'woocommerce_order_formatted_billing_address', [ $this, 'display_nip_in_address' ], 10, 2 );
        
        // Only add {nip} placeholder system if NOT using blocks checkout
        // Blocks checkout uses direct HTML modification instead (see CheckoutBlocks.php)
        if ( ! $this->site_uses_block_checkout() ) {
            add_filter( 'woocommerce_formatted_address_replacements', [ $this, 'address_replacements' ], 10, 2 );
            add_filter( 'woocommerce_localisation_address_formats', [ $this, 'address_formats' ] );
        }
        
    }

    /**
     * Check if NIP field is already available from another plugin
     * 
     * @return bool
     */
    private function is_nip_field_available() {
        // Check for popular NIP plugins
        $nip_plugins = [
            'nip-field-for-woocommerce/nip-field-for-woocommerce.php',
            'nip-field-for-woocommerce-pro/nip-field-for-woocommerce-pro.php',
            'nip-field-woocommerce/nip-field-woocommerce.php',
            'nip-field-woocommerce-pro/nip-field-woocommerce-pro.php',
            'flexible-invoices/flexible-invoices.php',
            'woocommerce-eu-vat-number/woocommerce-eu-vat-number.php',
        ];

        foreach ( $nip_plugins as $plugin ) {
            if ( is_plugin_active( $plugin ) ) {
                return true;
            }
        }

        // Check if billing_nip field is already registered
        // This will be checked at checkout rendering time
        return apply_filters( 'devikit_infakt_nip_field_available', false );
    }

    /**
     * Add NIP field to billing fields
     */
    public function add_nip_field( $fields ) {
        // Check if billing_nip already exists
        if ( isset( $fields['billing_nip'] ) ) {
            return $fields;
        }

        // Get settings
        $settings = get_option( 'devikit_infakt_settings', [] );
        $nip_required = isset( $settings['nip_required'] ) && $settings['nip_required'] === 'yes';

        $fields['billing_nip'] = [
            'type'        => 'text',
            'label'       => __( 'NIP', 'invoicing-integration-for-infakt-and-woocommerce' ),
            'placeholder' => __( 'Enter NIP if you need an invoice', 'invoicing-integration-for-infakt-and-woocommerce' ),
            'required'    => $nip_required,
            'class'       => [ 'form-row-wide' ],
            'priority'    => 35, // After company name
            'maxlength'   => 13,
        ];

        return $fields;
    }

    /**
     * Save NIP field to order meta
     */
    public function save_nip_field( $order_id ) {
        if ( ! empty( $_POST['billing_nip'] ) ) {
            $nip = sanitize_text_field( wp_unslash( $_POST['billing_nip'] ) );
            
            // Get billing country from POST or order
            $billing_country = '';
            if ( ! empty( $_POST['billing_country'] ) ) {
                $billing_country = sanitize_text_field( wp_unslash( $_POST['billing_country'] ) );
            } else {
                $order = wc_get_order( $order_id );
                if ( $order ) {
                    $billing_country = $order->get_billing_country();
                }
            }
            
            // Clean NIP based on country
            // For PL: remove all non-digits (Polish NIP is 10 digits only)
            // For foreign countries: remove only spaces and dashes (preserve country prefix like PL, DE, GB, etc.)
            if ( $billing_country === 'PL' ) {
                $nip = preg_replace( '/[^0-9]/', '', $nip );
            } else {
                // For foreign countries, preserve country prefix and remove only spaces/dashes
                $nip = preg_replace( '/[\s\-]/', '', $nip );
            }
            
            $order = wc_get_order( $order_id );
            $order->update_meta_data( '_billing_nip', $nip );
            $order->save();
        }
    }

    /**
     * Display NIP in formatted address
     */
    public function display_nip_in_address( $address, $order ) {
        $nip = $order->get_meta( '_billing_nip' );
        if ( $nip ) {
            $address['nip'] = $nip;
        }
        return $address;
    }

    /**
     * Address replacements
     */
    public function address_replacements( $replacements, $args ) {
        $replacements['{nip}'] = isset( $args['nip'] ) ? __( 'NIP: ', 'invoicing-integration-for-infakt-and-woocommerce' ) . $args['nip'] : '';
        return $replacements;
    }

    /**
     * Address formats
     */
    public function address_formats( $formats ) {
        foreach ( $formats as $country => $format ) {
            $formats[ $country ] = $format . "\n{nip}";
        }
        return $formats;
    }

    /**
     * Add editable NIP field to admin order billing section (same as address fields)
     */
    public function add_editable_nip_to_admin_billing( $fields ) {
        if ( isset( $fields['nip'] ) ) {
            return $fields;
        }
        $fields['nip'] = [
            'label' => __( 'VAT Number', 'invoicing-integration-for-infakt-and-woocommerce' ),
            'show'  => true,
        ];
        return $fields;
    }

    /**
     * Save NIP when order is updated in admin
     *
     * @param int       $order_id Order ID.
     * @param \WC_Order $order    Order object (optional, for HPOS compatibility).
     */
    public function save_admin_order_nip( $order_id, $order = null ) {
        // Hook may pass WP_Post in some WooCommerce versions - always use WC_Order
        if ( ! $order instanceof \WC_Order ) {
            $order = wc_get_order( $order_id );
        }
        if ( ! $order ) {
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Called by WooCommerce save hook which verifies nonce
        if ( isset( $_POST['_billing_nip'] ) ) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Called by WooCommerce save hook which verifies nonce
            $nip = sanitize_text_field( wp_unslash( $_POST['_billing_nip'] ) );
            $order->update_meta_data( '_billing_nip', $nip );
            $order->save();
        }
    }

    /**
     * Add NIP field to user profile in admin
     *
     * @param array $fields Customer meta fields
     * @return array Modified fields
     */
    public function add_nip_to_user_profile( $fields ) {
        // Check if another NIP plugin already added the field
        if ( isset( $fields['billing']['fields']['billing_nip'] ) ) {
            return $fields;
        }
        
        // Check settings
        $settings = get_option( 'devikit_infakt_settings', [] );
        $add_nip = isset( $settings['add_nip_field'] ) ? $settings['add_nip_field'] : 'yes';
        
        if ( $add_nip !== 'yes' ) {
            return $fields;
        }
        
        // Add NIP field to billing address section
        if ( isset( $fields['billing']['fields'] ) ) {
            if ( isset( $fields['billing']['fields']['billing_company'] ) ) {
                // Insert NIP field after Company Name field
                $new_fields = [];
                
                foreach ( $fields['billing']['fields'] as $key => $field ) {
                    $new_fields[ $key ] = $field;
                    
                    if ( $key === 'billing_company' ) {
                        // Add NIP field after Company Name field
                        $new_fields['billing_nip'] = [
                            'label'       => __( 'NIP', 'invoicing-integration-for-infakt-and-woocommerce' ),
                            'description' => '',
                        ];
                    }
                }
                
                $fields['billing']['fields'] = $new_fields;
            } else {
                // If Company Name field doesn't exist, add NIP field at the beginning
                $fields['billing']['fields'] = array_merge(
                    [
                        'billing_nip' => [
                            'label'       => __( 'NIP', 'invoicing-integration-for-infakt-and-woocommerce' ),
                            'description' => '',
                        ],
                    ],
                    $fields['billing']['fields']
                );
            }
        }
        
        return $fields;
    }

    /**
     * Check if site uses block checkout
     */
    private function site_uses_block_checkout() {
        $checkout_page_id = wc_get_page_id( 'checkout' );
        if ( ! $checkout_page_id ) {
            return false;
        }
        
        // Check if checkout page has blocks
        return has_block( 'woocommerce/checkout', $checkout_page_id );
    }

    /**
     * Save NIP directly from the WP admin user profile form.
     *
     * @param int $user_id User ID being saved.
     */
    public function save_user_profile_nip( $user_id ) {
        if ( ! current_user_can( 'edit_user', $user_id ) ) {
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- WP core verifies nonce for profile/user-edit forms
        if ( ! isset( $_POST['billing_nip'] ) ) {
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        $nip = sanitize_text_field( wp_unslash( $_POST['billing_nip'] ) );

        update_user_meta( $user_id, 'billing_nip', $nip );
        update_user_meta( $user_id, 'devikit-infakt/billing_nip', $nip );
        update_user_meta( $user_id, '_wc_billing/devikit-infakt/billing_nip', $nip );
    }

    /**
     * Sync NIP meta keys before the WP admin user profile page is rendered.
     */
    public function sync_nip_before_profile_display() {
        $user_id = isset( $_REQUEST['user_id'] ) ? absint( $_REQUEST['user_id'] ) : get_current_user_id();
        if ( ! $user_id ) {
            return;
        }

        $billing_nip = (string) get_user_meta( $user_id, 'billing_nip', true );
        $wc_nip      = (string) get_user_meta( $user_id, 'devikit-infakt/billing_nip', true );
        $wc_nip_pref = (string) get_user_meta( $user_id, '_wc_billing/devikit-infakt/billing_nip', true );

        $latest = ! empty( $wc_nip_pref ) ? $wc_nip_pref : ( ! empty( $wc_nip ) ? $wc_nip : $billing_nip );

        if ( $billing_nip === $latest && $wc_nip === $latest ) {
            return;
        }

        update_user_meta( $user_id, 'billing_nip', $latest );
        update_user_meta( $user_id, 'devikit-infakt/billing_nip', $latest );
        update_user_meta( $user_id, '_wc_billing/devikit-infakt/billing_nip', $latest );
    }

    /**
     * Inject billing NIP into the WooCommerce AJAX customer-details response.
     *
     * @param array $data    Customer data array.
     * @param int   $user_id Customer user ID.
     * @return array
     */
    public function add_nip_to_customer_details( $data, $user_id ) {
        $nip = get_user_meta( $user_id, 'billing_nip', true );

        if ( empty( $nip ) ) {
            foreach ( $this->get_external_nip_user_meta_keys() as $key ) {
                $nip = get_user_meta( $user_id, $key, true );
                if ( ! empty( $nip ) ) {
                    break;
                }
            }
        }

        if ( ! empty( $nip ) ) {
            $data['billing']['nip'] = esc_attr( $nip );
        }

        return $data;
    }

    /**
     * Enqueue admin JavaScript for the order edit screen.
     *
     * @param string $hook Current admin page hook.
     */
    public function enqueue_admin_order_scripts( $hook ) {
        $order_hooks = [ 'post.php', 'post-new.php', 'woocommerce_page_wc-orders' ];
        if ( ! in_array( $hook, $order_hooks, true ) ) {
            return;
        }

        $screen = get_current_screen();
        if ( ! $screen ) {
            return;
        }

        $is_order_screen = in_array( $screen->id, [ 'shop_order', 'woocommerce_page_wc-orders' ], true );
        if ( ! $is_order_screen ) {
            return;
        }

        wp_enqueue_script(
            'devikit-infakt-admin-order-nip',
            DEVIKIT_INFAKT_URL . 'assets/js/admin-order-nip.js',
            [ 'jquery', 'wc-admin-order-meta-boxes' ],
            DEVIKIT_INFAKT_VERSION,
            true
        );

        wp_localize_script(
            'devikit-infakt-admin-order-nip',
            'devikit_infakt_admin_nip',
            [
                'ajax_url' => admin_url( 'admin-ajax.php' ),
                'nonce'    => wp_create_nonce( 'devikit_infakt_admin_nip' ),
            ]
        );
    }

    /**
     * AJAX handler: return the NIP for a given customer.
     */
    public function ajax_get_customer_nip() {
        check_ajax_referer( 'devikit_infakt_admin_nip', 'nonce' );

        if ( ! current_user_can( 'manage_woocommerce' ) ) {
            wp_send_json_error( 'Unauthorized', 403 );
        }

        $user_id = isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0;
        if ( ! $user_id ) {
            wp_send_json_error( 'Invalid user ID', 400 );
        }

        $nip = '';
        foreach ( $this->get_external_nip_user_meta_keys() as $key ) {
            $nip = (string) get_user_meta( $user_id, $key, true );
            if ( ! empty( $nip ) ) {
                break;
            }
        }

        if ( empty( $nip ) ) {
            $nip = (string) get_user_meta( $user_id, 'billing_nip', true );
        }

        wp_send_json_success( [ 'nip' => $nip ] );
    }

    /**
     * Returns user meta keys used by known external NIP plugins.
     *
     * @return string[]
     */
    private function get_external_nip_user_meta_keys() {
        return [
            '_wc_billing/devikit-infakt/billing_nip',
            'devikit-infakt/billing_nip',
            'nip-field-wc/billing_nip',
            '_wc_billing/nip-field-wc/billing_nip',
            'nip-field-woocommerce/billing_nip',
            '_wc_billing/nip-field-woocommerce/billing_nip',
        ];
    }
}
