<?php

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

/**
 * Builds product card data directly from WooCommerce products.
 *
 * No external API calls - only local DB lookups.
 * Used after the LLM response to convert matched product names into card objects.
 */
class Niwa_AI_Card_Builder {

    /**
     * Build a card array from a WooCommerce product ID.
     *
     * @param int $product_id WooCommerce product ID.
     * @return array Card data or empty array on failure.
     */
    public function build_card_from_id( $product_id, $variation_preview = array() ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array();
        }

        $product = wc_get_product( $product_id );
        if ( ! $product || 'publish' !== $product->get_status() ) {
            return array();
        }

        $card = $this->product_to_card( $product );
        if ( ! empty( $variation_preview['image'] ) ) {
            $card['image']                = (string) $variation_preview['image'];
            $card['image_width']          = intval( $variation_preview['image_width'] ?? 0 );
            $card['image_height']         = intval( $variation_preview['image_height'] ?? 0 );
            $card['preview_image']        = (string) $variation_preview['image'];
            $card['preview_image_width']  = intval( $variation_preview['image_width'] ?? 0 );
            $card['preview_image_height'] = intval( $variation_preview['image_height'] ?? 0 );
            $card['preview_variation_id'] = intval( $variation_preview['variation_id'] ?? 0 );
            $card['preview_attributes']   = (array) ( $variation_preview['selection'] ?? $variation_preview['attributes'] ?? array() );
        }
        unset( $card['short_desc'], $card['variation_options'] );

