<?php
declare(strict_types=1);

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Api\RecordDetailsApiInterface;
use Magento\Framework\Webapi\Rest\Response as RestResponse;
use WriteTextAI\WriteTextAI\Model\Config\Source\ProductAttributes;
use WriteTextAI\WriteTextAI\Model\Config\Source\ProductAttributes\Excluded;
use Magento\Customer\Api\GroupRepositoryInterface;
use Magento\Customer\Api\Data\GroupInterface;
use Magento\Catalog\Helper\Image as ImageHelper;
use WriteTextAI\WriteTextAI\Helper\Image as WTAImageHelper;
use Magento\Catalog\Helper\Output as OutputHelper;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory as AttributeCollectionFactory;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Filter\FilterManager;
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
use WriteTextAI\WriteTextAI\Helper\Product as ProductHelper;
use WriteTextAI\WriteTextAI\Helper\Fields as FieldHelper;
use WriteTextAI\WriteTextAI\ViewModel\Settings;

/**
 * Class RecordDetailsApi
 * Implementation for record details API endpoint
 */
class RecordDetailsApi implements RecordDetailsApiInterface
{
    /**
     * @var RestResponse
     */
    private $response;

    /**
     * @var WTAImageHelper
     */
    private $wtaImageHelper;

    /**
     * @var ProductHelper
     */
    private $productHelper;

    /**
     * @var FilterManager
     */
    private $filterManager;

    /**
     * @var ImageHelper
     */
    private $imageHelper;

    /**
     * @var OutputHelper
     */
    private $outputHelper;

    /**
     * @var AttributeCollectionFactory
     */
    private $attributeCollectionFactory;

    /**
     * @var GroupRepositoryInterface
     */
    private $groupRepository;

    /**
     * @var ProductAttributes
     */
    private $productAttributes;

    /**
     * @var FieldHelper
     */
    private $fieldHelper;

    /**
     * @var Settings
     */
    private $settings;

    /**
     * @param RestResponse $response
     * @param WTAImageHelper $wtaImageHelper
     * @param ProductHelper $productHelper
     * @param FilterManager $filterManager
     * @param ImageHelper $imageHelper
     * @param OutputHelper $outputHelper
     * @param AttributeCollectionFactory $attributeCollectionFactory
     * @param GroupRepositoryInterface $groupRepository
     * @param ProductAttributes $productAttributes
     * @param FieldHelper $fieldHelper
     * @param Settings $settings
     */
    public function __construct(
        RestResponse $response,
        WTAImageHelper $wtaImageHelper,
        ProductHelper $productHelper,
        FilterManager $filterManager,
        ImageHelper $imageHelper,
        OutputHelper $outputHelper,
        AttributeCollectionFactory $attributeCollectionFactory,
        GroupRepositoryInterface $groupRepository,
        ProductAttributes $productAttributes,
        FieldHelper $fieldHelper,
        Settings $settings
    ) {
        $this->response = $response;
        $this->wtaImageHelper = $wtaImageHelper;
        $this->productHelper = $productHelper;
        $this->filterManager = $filterManager;
        $this->imageHelper = $imageHelper;
        $this->outputHelper = $outputHelper;
        $this->attributeCollectionFactory = $attributeCollectionFactory;
        $this->groupRepository = $groupRepository;
        $this->productAttributes = $productAttributes;
        $this->fieldHelper = $fieldHelper;
        $this->settings = $settings;
    }

    /**
     * Get record details
     *
     * @param string $storeId
     * @param string $type
     * @param string $recordId
     * @param mixed $parameters
     * @return void
     */
    public function getRecordDetails($storeId, $type, $recordId, $parameters)
    {
        $attributesData = [];
        $responseData = [];
        $rules = $this->settings->getSettings('rules');

        $responseData['attributes'] = [];

        try {
            if ($type === 'Product') {
                $product = $this->productHelper->getProduct($recordId, $storeId);
                $productType = $this->getProductTypeMapping((string) $product->getTypeId());
                $flags = $this->getVirtualDownloadableFlags($product->getTypeId());
                $isVirtual = (bool) ($flags['isVirtual'] ?? false);
                $isDownloadable = (bool) ($flags['isDownloadable'] ?? false);
                $childProducts = $this->getChildProducts($product);
                $attributesData = $this->getProductDetails($storeId, $type, $recordId, $parameters, $rules);

                $responseData['attributes'] = $attributesData;
                $responseData['productType'] = $productType;
                $responseData['isVirtual'] = $isVirtual;
                $responseData['isDownloadable'] = $isDownloadable;
                $responseData['childProducts'] = $childProducts;
            }
        } catch (\Exception $e) {
            $attributesData = [];
        }

        $this->response->setBody(json_encode($responseData))->sendResponse();
    }

