<?php

defined( 'ABSPATH' ) or die( 'Forbidden' );

/**
 * SCPWOO_Fields
 *
 * Handles all WooCommerce checkout field customizations:
 * state/city ordering, city select, neighborhood select, landmark, and custom address fields.
 */
class SCPWOO_Fields {

    /**
     * Constructor - registers all field hooks.
     */
    public function __construct() {
        add_filter( 'woocommerce_get_country_locale', array( $this, 'override_country_locale' ) );
        add_filter( 'woocommerce_billing_fields', array( $this, 'add_custom_address_fields' ), 20, 1 );
        add_filter( 'woocommerce_shipping_fields', array( $this, 'add_custom_address_fields' ), 20, 1 );
        add_filter( 'woocommerce_billing_fields', array( $this, 'wc_billing_fields' ), 10, 2 );
        add_filter( 'woocommerce_shipping_fields', array( $this, 'wc_shipping_fields' ), 10, 2 );
        add_filter( 'woocommerce_form_field_city', array( $this, 'wc_form_field_city' ), 10, 4 );
        add_filter( 'woocommerce_billing_fields', array( $this, 'wc_billing_neighborhood_fields' ), 25, 2 );
        add_filter( 'woocommerce_shipping_fields', array( $this, 'wc_shipping_neighborhood_fields' ), 25, 2 );
        add_filter( 'woocommerce_form_field_neighborhood', array( $this, 'wc_form_field_neighborhood' ), 10, 4 );
        add_filter( 'woocommerce_default_address_fields', array( $this, 'wc_change_state_and_city_order' ) );

        // Cart shipping calculator — inject neighborhood field
        add_action( 'woocommerce_after_shipping_calculator', array( $this, 'cart_calculator_neighborhood_field' ) );

        // Coherence check: clear stale saved city/neighborhood on checkout load
        add_filter( 'woocommerce_checkout_get_value', array( $this, 'validate_saved_address_value' ), 10, 2 );
    }

    /**
     * Validate saved city/neighborhood values against current geo data.
     *
     * When a returning customer loads checkout, WooCommerce pre-fills fields
     * from user meta. If the admin has since deleted or renamed a city or
     * neighborhood, the saved value would be invalid. This filter detects
     * stale values and clears them so the customer must re-select.
     *
     * @param mixed  $value The saved value from user meta.
     * @param string $input The field key (e.g. 'billing_city', 'billing_neighborhood').
     * @return mixed The original value if still valid, or empty string if stale.
     */
    public function validate_saved_address_value( $value, string $input ) {
        // Only check city and neighborhood fields
        if ( empty( $value ) || ! is_string( $value ) ) {
            return $value;
        }

        $plugin = SCPWOO_States_Places::instance();
        if ( ! $plugin ) {
            return $value;
        }

        // Determine which address type and field
        $is_city         = in_array( $input, array( 'billing_city', 'shipping_city' ), true );
        $is_neighborhood = in_array( $input, array( 'billing_neighborhood', 'shipping_neighborhood' ), true );

        if ( ! $is_city && ! $is_neighborhood ) {
            return $value;
        }

        $prefix  = strpos( $input, 'billing_' ) === 0 ? 'billing' : 'shipping';
        $customer = WC()->customer;
        if ( ! $customer ) {
            return $value;
        }

        $country = $prefix === 'billing' ? $customer->get_billing_country() : $customer->get_shipping_country();
        if ( ! $plugin->is_supported_country( $country ) ) {
            return $value;
        }

        $state = $prefix === 'billing' ? $customer->get_billing_state() : $customer->get_shipping_state();

        if ( $is_city && ! empty( $state ) ) {
            $places = $plugin->get_places( $country );
            if ( is_array( $places ) && isset( $places[ $state ] ) ) {
                // Resolve alias before checking
                $resolved = SCPWOO_Aliases::resolve_city( $value, $country );
                if ( ! in_array( $resolved, $places[ $state ], true ) ) {
                    return ''; // City no longer exists in this region
                }
            }
        }

        if ( $is_neighborhood ) {
            $city = $prefix === 'billing' ? $customer->get_billing_city() : $customer->get_shipping_city();
            if ( ! empty( $city ) ) {
                $neighborhoods = $plugin->get_neighborhoods( $country );
                if ( is_array( $neighborhoods ) && isset( $neighborhoods[ $city ] ) ) {
                    $resolved = SCPWOO_Aliases::resolve_neighborhood( $value, $city, $country );
                    $hoods_lower = array_map( 'mb_strtolower', $neighborhoods[ $city ] );
                    if ( ! in_array( mb_strtolower( $resolved ), $hoods_lower, true ) ) {
                        return ''; // Neighborhood no longer exists in this city
                    }
                }
            }
        }

        return $value;
    }