        return $card;
    }

    /**
     * Build the expanded product payload used by the in-chat product view.
     * Heavy variation and relationship data is loaded only after a visitor
     * opens a product, keeping the initial chat response compact.
     *
     * @param int $product_id WooCommerce product ID.
     * @return array Product details or an empty array on failure.
     */
    public function build_details_from_id( $product_id ) {
        if ( ! function_exists( 'wc_get_product' ) ) {
            return array();
        }

        $product = wc_get_product( $product_id );
        if ( ! $product || 'publish' !== $product->get_status() ) {
            return array();
        }

        $details = $this->product_to_card( $product );
        $single_image_id = method_exists( $product, 'get_image_id' ) ? intval( $product->get_image_id() ) : 0;
        $single_data = $single_image_id
            ? wp_get_attachment_image_src( $single_image_id, 'woocommerce_single' )
            : false;
        $single      = is_array( $single_data ) && ! empty( $single_data[0] ) ? (string) $single_data[0] : '';
        $dimensions  = $this->get_image_dimensions( $single_image_id, $single_data );

        if ( $single ) {
            $details['image'] = (string) $single;
            $details['image_width']  = $dimensions['width'];
            $details['image_height'] = $dimensions['height'];
        }

        $details['attributes']       = ! empty( $details['hasOptions'] ) ? $this->build_variation_options( $product ) : array();
        $details['variations']       = ! empty( $details['hasOptions'] ) ? $this->build_available_variations( $product ) : array();
        $details['related_products'] = $this->build_related_products( $product );

        return $details;
    }

    /**
     * Convert a WC_Product object into a card array.
     *
     * @param WC_Product $product WooCommerce product.
     * @return array Card data.
     */
    private function product_to_card( $product ) {
        $is_variable         = method_exists( $product, 'is_type' ) ? $product->is_type( 'variable' ) : false;
        list( $regular_price, $sale_price ) = $this->get_product_price_values( $product );
        $regular_price_float = is_numeric( $regular_price ) ? (float) $regular_price : 0.0;
        $sale_price_float    = is_numeric( $sale_price ) ? (float) $sale_price : 0.0;
        $discount_percent    = 0;
        $discount_amount     = 0.0;

        $badge = '';
        if ( $regular_price_float > 0 && $sale_price_float > 0 && $regular_price_float > $sale_price_float ) {
            $discount_amount  = $regular_price_float - $sale_price_float;
            $discount_percent = (int) round( ( $discount_amount / $regular_price_float ) * 100 );
            if ( $discount_percent > 0 ) {
                $badge = '-' . $discount_percent . '%';
            }
        }

        $image_id   = method_exists( $product, 'get_image_id' ) ? $product->get_image_id() : 0;
        $image_data = $image_id ? wp_get_attachment_image_src( $image_id, 'woocommerce_thumbnail' ) : false;
        $image      = is_array( $image_data ) && ! empty( $image_data[0] ) ? (string) $image_data[0] : '';
        $dimensions = $this->get_image_dimensions( $image_id, $image_data );

        $short_desc = '';
        if ( method_exists( $product, 'get_short_description' ) ) {
            $short_desc = wp_strip_all_tags( $product->get_short_description() );
            if ( mb_strlen( $short_desc ) > 120 ) {
                $short_desc = mb_substr( $short_desc, 0, 117 ) . '...';
            }
        }

        $rating       = method_exists( $product, 'get_average_rating' ) ? floatval( $product->get_average_rating() ) : 0;
        $rating_count = method_exists( $product, 'get_rating_count' ) ? intval( $product->get_rating_count() ) : 0;

        $discount_amount_label = '';
        if ( $discount_amount > 0 ) {
            $decimals              = function_exists( 'wc_get_price_decimals' ) ? wc_get_price_decimals() : 2;
            $discount_amount_label = function_exists( 'wc_format_localized_price' )
                ? wc_format_localized_price( wc_format_decimal( $discount_amount, $decimals ) )
                : number_format_i18n( $discount_amount, $decimals );
        }

        $sale_percent_label = $discount_percent > 0 ? '-' . $discount_percent . '%' : '';
        $amount_off_label   = $discount_amount_label ? '-' . $discount_amount_label . ' OFF' : '';
        $rating_display     = ( $rating_count > 0 && $rating > 0 ) ? $rating : 5;

        return array(
            'id'                    => $product->get_id(),
            'name'                  => $product->get_name(),
            'price'                 => function_exists( 'wc_price' ) ? wc_price( $product->get_price() ) : (string) $product->get_price(),
            'oldPrice'              => $regular_price_float > $sale_price_float && $sale_price_float > 0
                ? ( function_exists( 'wc_price' ) ? wc_price( $regular_price ) : (string) $regular_price )
                : '',
            'image'                 => (string) $image,
            'image_width'           => $dimensions['width'],
            'image_height'          => $dimensions['height'],
            'url'                   => get_permalink( $product->get_id() ),
            'type'                  => method_exists( $product, 'get_type' ) ? (string) $product->get_type() : '',
            'hasOptions'            => (bool) $is_variable,
            'in_stock'              => method_exists( $product, 'is_in_stock' ) ? (bool) $product->is_in_stock() : true,
            'purchasable'           => method_exists( $product, 'is_purchasable' ) ? (bool) $product->is_purchasable() : true,
            'variation_options'     => $is_variable ? $this->build_variation_options( $product ) : array(),
            'badge'                 => $sale_percent_label,
            'sale_percent'          => $discount_percent,
            'amount_off'            => $discount_amount,
            'sale_percent_label'    => $sale_percent_label,
            'amount_off_label'      => $amount_off_label,
            'discount_percent'      => $discount_percent,
            'discount_amount'       => $discount_amount,
            'discount_amount_label' => $discount_amount_label,
            'short_desc'            => $short_desc,
            'meta'                  => $this->build_product_meta( $product ),
            'rating'                => $rating,
            'rating_display'        => $rating_display,
            'rating_count'          => $rating_count,
            '_source'               => 'local_index',
            '_score'                => 1.0,
        );
    }

    /**
     * Resolve effective regular and sale prices for a product.
     *
     * Variable products often keep empty parent prices, so fall back to the
     * variation min prices when needed.
     *
     * @param WC_Product $product WooCommerce product.
     * @return array{0:string,1:string} Regular and sale price strings.
     */
    private function get_product_price_values( $product ) {
        $regular_price = method_exists( $product, 'get_regular_price' ) ? (string) $product->get_regular_price() : '';
        $sale_price    = method_exists( $product, 'get_sale_price' ) ? (string) $product->get_sale_price() : '';

        if ( method_exists( $product, 'is_type' ) && $product->is_type( 'variable' ) ) {
            if ( '' === trim( $regular_price ) && method_exists( $product, 'get_variation_regular_price' ) ) {
                $regular_price = (string) $product->get_variation_regular_price( 'min', false );
            }

            if ( '' === trim( $sale_price ) && method_exists( $product, 'get_variation_sale_price' ) ) {
                $sale_price = (string) $product->get_variation_sale_price( 'min', false );
            }
        }

        return array( $regular_price, $sale_price );
    }

    /**
     * Build a compact, language-neutral summary from structured WooCommerce
     * attributes. Labels and values come from the store itself; no attribute
     * meaning is inferred from keywords.
     *
     * @param WC_Product $product WooCommerce product.
     * @return string
     */
    private function build_product_meta( $product ) {
        if ( ! method_exists( $product, 'get_attributes' ) ) {
            return '';
        }

        $parts = array();
        foreach ( (array) $product->get_attributes() as $attribute ) {
            if ( ! is_object( $attribute ) || ! method_exists( $attribute, 'get_name' ) ) {
                continue;
            }

            $name   = (string) $attribute->get_name();
            $label  = function_exists( 'wc_attribute_label' ) ? wc_attribute_label( $name, $product ) : $name;
            $values = array();

            if ( method_exists( $attribute, 'is_taxonomy' ) && $attribute->is_taxonomy() && function_exists( 'wc_get_product_terms' ) ) {
                $values = (array) wc_get_product_terms( $product->get_id(), $name, array( 'fields' => 'names' ) );
            } elseif ( method_exists( $attribute, 'get_options' ) ) {
                $values = array_map( 'strval', (array) $attribute->get_options() );
            }

            $values = array_values( array_filter( array_unique( array_map( 'trim', $values ) ) ) );
            if ( empty( $values ) ) {
                continue;
            }

            $parts[] = count( $values ) > 1
                ? sprintf( '%s: %d', $label, count( $values ) )
                : sprintf( '%s: %s', $label, $values[0] );

            if ( count( $parts ) >= 2 ) {
                break;
            }
        }

        return implode( ' | ', $parts );
    }

    private function build_variation_options( $product ) {
        if ( ! method_exists( $product, 'get_variation_attributes' ) ) {
            return array();
        }

        $out = array();
        foreach ( (array) $product->get_variation_attributes() as $name => $options ) {
            $label = function_exists( 'wc_attribute_label' ) ? wc_attribute_label( $name, $product ) : (string) $name;
            $values  = array();
            $choices = array();
            foreach ( (array) $options as $value ) {
                $raw_value = (string) $value;
                if ( '' === $raw_value ) {
                    continue;
                }
                $display_value = $raw_value;
                if ( taxonomy_exists( (string) $name ) ) {
                    $term = get_term_by( 'slug', $raw_value, (string) $name );
                    if ( $term && ! is_wp_error( $term ) && ! empty( $term->name ) ) {
                        $display_value = (string) $term->name;
                    }
                }
                $values[]  = $display_value;
                $choices[] = array(
                    'value' => $raw_value,
                    'label' => $display_value,
                );
            }

            if ( ! empty( $values ) ) {
                $out[] = array(
                    'attribute' => 'attribute_' . sanitize_title( (string) $name ),
                    'label'     => (string) $label,
                    'options'   => array_values( array_unique( $values ) ),
                    'choices'   => $choices,
                );
            }
        }

        return $out;
    }

    /**
     * Return purchasable variations with their exact WooCommerce attribute
     * values. The frontend uses this structured data to resolve any number of
     * custom attributes without language- or keyword-based rules.
     *
     * @param WC_Product $product Variable product.
     * @return array
     */
    private function build_available_variations( $product ) {
        if ( ! method_exists( $product, 'get_available_variations' ) ) {
            return array();
        }

        $out = array();
        foreach ( (array) $product->get_available_variations( 'objects' ) as $variation ) {
            if ( ! is_object( $variation ) || ! method_exists( $variation, 'get_id' ) ) {
                continue;
            }

            $image_id   = method_exists( $variation, 'get_image_id' ) ? $variation->get_image_id() : 0;
            $image_data = $image_id ? wp_get_attachment_image_src( $image_id, 'woocommerce_thumbnail' ) : false;
            $image      = is_array( $image_data ) && ! empty( $image_data[0] ) ? (string) $image_data[0] : '';
            $dimensions = $this->get_image_dimensions( $image_id, $image_data );
            $price      = method_exists( $variation, 'get_price' ) ? $variation->get_price() : '';

            $out[] = array(
                'id'         => (int) $variation->get_id(),
                'attributes' => method_exists( $variation, 'get_variation_attributes' ) ? (array) $variation->get_variation_attributes() : array(),
                'image'       => (string) $image,
                'image_width'  => $dimensions['width'],
                'image_height' => $dimensions['height'],
                'price'        => function_exists( 'wc_price' ) ? wc_price( $price ) : (string) $price,
                'in_stock'     => method_exists( $variation, 'is_in_stock' ) ? (bool) $variation->is_in_stock() : true,
                'purchasable' => method_exists( $variation, 'is_purchasable' ) ? (bool) $variation->is_purchasable() : true,
            );
        }

        return $out;
    }

    private function get_image_dimensions( $image_id, $image_data ) {
        $metadata = $image_id && function_exists( 'wp_get_attachment_metadata' )
            ? wp_get_attachment_metadata( $image_id )
            : array();

        return array(
            'width'  => ! empty( $metadata['width'] ) ? intval( $metadata['width'] ) : ( is_array( $image_data ) && isset( $image_data[1] ) ? intval( $image_data[1] ) : 0 ),
            'height' => ! empty( $metadata['height'] ) ? intval( $metadata['height'] ) : ( is_array( $image_data ) && isset( $image_data[2] ) ? intval( $image_data[2] ) : 0 ),
        );
    }

    /**
     * Prefer curated WooCommerce upsell/cross-sell links, then fill the panel
     * with catalog peers whose product content is genuinely similar.
     *
     * @param WC_Product $product Product being viewed.
     * @return array
     */
    private function build_related_products( $product ) {
        $ids = array();
        if ( method_exists( $product, 'get_upsell_ids' ) ) {
            $ids = array_merge( $ids, (array) $product->get_upsell_ids() );
        }
        if ( method_exists( $product, 'get_cross_sell_ids' ) ) {
            $ids = array_merge( $ids, (array) $product->get_cross_sell_ids() );
        }
        $ids = array_values( array_unique( array_map( 'intval', $ids ) ) );
        if ( count( $ids ) < 8 && class_exists( 'Niwa_AI_Frontend_Product_Matcher' ) ) {
            foreach ( Niwa_AI_Frontend_Product_Matcher::find_similar_products( array( $product->get_id() ), 8 - count( $ids ) ) as $peer ) {
                if ( is_object( $peer ) && method_exists( $peer, 'get_id' ) ) {
                    $ids[] = intval( $peer->get_id() );
                }
            }
        }

        $cards = array();
        foreach ( array_slice( array_values( array_unique( $ids ) ), 0, 8 ) as $product_id ) {
            if ( $product_id <= 0 || $product_id === (int) $product->get_id() ) {
                continue;
            }
            $related = $this->build_card_from_id( $product_id );
            if ( ! empty( $related ) ) {
                $cards[] = $related;
            }
        }

        return $cards;
    }
}