    /**
     * Product get child producs
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return array
     */
    private function getChildProducts($product): array
    {
        $typeId = (string) $product->getTypeId();
        $childProducts = [];

        try {
            if ($typeId === 'configurable') {
                $children = $product->getTypeInstance()->getUsedProducts($product);
            } elseif ($typeId === 'grouped') {
                $children = $product->getTypeInstance()->getAssociatedProducts($product);
            } elseif ($typeId === 'bundle') {
                $typeInstance = $product->getTypeInstance();
                $optionIds = $typeInstance->getOptionsIds($product);
                $children = $typeInstance->getSelectionsCollection($optionIds, $product);
            } else {
                return [];
            }

            foreach ($children as $child) {
                if ((int)$child->getStatus() === ProductStatus::STATUS_DISABLED) {
                    continue;
                }

                $childProducts[] = [
                    'ID' => (string) $child->getId(),
                    'Name' => (string) $child->getName()
                ];
            }
        } catch (\Exception $e) {
            return [];
        }

        return $childProducts;
    }
    
    /**
     * Is virtual and/or downloadable
     *
     * @param string $typeId
     * @return array{isVirtual: bool, isDownloadable: bool}
     */
    private function getVirtualDownloadableFlags($typeId)
    {
        $typeId = (string)$typeId;
        $isVirtual = ($typeId === 'virtual');
        $isDownloadable = ($typeId === 'downloadable');

        if ($isDownloadable) {
            return ['isVirtual' =>true, 'isDownloadable' => $isDownloadable];
        }

        return ['isVirtual' => $isVirtual, 'isDownloadable' => $isDownloadable];
    }

