<?php

namespace FuseCommerce\Services;

use FuseCommerce\Config\IndexingConfig;
use FuseCommerce\Repositories\ProductRepository;
use WC_Product;
use WC_Product_Variable;
use WC_Product_Variation;

/**
 * Service for serializing WooCommerce products to array format
 */
class ProductSerializerService {
    /**
     * @var ProductRepository
     */
    private $product_repository;

    /**
     * @var CurrencyService
     */
    private $currency_service;

    /**
     * Constructor
     */
    public function __construct() {
        $this->product_repository = new ProductRepository();
        $this->currency_service = new CurrencyService();
    }

    /**
     * Serialize a product to array format for JSON export
     *
     * @param WC_Product $product WooCommerce product object
     * @return array Product data array
     */
    public function serialize( WC_Product $product ): array {
        $data = [];

        $data['id']                = $product->get_id();
        $data['type']              = $product->get_type();
        $data['name']              = $product->get_name();
        $data['slug']              = $product->get_slug();
        $data['permalink']         = $product->get_permalink();
        $data['stock_status']      = $product->get_stock_status();
        $data['short_description'] = wp_strip_all_tags( $product->get_short_description() );

        // Categories
        $data['categories'] = $this->serializeCategories( $product );

        // Tags
        $data['tags'] = $this->serializeTags( $product );

        // Attributes
        $data['attributes'] = $this->serializeAttributes( $product );

        // Images
        $data['images'] = $this->serializeImages( $product );

        // Handle price and variations based on product type
        if ( $product->is_type( 'variable' ) ) {
            $variations = $this->serializeVariations( $product );
            $data['variations'] = $variations;

            // Calculate price range from variations
            $price_range = $this->calculatePriceRange( $variations );
            $data['price'] = $price_range['min'] ?? null;
            $data['price_range'] = $price_range;
        } else {
            // Simple product
            $data['price'] = $this->getProductPriceFloat( $product );
            $data['price_range'] = null;
            $data['variations'] = [];
        }

        return $data;
    }

    /**
     * Get product price as float
     *
     * @param WC_Product|WC_Product_Variation $product
     * @return float|null Price or null if not available
     */
    private function getProductPriceFloat( $product ): ?float {
        $price = $product->get_price();

        if ( empty( $price ) ) {
            return null;
        }

        return (float) $price;
    }

    /**
     * Serialize product categories
     *
     * @param WC_Product $product
     * @return array Array of category IDs
     */
    private function serializeCategories( WC_Product $product ): array {
        $cats = wp_get_post_terms( $product->get_id(), 'product_cat' );

        if ( is_wp_error( $cats ) || empty( $cats ) ) {
            return [];
        }

        return array_map(
            function ( $category ) {
                return $category->term_id;
            },
            $cats
        );
    }

    /**
     * Serialize product tags
     *
     * @param WC_Product $product
     * @return array Array of tag IDs
     */
    private function serializeTags( WC_Product $product ): array {
        $tags = wp_get_post_terms( $product->get_id(), 'product_tag' );

        if ( is_wp_error( $tags ) || empty( $tags ) ) {
            return [];
        }

        return array_map(
            function ( $tag ) {
                return $tag->term_id;
            },
            $tags
        );
    }

    /**
     * Serialize product attributes
     *
     * @param WC_Product $product
     * @return array Array of attribute objects
     */
    private function serializeAttributes( WC_Product $product ): array {
        $attributes = $product->get_attributes();

        if ( empty( $attributes ) ) {
            return [];
        }

        $result = [];

        foreach ( $attributes as $attribute ) {
            $attr_data = $this->serializeAttribute( $product, $attribute );

            if ( ! empty( $attr_data ) ) {
                $result[] = $attr_data;
            }
        }

        return $result;
    }