    /**
     * Change the order of State and City fields
     * @param mixed $fields
     * @return mixed
     */
    public function wc_change_state_and_city_order( array $fields ): array {
        $fields['state']['priority'] = 70;
        $fields['city']['priority'] = 80;

        return $fields;
    }

    /**
     * Make postcode optional and hidden for supported countries (e.g. Cameroon).
     */
    public function override_country_locale( array $locale ): array {
        $plugin = SCPWOO_States_Places::instance();
        if ( ! $plugin ) {
            return $locale;
        }

        $supported = $plugin->get_supported_countries();

        foreach ( $supported as $cc ) {
            $locale[ $cc ]['postcode'] = array(
                'required' => false,
                'hidden'   => true,
            );
            $locale[ $cc ]['address_1'] = array(
                'label'       => __( 'Adresse complète', 'afriregions-city-neighborhood-for-woocommerce' ),
                'placeholder' => __( 'Ex. : En face de la station Total, bâtiment bleu au 2e étage...', 'afriregions-city-neighborhood-for-woocommerce' ),
                'required'    => false,
            );
            $locale[ $cc ]['address_2'] = array(
                'hidden'   => true,
                'required' => false,
            );
        }

        return $locale;
    }

    /**
     * Add custom address fields (Neighborhood)
     * @param mixed $fields
     * @return mixed
     */
    public function add_custom_address_fields( array $fields ): array {
        $field_prefix = str_contains( current_filter(), 'billing' ) ? 'billing' : 'shipping';

        // Always add neighborhood/landmark fields if the plugin is active
        // (init() already checked that at least one allowed country is supported).
        // The JS handles showing select vs text input based on the current country.
        $plugin = SCPWOO_States_Places::instance();
        if ( ! $plugin || ! $plugin->has_supported_allowed_country() ) {
            return $fields;
        }

        // Neighborhood field
        $fields[$field_prefix . '_neighborhood'] = array(
            'label'        => __('Neighborhood', 'afriregions-city-neighborhood-for-woocommerce'),
            'placeholder'  => __('E.g.: Bastos, Akwa, Omnisport...', 'afriregions-city-neighborhood-for-woocommerce'),
            'required'     => false,
            'class'        => array('form-row-wide'),
            'priority'     => 85,
            'autocomplete' => 'address-level3',
        );

        return $fields;
    }

    /**
     * Modify billing field
     * @param mixed $fields
     * @param mixed $country
     * @return mixed
     */
    public function wc_billing_fields( array $fields, string $country ): array {
        $fields['billing_city']['type'] = 'city';
        $fields['billing_city']['class'][] = 'form-row-wide';
        $fields['billing_city']['autocomplete'] = 'address-level2';

        if ( $this->has_single_active_country() ) {
            $fields['billing_country']['class'][] = 'scpwoo-country-single';
        }

        return apply_filters( 'scpwoo_billing_fields', $fields, $country );
    }

    /**
     * Modify shipping field
     * @param mixed $fields
     * @param mixed $country
     * @return mixed
     */
    public function wc_shipping_fields( array $fields, string $country ): array {
        $fields['shipping_city']['type'] = 'city';
        $fields['shipping_city']['class'][] = 'form-row-wide';
        $fields['shipping_city']['autocomplete'] = 'address-level2';

        if ( $this->has_single_active_country() ) {
            $fields['shipping_country']['class'][] = 'scpwoo-country-single';
        }

        return apply_filters( 'scpwoo_shipping_fields', $fields, $country );
    }