    /**
     * Product get details
     *
     * @param string $storeId
     * @param string $type
     * @param string $recordId
     * @param mixed $parameters
     * @param array $rules
     * @return array
     */
    public function getProductDetails($storeId, $type, $recordId, $parameters, $rules)
    {
        $maxAttributes = $rules['maxAttributes'] ?? '';
        $maxAttributeValueLength = $rules['maxAttributeValueLength'] ?? '';
        $product = $this->productHelper->getProduct($recordId, $storeId);
        if (empty($parameters['attributes'])) {
            return [];
        }
        
        $productAttributes = $this->getProductAttributes($product, $parameters['attributes']);
        $attributesData = [];
        foreach ($productAttributes as $attribute) {
            try {
                if (strtolower($attribute['attribute'])
                    === ProductAttributes::PRODUCT_RESEARCH_DATA_ATTRIBUTE_CODE
                ) {
                    continue;
                }
                
                if (strtolower($attribute['attribute']) !== 'thumbnail') {
                    $stringAttribute = (string)$attribute['details'];
                    if (!empty(trim($stringAttribute))) {
                        $attributeValue = str_replace("<br />", "\n", $stringAttribute);
                        $attributeValue = $this->filterManager->stripTags($attributeValue);
                        //@codingStandardsIgnoreStart
                        $attributeValue = htmlspecialchars(
                            $attributeValue,
                            ENT_QUOTES | ENT_SUBSTITUTE,
                            'UTF-8'
                        );
                        //@codingStandardsIgnoreEnd
                        $attributesData[] = [
                            "name" => $attribute['attribute'],
                            "value" => $attributeValue,
                            "isCustom" => "false"
                        ];
                    }
                }
            } catch (\Exception $e) {
                continue;
            }
        }

        return $attributesData;
    }
    // phpcs:disable Generic.Metrics.NestingLevel.TooHigh
    /**
     * Get product attributes
     *
     * @param \Magento\Catalog\Model\Product $product
     * @param array $selectedAttributes
     * @return array
     */
    protected function getProductAttributes($product, $selectedAttributes = [])
    {
        $attributes = $this->productAttributes->toOptionArray();

        $attributeSetId = $product->getAttributeSetId();
        $attributeSet = $this->attributeCollectionFactory->create()
            ->addFieldToSelect('attribute_id')
            ->setAttributeSetFilter($attributeSetId)
            ->load()
            ->getItems();
        $attributeIds = array_map(fn($attribute) => $attribute->getAttributeId(), $attributeSet);
        
        $data = [];
        foreach ($attributes as &$attribute) {
            if ($attribute['value'] === 'wtai_product_research_data'
                && in_array($attribute['value'], $selectedAttributes)
            ) {
                $selectedAttribute = [
                    'attribute' => $attribute['value'],
                    'value' => $attribute['value'],
                    'details' => ''
                ];
                $data[] = $selectedAttribute;
                continue;
            }
            
            if (!in_array($attribute['value'], $selectedAttributes) ||
                !in_array($attribute['id'], $attributeIds)
            ) {
                continue;
            }
            
            $selectedAttribute = [
                'attribute' => $attribute['label'],
                'value' => $attribute['value']
            ];

            if (in_array($attribute['value'], Excluded::ARRAY_ATTRIBUTES)) {
                try {
                    switch ($attribute['value']) {
                        case 'tier_price':
                            $selectedAttribute['details'] = '';
                            foreach ($product->getTierPrices() as $tierPrice) {
                                $qty = $tierPrice['qty'] ?? '';
                                $price = $tierPrice['value'] ?? '';
                                $groupName = $tierPrice['customer_group_id'] !== GroupInterface::CUST_GROUP_ALL ?
                                    $this->groupRepository->getById($tierPrice['customer_group_id']) :
                                    __('ALL GROUPS');
                                
                                // Check if it's an object and get the group name
                                if (is_object($groupName) && method_exists($groupName, 'getCode')) {
                                    $groupName = $groupName->getCode();
                                }

                                $selectedAttribute['details'] .= sprintf("%s: ", __('Customer Group'))
                                    . $groupName . "<br />"
                                    . sprintf("%s: ", __('Quantity')) . $qty . "<br />"
                                    . sprintf("%s: ", __('Price')) . $price . "<br /><br />";
                            }
                            
                            break;

                        case 'quantity_and_stock_status':
                            $quantityAndStockStatus = $product->getQuantityAndStockStatus();
                            $qty = $quantityAndStockStatus['qty'] ?? '';
                            $stockStatus = $quantityAndStockStatus['is_in_stock'] ?? '';
                            $stockStatusLabel = $stockStatus ? __('In Stock') : __('Out of Stock');
                            $selectedAttribute['details'] = '';

                            if ($qty || $stockStatus) {
                                $selectedAttribute['details'] = sprintf("%s: ", __('Quantity')) . $qty . "<br />"
                                    . sprintf("%s: ", __('Stock status')) . $stockStatusLabel;
                            }
                            
                            break;
                    }
                } catch (\Exception $e) {
                    continue;
                }

                $data[] = $selectedAttribute;
            } else {
                $selectedAttribute['details'] = '';

                if (strtolower($attribute['value']) === 'thumbnail') {
                    $imageHelper = $this->imageHelper->init($product, 'product_listing_thumbnail_preview');
                    try {
                        $thumbnail = $imageHelper->setImageFile($product->getData('thumbnail'));
                        $thumbnail->getUrl();
                        $thumbnail->getOriginalWidth();
                    } catch (\InvalidArgumentException | FileSystemException $e) {
                        continue;
                    }
                    
                    $selectedAttribute['details'] = $imageHelper->getUrl();
                    $imageHelper = $this->imageHelper->init($product, 'product_listing_thumbnail');
                    $selectedAttribute['thumbnail'] = $imageHelper->getUrl();
                    $value = $this->wtaImageHelper->getImageValue($product);
                    $selectedAttribute['value'] = $value;

                    if ($value) {
                        $data[] = $selectedAttribute;
                    }
                } else {
                    try {
                        $details = $product->getResource()
                            ->getAttribute($attribute['value'])
                            ->getFrontend()
                            ->getValue($product);

                        if ($details) {
                            $selectedAttribute['details'] = $this->outputHelper->productAttribute(
                                $product,
                                $details,
                                $attribute['value']
                            );
                        }

                        $data[] = $selectedAttribute;
                    } catch (\Exception $e) {
                        /** Skip attributes with error */
                        continue;
                    }
                }
            }
        }
        return $data;
    }
    // phpcs:enable Generic.Metrics.NestingLevel.TooHigh

    /**
     * Get product type mapping
     *
     * @param string $type
     * @return string
     */
    private function getProductTypeMapping($type)
    {
        if ($type === 'virtual' || $type === 'downloadable' || $type === 'simple') {
            return 'Simple';
        } elseif ($type === 'configurable') {
            return 'Variable';
        } elseif ($type === 'grouped') {
            return 'Grouped';
        } elseif ($type === 'bundle') {
            return 'Bundle';
        }
        return $type;
    }
}
