<?php

namespace DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\Value;

use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\ProductFieldArrayValue;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\ProductFieldValue;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\ProductFieldValueModificator;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\AttributesModificator;
use WC_Product;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\ProductFields\ProductFieldStringValue;
class AttributesProductFieldValue implements ProductFieldValue, ProductFieldStringValue, ProductFieldArrayValue, AttributesModificator
{
    public const ATTRIBUTE_VALUE = 'attribute_value';
    public const ATTRIBUTE_NAME = 'attribute_name';
    public const ATTRIBUTE_GLUE = ', ';
    public const KEY = 'attributes';
    private ?string $string_value = null;
    private ?array $value = null;
    private WC_Product $product;
    private ?ProductFieldValueModificator $modificator = null;
    public function __construct(WC_Product $product)
    {
        $this->product = $product;
    }
    public function has_string_value(): bool
    {
        return is_string($this->get_string_value());
    }
    public function get_string_value(): ?string
    {
        if ($this->string_value !== null) {
            return $this->string_value;
        }
        if ($this->modificator instanceof ProductFieldStringValue && $this->modificator->has_string_value()) {
            $this->string_value = $this->modificator->get_string_value();
        } elseif ($this->has_array_value()) {
            $this->string_value = implode(self::ATTRIBUTE_GLUE, $this->get_array_value());
        }
        return $this->string_value;
    }
    public function set_attributes_modificator(ProductFieldValueModificator $modificator): void
    {
        $this->modificator = $modificator;
        $this->modificator->set_product($this->product);
    }
    public static function get_key(): string
    {
        return self::KEY;
    }
    public static function get_name(): string
    {
        return esc_attr__('Attributes', 'dropshipping-export-free');
    }
    public function has_array_value(): bool
    {
        return \is_array($this->get_field_value());
    }
    public function get_array_value(): ?array
    {
        return $this->get_field_value();
    }
    private function get_field_value(): ?array
    {
        if ($this->value !== null) {
            return $this->value;
        }
        if (isset($this->modificator)) {
            if ($this->modificator instanceof ProductFieldArrayValue && $this->modificator->has_array_value()) {
                $this->value = $this->modificator->get_array_value();
            } else {
                $this->value = null;
            }
        } else {
            $this->value = $this->get_product_attributes($this->product);
        }
        return $this->value;
    }
    private function get_product_attributes(WC_Product $product): ?array
    {
        $attributes = $product->get_attributes();
        if ($attributes && is_array($attributes) > 0) {
            if ($this->are_attributes_oop($attributes)) {
                return $this->map_oop_attributes_to_assoc_format($attributes);
            }
            return $this->map_string_attributes_to_assoc_format($attributes);
        }
        return null;
    }
    private function map_oop_attributes_to_assoc_format(array $attributes): array
    {
        $result = [];
        foreach ($attributes as $attribute) {
            if ($attribute->is_taxonomy()) {
                $values = $this->get_names_from_terms($attribute->get_terms());
                $taxonomy_object = $attribute->get_taxonomy_object();
                if (isset($taxonomy_object) && $taxonomy_object instanceof \stdClass && isset($taxonomy_object->attribute_label)) {
                    $name = $taxonomy_object->attribute_label;
                } else {
                    $name = $attribute->get_name();
                }
            } else {
                $name = $attribute->get_name();
                $values = $attribute->get_options();
            }
            $result[$name] = implode(self::ATTRIBUTE_GLUE, $values);
        }
        return $result;
    }
    protected function get_names_from_terms(array $terms): array
    {
        return array_reduce($terms, function ($carry, \WP_Term $item) {
            array_unshift($carry, $item->name);
            return $carry;
        }, []);
    }
    private function map_string_attributes_to_assoc_format(array $attributes): array
    {
        $converted = [];
        foreach ($attributes as $key => $value) {
            $taxonomy = get_taxonomy($key);
            if ($taxonomy instanceof \WP_Taxonomy) {
                $name = $taxonomy->labels->singular_name;
                if (empty($value)) {
                    $terms = get_terms($taxonomy->name);
                    if (is_array($terms)) {
                        $result = [];
                        foreach ($terms as $term) {
                            $result[] = $term->name;
                        }
                        $value = implode(self::ATTRIBUTE_GLUE, $result);
                    }
                }
            } else {
                $name = $key;
            }
            $converted[$name] = $value;
        }
        return $converted;
    }
    private function are_attributes_oop(array $attributes): bool
    {
        $first = reset($attributes);
        return $first instanceof \WC_Product_Attribute;
    }
}
