<?php
/**
 * Coupon Description AI — Advanced Coupons data provider.
 *
 * Enriches the coupon data payload with Advanced Coupons for WooCommerce
 * fields (BOGO, cart conditions, scheduling, etc.) when the plugin is active.
 *
 * @since   1.1.4
 * @package SAAI\AI_Agents\Advanced_Coupons
 */

namespace SAAI\AI_Agents\Advanced_Coupons;

use SAAI\Abstracts\Abstract_Class;
use SAAI\Traits\Singleton_Trait;

defined( 'ABSPATH' ) || exit;

/**
 * Coupon_Description_Data class.
 *
 * @since 1.1.4
 */
class Coupon_Description_Data extends Abstract_Class {

    use Singleton_Trait;

    /**
     * Holds the class instance object.
     *
     * @since 1.1.4
     * @var Coupon_Description_Data $instance object
     */
    protected static $instance;

    /**
     * Run hooks.
     *
     * @since 1.1.4
     *
     * @return void
     */
    public function run() {
        if ( ! class_exists( 'ACFWF' ) ) {
            return;
        }

        add_filter( 'saai_coupon_description_data', array( $this, 'add_acfw_data' ), 10, 2 );
    }

    /**
     * Add Advanced Coupons specific data.
     *
     * @since 1.1.4
     *
     * @param array      $data   The existing coupon data.
     * @param \WC_Coupon $coupon The coupon object.
     * @return array The data with ACFW fields added.
     */
    public function add_acfw_data( $data, $coupon ) {
        $coupon_id = $coupon->get_id();

        // Array fields that need operator symbol humanization.
        foreach ( array( 'bogo_deals', 'cart_conditions' ) as $field ) {
            $value = $this->get_meta( $coupon_id, $field );
            if ( ! empty( $value ) && is_array( $value ) ) {
                $data[ $field ] = $this->humanize_operator_conditions( $value );
            }
        }

        // Schedule start/end as formatted datetime strings.
        $datetime_format = get_option( 'date_format', 'F j, Y' ) . ' ' . get_option( 'time_format', 'g:i A' );
        foreach ( array( 'schedule_start', 'schedule_end' ) as $field ) {
            $value = $this->get_meta( $coupon_id, $field );
            if ( $value ) {
                $wc_dt          = new \WC_DateTime( $value, new \DateTimeZone( wc_timezone_string() ) );
                $data[ $field ] = $wc_dt->date_i18n( $datetime_format );
            }
        }

        // Features guarded by an enable flag with optional formatter.
        $guarded_features = array(
            'enable_day_time_schedules' => array(
                'field'  => 'day_time_schedules',
                'format' => 'format_day_time_schedules',
            ),
            'enable_add_products'       => array(
                'field'    => 'add_products_data',
                'data_key' => 'add_products',
                'format'   => 'format_add_products_data',
            ),
            'enable_shipping_overrides' => array(
                'field' => 'shipping_overrides',
            ),
        );

        foreach ( $guarded_features as $enable_key => $config ) {
            if ( 'yes' !== $this->get_meta( $coupon_id, $enable_key ) ) {
                continue;
            }

            $value = $this->get_meta( $coupon_id, $config['field'] );
            if ( ! empty( $value ) && is_array( $value ) ) {
                $data_key          = $config['data_key'] ?? $config['field'];
                $data[ $data_key ] = isset( $config['format'] ) ? $this->{$config['format']}( $value ) : $value;
            }
        }

        // Simple fields: include if not empty.
        $simple_fields = array(
            'auto_apply_coupon'           => 'auto_apply',
            'enable_coupon_url'           => 'coupon_url_enabled',
            'percentage_discount_cap'     => 'percentage_discount_cap',
            'excluded_coupons'            => 'excluded_coupons',
            'allowed_coupons'             => 'allowed_coupons',
            'product_attributes'          => 'product_attributes',
            'excluded_product_attributes' => 'excluded_product_attributes',
            'allowed_customers'           => 'allowed_customers',
        );

        foreach ( $simple_fields as $meta_suffix => $data_key ) {
            $value = $this->get_meta( $coupon_id, $meta_suffix );
            if ( ! empty( $value ) ) {
                $data[ $data_key ] = $value;
            }
        }

        // URL coupon: force apply (exclude 'disable' value).
        $force_apply = $this->get_meta( $coupon_id, 'force_apply_url_coupon' );
        if ( $force_apply && 'disable' !== $force_apply ) {
            $data['force_apply_url_coupon'] = $force_apply;
        }

        // URL coupon: defer apply.
        if ( 'yes' === $this->get_meta( $coupon_id, 'defer_apply_url_coupon' ) ) {
            $data['defer_apply_url_coupon'] = 'yes';
        }

        // Usage limits: reset period (exclude 'none' value).
        $reset_period = $this->get_meta( $coupon_id, 'reset_usage_limit_period' );
        if ( $reset_period && 'none' !== $reset_period ) {
            $data['reset_usage_limit_period'] = $reset_period;
        }

        // Role restrictions.
        if ( 'yes' === $this->get_meta( $coupon_id, 'enable_role_restriction' ) ) {
            $data['enable_role_restriction'] = 'yes';

            $role_type = $this->get_meta( $coupon_id, 'role_restrictions_type' );
            if ( $role_type ) {
                $data['role_restrictions_type'] = $role_type;
            }

            $role_restrictions = $this->get_meta( $coupon_id, 'role_restrictions' );
            if ( ! empty( $role_restrictions ) ) {
                $data['role_restrictions'] = $role_restrictions;
            }
        }

        // One Click Apply notification.
        if ( 'yes' === $this->get_meta( $coupon_id, 'enable_apply_notification' ) ) {
            $data['enable_apply_notification'] = 'yes';
        }

        // Payment method restrictions.
        if ( 'yes' === $this->get_meta( $coupon_id, 'enable_payment_methods_restrict' ) ) {
            $restrict_type = $this->get_meta( $coupon_id, 'payment_methods_restrict_type' );
            if ( $restrict_type ) {
                $data['payment_methods_restrict_type'] = $restrict_type;
            }

            $payment_methods = $this->get_meta( $coupon_id, 'payment_methods_restrict_selection' );
            if ( ! empty( $payment_methods ) && is_array( $payment_methods ) ) {
                $data['payment_methods_restriction'] = $this->get_payment_method_titles( $payment_methods );
            }
        }

        // Cashback waiting period.
        $cashback_period = $this->get_meta( $coupon_id, 'cashback_waiting_period' );
        if ( ! empty( $cashback_period ) && intval( $cashback_period ) > 0 ) {
            $data['cashback_waiting_period_days'] = intval( $cashback_period );
        }

        return $data;
    }