    /**
     * Implement places/city field
     * @param mixed $field
     * @param string $key
     * @param mixed $args
     * @param string $value
     * @return mixed
     */
    public function wc_form_field_city( string $field, string $key, array $args, $value ): string {
        // Do we need a clear div?
        if ( ( ! empty( $args['clear'] ) ) ) {
            $after = '<div class="clear"></div>';
        } else {
            $after = '';
        }

        // Required markup
        if ( $args['required'] ) {
            $args['class'][] = 'validate-required';
            $required = ' <abbr class="required" title="' . esc_attr__( 'required', 'afriregions-city-neighborhood-for-woocommerce'  ) . '">*</abbr>';
        } else {
            $required = '';
        }

        // Custom attribute handling
        $custom_attributes = array();

        if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
            foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
                $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
            }
        }

        // Validate classes
        if ( ! empty( $args['validate'] ) ) {
            foreach( $args['validate'] as $validate ) {
                $args['class'][] = 'validate-' . $validate;
            }
        }

        // field p and label
        $field  = '<p class="form-row ' . esc_attr( implode( ' ', $args['class'] ) ) .'" id="' . esc_attr( $args['id'] ) . '_field">';
        if ( $args['label'] ) {
            $field .= '<label for="' . esc_attr( $args['id'] ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) .'">' . esc_html( $args['label'] ) . $required . '</label>';
        }

        // Get Country
        $country_key = $key === 'billing_city' ? 'billing_country' : 'shipping_country';
        $current_cc  = '';
        if ( function_exists( 'WC' ) && WC()->checkout ) {
            $current_cc = WC()->checkout->get_value( $country_key );
        }

        $state_key = $key === 'billing_city' ? 'billing_state' : 'shipping_state';
        $current_sc  = '';
        if ( function_exists( 'WC' ) && WC()->checkout ) {
            $current_sc = WC()->checkout->get_value( $state_key );
        }

        // Get country places
        $plugin = SCPWOO_States_Places::instance();
        $places = $plugin ? $plugin->get_places( $current_cc ) : null;

        if ( is_array( $places ) ) {

            // Expose data-loaded-state so JS can skip the AJAX cascade on page load
            // when PHP has already rendered the correct city list for this state.
            if ( $current_sc && array_key_exists( $current_sc, $places ) ) {
                $dropdown_places  = $places[ $current_sc ];
                $loaded_state_att = ' data-loaded-state="' . esc_attr( $current_sc ) . '"';
            } else {
                $dropdown_places  = $places;
                $loaded_state_att = '';
            }

            $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="city_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) .'" ' . implode( ' ', $custom_attributes ) . $loaded_state_att . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" aria-label="' . esc_attr( $args['label'] ) . '" aria-required="' . ( $args['required'] ? 'true' : 'false' ) . '">';

            $field .= '<option value="">'. __( 'Select an option&hellip;', 'afriregions-city-neighborhood-for-woocommerce' ) .'</option>';

            foreach ( $dropdown_places as $city_name ) {
                if ( ! is_array( $city_name ) ) {
                    $field .= '<option value="' . esc_attr( $city_name ) . '" '.selected( $value, $city_name, false ) . '>' . esc_html( $city_name ) .'</option>';
                }
            }

            $field .= '</select>';

        } else {

            $field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) .'" value="' . esc_attr( $value ) . '"  placeholder="' . esc_attr( $args['placeholder'] ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" aria-label="' . esc_attr( $args['label'] ) . '" aria-required="' . ( $args['required'] ? 'true' : 'false' ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
        }

        // field description and close wrapper
        if ( $args['description'] ) {
            $field .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
        }

        $field .= '</p>' . $after;

        return $field;
    }

    /**
     * Transform the billing neighborhood field to type 'neighborhood'
     * @param mixed $fields
     * @param mixed $country
     * @return mixed
     */
    public function wc_billing_neighborhood_fields( array $fields, string $country ): array {
        if ( isset( $fields['billing_neighborhood'] ) ) {
            $fields['billing_neighborhood']['type'] = 'neighborhood';
            $fields['billing_neighborhood']['class'][] = 'form-row-wide';
        }
        return apply_filters( 'scpwoo_billing_neighborhood_fields', $fields, $country );
    }

    /**
     * Transform the shipping neighborhood field to type 'neighborhood'
     * @param mixed $fields
     * @param mixed $country
     * @return mixed
     */
    public function wc_shipping_neighborhood_fields( array $fields, string $country ): array {
        if ( isset( $fields['shipping_neighborhood'] ) ) {
            $fields['shipping_neighborhood']['type'] = 'neighborhood';
            $fields['shipping_neighborhood']['class'][] = 'form-row-wide';
        }
        return apply_filters( 'scpwoo_shipping_neighborhood_fields', $fields, $country );
    }

    /**
     * Render the neighborhood field as select or input based on available data
     * @param mixed $field
     * @param string $key
     * @param mixed $args
     * @param string $value
     * @return mixed
     */
    public function wc_form_field_neighborhood( string $field, string $key, array $args, $value ): string {
        // Clear div if necessary
        if ( ( ! empty( $args['clear'] ) ) ) {
            $after = '<div class="clear"></div>';
        } else {
            $after = '';
        }

        // Required markup
        if ( $args['required'] ) {
            $args['class'][] = 'validate-required';
            $required = ' <abbr class="required" title="' . esc_attr__( 'required', 'afriregions-city-neighborhood-for-woocommerce' ) . '">*</abbr>';
        } else {
            $required = '';
        }

        // Custom attributes
        $custom_attributes = array();
        if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
            foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
                $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
            }
        }

        // Validation classes
        if ( ! empty( $args['validate'] ) ) {
            foreach ( $args['validate'] as $validate ) {
                $args['class'][] = 'validate-' . $validate;
            }
        }

        // Wrapper and label
        $field = '<p class="form-row ' . esc_attr( implode( ' ', $args['class'] ) ) . '" id="' . esc_attr( $args['id'] ) . '_field">';
        if ( $args['label'] ) {
            $field .= '<label for="' . esc_attr( $args['id'] ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . esc_html( $args['label'] ) . $required . '</label>';
        }

        // Get current country and city
        $country_key = $key === 'billing_neighborhood' ? 'billing_country' : 'shipping_country';
        $current_cc  = '';
        if ( function_exists( 'WC' ) && WC()->checkout ) {
            $current_cc = WC()->checkout->get_value( $country_key );
        }

        $city_key = $key === 'billing_neighborhood' ? 'billing_city' : 'shipping_city';
        $current_city = '';
        if ( function_exists( 'WC' ) && WC()->checkout ) {
            $current_city = WC()->checkout->get_value( $city_key );
        }

        // Get neighborhoods for the country
        $plugin = SCPWOO_States_Places::instance();
        $country_neighborhoods = $plugin ? $plugin->get_neighborhoods( $current_cc ) : null;

        if ( is_array( $country_neighborhoods ) && $current_city && isset( $country_neighborhoods[ $current_city ] ) ) {
            $dropdown_neighborhoods = $country_neighborhoods[ $current_city ];

            $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="neighborhood_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-loaded-city="' . esc_attr( $current_city ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" aria-label="' . esc_attr( $args['label'] ) . '" aria-required="' . ( $args['required'] ? 'true' : 'false' ) . '">';
            $field .= '<option value="">' . __( 'Select an option&hellip;', 'afriregions-city-neighborhood-for-woocommerce' ) . '</option>';

            foreach ( $dropdown_neighborhoods as $neighborhood_name ) {
                $field .= '<option value="' . esc_attr( $neighborhood_name ) . '" ' . selected( $value, $neighborhood_name, false ) . '>' . esc_html( $neighborhood_name ) . '</option>';
            }

            $field .= '</select>';
        } else {
            $field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" aria-label="' . esc_attr( $args['label'] ) . '" aria-required="' . ( $args['required'] ? 'true' : 'false' ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
        }

        // Description and close wrapper
        if ( $args['description'] ) {
            $field .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
        }

        $field .= '</p>' . $after;

        return $field;
    }

    /**
     * Output a hidden marker so place-select.js knows to inject
     * the neighborhood field into the cart shipping calculator.
     *
     * All logic is handled in the enqueued JS — no inline script.
     */
    public function cart_calculator_neighborhood_field(): void {
        $plugin = SCPWOO_States_Places::instance();
        if ( ! $plugin || ! $plugin->has_supported_allowed_country() ) {
            return;
        }
        // Hidden marker — place-select.js detects this and injects the select
        echo '<span id="scpwoo-calc-neighborhood-marker" class="scpwoo-hidden" aria-hidden="true"></span>';
    }

    /**
     * Returns true when exactly one supported country is active in WooCommerce.
     * Used to auto-hide the country field — no point showing it if there's no choice.
     */
    private function has_single_active_country(): bool {
        $plugin = SCPWOO_States_Places::instance();
        if ( ! $plugin || ! function_exists( 'WC' ) || ! WC()->countries ) {
            return false;
        }
        $supported = $plugin->get_supported_countries();
        $sell_to   = get_option( 'woocommerce_allowed_countries', 'all' );

        if ( 'specific' === $sell_to ) {
            $specific = (array) get_option( 'woocommerce_specific_allowed_countries', array() );
            $active   = array_intersect( $supported, array_map( 'strtoupper', $specific ) );
            return 1 === count( $active );
        }

        // 'all' or 'all_except': use store base country as proxy
        $base = $plugin->get_store_country();
        return in_array( $base, $supported, true );
    }
}
