<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Magento\Customer\Model\Indexer;

use Magento\Customer\Model\Config\Source\FilterConditionType;
use Magento\Customer\Model\Customer;
use Magento\Framework\Indexer\FieldsetInterface;
use Magento\Eav\Model\Config;
use Magento\Customer\Model\Attribute;

class AttributeProvider implements FieldsetInterface
{
    /**
     * EAV entity
     */
    public const ENTITY = Customer::ENTITY;

    /**
     * @var Attribute[]
     */
    protected $attributes;

    /**
     * @var Config
     */
    protected $eavConfig;

    /**
     * @param Config $eavConfig
     */
    public function __construct(
        Config $eavConfig
    ) {
        $this->eavConfig = $eavConfig;
    }

    /**
     * Add EAV attribute fields to fieldset
     *
     * @param array $data
     * @return array
     */
    public function addDynamicData(array $data)
    {
        $additionalFields = $this->convert($this->getAttributes(), $data);
        $data['fields'] = $this->merge($data['fields'], $additionalFields);
        return $data;
    }

    /**
     * Retrieve all attributes
     *
     * @return Attribute[]
     */
    private function getAttributes()
    {
        if ($this->attributes === null) {
            $this->attributes = [];
            $entityType = $this->eavConfig->getEntityType(static::ENTITY);
            /** @var \Magento\Customer\Model\Attribute[] $attributes */
            $attributes = $entityType->getAttributeCollection()->getItems();
            /** @var \Magento\Customer\Model\ResourceModel\Customer $entity */
            $entity = $entityType->getEntity();

            foreach ($attributes as $attribute) {
                $attribute->setEntity($entity);
            }
            $this->attributes = $attributes;
        }

        return $this->attributes;
    }

    /**
     * Convert attributes to fields
     *
     * @param Attribute[] $attributes
     * @param array $fieldset
     * @return array
     */
    protected function convert(array $attributes, array $fieldset)
    {
        $fields = [];
        foreach ($attributes as $attribute) {
            if (!$attribute->isStatic()) {
                if ($attribute->getData('is_used_in_grid')) {
                    $fields[$attribute->getName()] = [
                        'name' => $attribute->getName(),
                        'handler' => \Magento\Framework\Indexer\Handler\AttributeHandler::class,
                        'origin' => $attribute->getName(),
                        'type' => $this->getType($attribute),
                        'dataType' => $attribute->getBackendType(),
                        'filters' => [],
                        'entity' => static::ENTITY,
                        'bind' => $fieldset['references']['customer']['to'] ?? null,
                        'index' => $this->hasIndex($attribute)
                    ];
                }
            } else {
                $fields[$attribute->getName()] = [
                    'type' => $this->getType($attribute),
                    'index' => $this->hasIndex($attribute)
                ];
            }
        }

        return $fields;
    }

    /**
     * Get field type for attribute
     *
     * @param Attribute $attribute
     * @return string
     */
    protected function getType(Attribute $attribute)
    {
        if ($attribute->canBeSearchableInGrid()) {
            $type = 'searchable';
        } elseif ($attribute->canBeFilterableInGrid()) {
            $type = 'filterable';
        } else {
            $type = 'virtual';
        }

        return $type;
    }

    /**
     * Merge fields with attribute fields
     *
     * @param array $dataFields
     * @param array $searchableFields
     * @return array
     */
    protected function merge(array $dataFields, array $searchableFields)
    {
        foreach ($searchableFields as $name => $field) {
            if (!isset($field['name']) && !isset($dataFields[$name])) {
                continue;
            }
            if (!isset($dataFields[$name])) {
                $dataFields[$name] = [];
            }
            foreach ($field as $key => $value) {
                $dataFields[$name][$key] = $value;
            }
        }

        return $dataFields;
    }

    /**
     * Checks whether the attribute should be indexed
     *
     * @param Attribute $attribute
     * @return bool
     */
    private function hasIndex(Attribute $attribute): bool
    {
        return $attribute->canBeFilterableInGrid()
            && in_array(
                (int) $attribute->getGridFilterConditionType(),
                [FilterConditionType::FULL_MATCH, FilterConditionType::PREFIX_MATCH],
                true
            );
    }
}