    /**
     * Get an Advanced Coupons post meta value.
     *
     * @since 1.1.4
     *
     * @param int    $coupon_id  The coupon post ID.
     * @param string $field_name The field name without the '_acfw_' prefix.
     * @return mixed The meta value.
     */
    protected function get_meta( $coupon_id, $field_name ) {
        return get_post_meta( $coupon_id, "_acfw_{$field_name}", true );
    }

    /**
     * Format add products data into human-readable strings.
     *
     * Converts the serialized ACFW add products structure into an array
     * of readable entries like "Product Name x2 (free)" or "Product Name x1 (50% off)".
     *
     * @since 1.1.4
     *
     * @param array $add_products_data The raw add products data from post meta.
     * @return array Array of formatted product strings.
     */
    protected function format_add_products_data( $add_products_data ) {
        $formatted = array();

        foreach ( $add_products_data as $entry ) {
            $product_id = $entry['product_id'] ?? 0;
            $product    = wc_get_product( $product_id );

            if ( ! $product ) {
                continue;
            }

            $name     = $product->get_name();
            $qty      = $entry['quantity'] ?? 1;
            $type     = $entry['discount_type'] ?? 'override';
            $value    = floatval( $entry['discount_value'] ?? 0 );
            $discount = '';

            if ( 'override' === $type && 0.0 === $value ) {
                $discount = 'free';
            } elseif ( 'override' === $type ) {
                $discount = wp_strip_all_tags( wc_price( $value ) );
            } elseif ( 'percent' === $type ) {
                $discount = $value . '% off';
            } elseif ( 'fixed' === $type ) {
                $discount = wp_strip_all_tags( wc_price( $value ) ) . ' off';
            }

            $formatted[] = $name . ' x' . $qty . ( $discount ? " ($discount)" : '' );
        }

        return $formatted;
    }