    /**
     * Serialize a single product attribute
     *
     * @param WC_Product $product
     * @param \WC_Product_Attribute $attribute
     * @return array|null Attribute data or null if empty
     */
    private function serializeAttribute( WC_Product $product, $attribute ): ?array {
        $name    = $attribute->get_name();
        $options = [];

        if ( $attribute->is_taxonomy() ) {
            // Taxonomy-based attribute
            $terms = wp_get_post_terms( $product->get_id(), $name );

            if ( is_wp_error( $terms ) || empty( $terms ) ) {
                return null;
            }

            foreach ( $terms as $term ) {
                $options[] = $term->name;
            }
        } else {
            // Custom attribute
            $options = $attribute->get_options();

            if ( empty( $options ) ) {
                return null;
            }
        }

        return [
            'name'    => $name,
            'options' => array_values( $options ),
        ];
    }

    /**
     * Serialize product images (main image + gallery)
     *
     * @param WC_Product $product
     * @return array Array of image URLs
     */
    private function serializeImages( WC_Product $product ): array {
        $images = [];

        // Get main image
        $image_id = $product->get_image_id();
        if ( $image_id ) {
            $image_url = wp_get_attachment_image_url( $image_id, IndexingConfig::PRODUCT_IMAGE_SIZE );
            if ( $image_url ) {
                $images[] = $image_url;
            }
        }

        return $images;
    }

    /**
     * Serialize product variations for variable products
     *
     * @param WC_Product $product Parent product
     * @return array Array of variation data
     */
    private function serializeVariations( WC_Product $product ): array {
        if ( ! $product->is_type( 'variable' ) ) {
            return [];
        }

        $variations = $this->product_repository->getProductVariations( $product->get_id() );

        if ( empty( $variations ) ) {
            return [];
        }

        $result = [];

        foreach ( $variations as $variation ) {
            $result[] = $this->serializeVariation( $variation );
        }

        return $result;
    }

    /**
     * Serialize a single product variation
     *
     * @param WC_Product_Variation $variation
     * @return array Variation data
     */
    private function serializeVariation( WC_Product_Variation $variation ): array {
        $data = [];

        $data['variation_id']   = $variation->get_id();
        $data['sku']            = $variation->get_sku();
        $data['price']          = $this->getProductPriceFloat( $variation );
        $data['regular_price']  = $variation->get_regular_price() ? (float) $variation->get_regular_price() : null;
        $data['sale_price']     = $variation->get_sale_price() ? (float) $variation->get_sale_price() : null;
        $data['stock_quantity'] = $variation->get_stock_quantity();
        $data['is_in_stock']    = $variation->is_in_stock();
        $data['stock_status']   = $variation->get_stock_status();

        // Variation attributes (e.g., {"pa_size": "small", "pa_color": "red"})
        $attributes = $variation->get_variation_attributes();
        $data['attributes'] = [];

        foreach ( $attributes as $attr_name => $attr_value ) {
            // Remove 'attribute_' prefix if present
            $clean_name = str_replace( 'attribute_', '', $attr_name );
            $data['attributes'][ $clean_name ] = $attr_value;
        }
        return $data;
    }

    /**
     * Calculate price range from variations
     *
     * @param array $variations Array of serialized variation data
     * @return array|null Price range with 'min' and 'max' keys, or null if no prices
     */
    private function calculatePriceRange( array $variations ): ?array {
        if ( empty( $variations ) ) {
            return null;
        }

        $prices = [];

        foreach ( $variations as $variation ) {
            if ( isset( $variation['price'] ) && $variation['price'] !== null ) {
                $prices[] = (float) $variation['price'];
            }
        }

        if ( empty( $prices ) ) {
            return null;
        }

        return [
            'min' => min( $prices ),
            'max' => max( $prices ),
        ];
    }

    /**
     * Batch serialize multiple products
     *
     * @param array $products Array of WC_Product objects
     * @return array Array of serialized product data
     */
    public function serializeBatch( array $products ): array {
        $result = [];

        foreach ( $products as $product ) {
            try {
                $result[] = $this->serialize( $product );
            } catch ( \Exception $e ) {
                // Log error but continue processing other products
                error_log( sprintf(
                    'FtlCommerce: Failed to serialize product ID %d: %s',
                    $product->get_id(),
                    $e->getMessage()
                ) );

                // Skip this product and continue
                continue;
            }
        }

        return $result;
    }

    /**
     * Get WooCommerce currency settings
     *
     * @return array Currency configuration
     */
    public function getCurrencySettings(): array {
        return $this->currency_service->getCurrencySettings();
    }
}