    /**
     * Get human-readable payment method titles from gateway IDs.
     *
     * @since 1.1.4
     *
     * @param array $gateway_ids Array of payment gateway IDs.
     * @return array Array of payment method titles.
     */
    protected function get_payment_method_titles( $gateway_ids ) {
        $available_gateways = WC()->payment_gateways()->payment_gateways();
        $titles             = array();

        foreach ( $gateway_ids as $gateway_id ) {
            if ( isset( $available_gateways[ $gateway_id ] ) ) {
                $titles[] = $available_gateways[ $gateway_id ]->get_title();
            } else {
                $titles[] = $gateway_id;
            }
        }

        return $titles;
    }

    /**
     * Format raw day/time schedule data into human-readable strings.
     *
     * Converts the serialized ACFW day/time schedule structure into an array
     * of readable entries like "Monday (all day)" or "Friday 9:00 AM - 5:00 PM".
     *
     * @since 1.1.4
     *
     * @param array $raw_schedules The raw day/time schedules from post meta.
     * @return array Array of formatted schedule strings.
     */
    protected function format_day_time_schedules( $raw_schedules ) {
        $formatted   = array();
        $time_format = get_option( 'time_format', 'g:i A' );

        foreach ( $raw_schedules as $day => $schedule ) {
            if ( ! is_array( $schedule ) || 'yes' !== ( $schedule['is_enabled'] ?? '' ) ) {
                continue;
            }

            $label = ucfirst( $day );

            if ( ! empty( $schedule['start_time'] ) && ! empty( $schedule['end_time'] ) ) {
                try {
                    $start       = ( new \DateTime( $schedule['start_time'] ) )->format( $time_format );
                    $end         = ( new \DateTime( $schedule['end_time'] ) )->format( $time_format );
                    $formatted[] = "$label $start - $end";
                } catch ( \Exception $ex ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- fallback only.
                    $formatted[] = "$label (invalid time format)";
                }
            } else {
                $formatted[] = "$label (all day)";
            }
        }

        return $formatted;
    }

    /**
     * Recursively replace operator symbols in condition fields with human-readable text.
     *
     * Prevents `sanitize_text_field()` from HTML-encoding `<` and `>` operators
     * in JSON data sent to the worker, which would produce invalid JSON.
     *
     * @since 1.1.4
     *
     * @param array $data The data array to process.
     * @return array The data with operator symbols replaced.
     */
    protected function humanize_operator_conditions( $data ) {
        $operator_map = array(
            '>=' => 'greater than or equal to',
            '<=' => 'less than or equal to',
            '>'  => 'greater than',
            '<'  => 'less than',
            '!=' => 'not equals',
            '='  => 'equals',
        );

        foreach ( $data as $key => &$value ) {
            if ( is_array( $value ) ) {
                $value = $this->humanize_operator_conditions( $value );
            } elseif ( 'condition' === $key && is_string( $value ) && isset( $operator_map[ $value ] ) ) {
                $value = $operator_map[ $value ];
            }
        }
        unset( $value );

        return $data;
    }
}
