<?php
/**
 * @author 1902 Software
 * @copyright Copyright © 2023 1902 Software (https://1902software.com/magento/)
 * @package WriteTextAI_WriteTextAI
 */

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Model\ApiManager;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Fields;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use WriteTextAI\WriteTextAI\Model\UserFeedbackManager;

class GeneratedText
{
    /**
     * @var ApiManager
     */
    protected $apiManager;

    /**
     * @var StoreHelper
     */
    protected $storeHelper;

    /**
     * @var UserFeedbackManager
     */
    protected $userFeedbackManager;

    /**
     * Constructor
     *
     * @param ApiManager $apiManager
     * @param StoreHelper $storeHelper
     * @param UserFeedbackManager $userFeedbackManager
     */
    public function __construct(
        ApiManager $apiManager,
        StoreHelper $storeHelper,
        UserFeedbackManager $userFeedbackManager
    ) {
        $this->apiManager = $apiManager;
        $this->storeHelper = $storeHelper;
        $this->userFeedbackManager = $userFeedbackManager;
    }

    /**
     * Returns raw data from api
     *
     * @param string $ids
     * @param int $storeId
     * @param string $type
     * @return mixed[]
     */
    public function getGenerated($ids, $storeId, $type = 'Product')
    {
        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            "type" => $type,
            "storeId" => $storeId,
            "language" => $language,
            "recordId" => $ids,
            "includeUpdateHistory" => "true"
        ];

        $response = [];
        try {
            $response = $this->apiManager->getGenerated($params);
        } catch (\Exception $e) {
            $response = [];
        }

        return $response;
    }

    /**
     * Process generated text by store id
     *
     * @param string $ids
     * @param int $storeId
     * @param bool $isTransfer
     * @param array $text
     * @return mixed[]
     */
    public function getGeneratedByStoreId($ids, $storeId, $isTransfer = false, $text = [])
    {
        $response = !empty($text) ? $text : $this->getGenerated($ids, $storeId);

        $generated = [];

        if (!isset($response['records'])) {
            return $generated;
        }

        foreach ($response['records'] as $record) {
            foreach ($record['stores'] as $store) {
                if ($store['storeId'] == $storeId) {
                    $generatedValue = $this->getGeneratedValue($store);

                    if ($isTransfer) {
                        $history = $this->getTextHistory($store);
                        $transferred = isset($history['publish']) && isset($history['reviewed']) ? (
                            $history['publish'] && $history['reviewed'] ? true : false
                        ) : false;
                        $generated[$record['recordId']]['status'][$store['field']] = $transferred;
                    }
                    
                    if ($generatedValue !== null) {
                        $generated[$record['recordId']][$store['field']] = $generatedValue;
                    }
                }
            }
        }

        return $generated;
    }

    /**
     * Get history
     *
     * @param array $store
     *
     * @return string|null
     */
    public function getTextHistory($store)
    {
        if (!isset($store['texts']) || empty($store['texts'])) {
            return null;
        }

        if (isset($store['texts'][0]['history']) && !empty($store['texts'][0]['history'])) {
            return $store['texts'][0]['history'][0];
        }

        return null;
    }

    /**
     * Get generated value
     *
     * @param array $store
     *
     * @return string|null
     */
    public function getGeneratedValue($store)
    {
        if (!isset($store['texts']) || empty($store['texts'])) {
            return null;
        }

        if (isset($store['texts'][0]['value']) && $store['texts'][0]['value'] !== null) {
            return $store['texts'][0]['value'];
        }

        if (isset($store['texts'][0]['outputs']) && !empty($store['texts'][0]['outputs'])) {
            return $store['texts'][0]['outputs'][0];
        }

        return null;
    }
    
    /**
     * Extract fields from generated data
     *
     * @param array $generated
     * @param string $key
     * @return array
     */
    protected function extractFieldsFromGenerated($generated, $key)
    {
        if (empty($generated) || !isset($generated['records']) ||
            empty($generated['records']) || !isset($generated['records'][0]['stores'])
        ) {
            return [];
        }

        $fields = [];
        foreach ($generated['records'][0]['stores'] as $field) {
            if (isset($field['texts'][0][$key])) {
                $fields[$field['field']] = $field['texts'][0][$key];
            }
        }

        return $fields;
    }

    /**
     * Get text fields template
     *
     * @param int $productId
     * @param int $storeId
     * @param array $text
     * @return array
     */
    public function getTextFieldsTemplate($productId, $storeId, $text = [])
    {
        $fieldsTemplate = $this->extractFieldsFromGenerated($text, 'templateId');
        if ($fieldsTemplate) {
            return [
                'page_title' => $fieldsTemplate[Fields::PAGE_TITLE] ?? '',
                'product_description' => $fieldsTemplate[Fields::PRODUCT_DESCRIPTION] ?? '',
                'short_product_description' => $fieldsTemplate[Fields::EXCERPT] ?? ''
            ];
        }

        return [
            'page_title' => '',
            'product_description' => '',
            'short_product_description' => ''
        ];
    }

    /**
     * Get text fields model
     *
     * @param int $productId
     * @param int $storeId
     * @param array $text
     * @return array
     */
    public function getTextFieldsModel($productId, $storeId, $text = [])
    {
        $fieldsModel = $this->extractFieldsFromGenerated($text, 'modelId');
        if ($fieldsModel) {
            return [
                'page_title' => $fieldsModel[Fields::PAGE_TITLE] ?? '',
                'page_description' => $fieldsModel[Fields::PAGE_DESCRIPTION] ?? '',
                'product_description' => $fieldsModel[Fields::PRODUCT_DESCRIPTION] ?? '',
                'short_product_description' => $fieldsModel[Fields::EXCERPT] ?? '',
                'open_graph' => $fieldsModel[Fields::OPEN_GRAPH] ?? ''
            ];
        }

        return [
            'page_title' => '',
            'page_description' => '',
            'product_description' => '',
            'short_product_description' => '',
            'open_graph' => ''
        ];
    }

    /**
     * Get text fields date
     *
     * @param int $productId
     * @param int $storeId
     * @param array $text
     * @return array
     */
    public function getTextFieldsDate($productId, $storeId, $text = [])
    {
        $fieldsDate = $this->extractFieldsFromGenerated($text, 'date');
        if ($fieldsDate) {
            return [
                'page_title' => $fieldsDate[Fields::PAGE_TITLE] ?? '',
                'page_description' => $fieldsDate[Fields::PAGE_DESCRIPTION] ?? '',
                'product_description' => $fieldsDate[Fields::PRODUCT_DESCRIPTION] ?? '',
                'short_product_description' => $fieldsDate[Fields::EXCERPT] ?? '',
                'open_graph' => $fieldsDate[Fields::OPEN_GRAPH] ?? ''
            ];
        }

        return [
            'page_title' => '',
            'page_description' => '',
            'product_description' => '',
            'short_product_description' => '',
            'open_graph' => ''
        ];
    }

    /**
     * Get text fields
     *
     * @param int $productId
     * @param int $storeId
     * @param bool $isTransfer
     * @param array $text
     * @return array
     */
    public function getTextFields($productId, $storeId, $isTransfer = false, $text = [])
    {
        $item = [];
        
        $generated = $this->getGeneratedByStoreId($productId, $storeId, $isTransfer, $text);

        if (isset($generated[$productId])) {
            $generatedProduct = $generated[$productId];

            $pageTitle = $generatedProduct[Fields::PAGE_TITLE] ?? '';
            if (!empty($pageTitle)) {
                $pageTitle = htmlspecialchars_decode($pageTitle, ENT_QUOTES);
            }
            $item['page_title'] = $pageTitle;
            $item['page_description'] = $generatedProduct[Fields::PAGE_DESCRIPTION] ?? '';
            $item['product_description'] = $generatedProduct[Fields::PRODUCT_DESCRIPTION] ?? '';
            $item['short_product_description'] = $generatedProduct[Fields::EXCERPT] ?? '';
            $item['open_graph'] = $generatedProduct[Fields::OPEN_GRAPH] ?? '';

            if ($isTransfer) {
                $item['status']['page_title'] = $generatedProduct['status'][Fields::PAGE_TITLE] ?? false;
                $item['status']['page_description'] = $generatedProduct['status'][Fields::PAGE_DESCRIPTION] ?? false;
                $item['status']['product_description'] = $generatedProduct['status'][Fields::PRODUCT_DESCRIPTION]
                    ?? false;
                $item['status']['short_product_description'] = $generatedProduct['status'][Fields::EXCERPT] ?? false;
                $item['status']['open_graph'] = $generatedProduct['status'][Fields::OPEN_GRAPH] ?? false;
            }
        } else {
            $item['page_title'] = '';
            $item['page_description'] = '';
            $item['product_description'] = '';
            $item['short_product_description'] = '';
            $item['open_graph'] = '';

            if ($isTransfer) {
                $item['status']['page_title'] = false;
                $item['status']['page_description'] = false;
                $item['status']['product_description'] = false;
                $item['status']['short_product_description'] = false;
                $item['status']['open_graph'] = false;
            }
        }

        return $item;
    }
    
    /**
     * Get text ids
     *
     * @param int $productId
     * @param int $storeId
     * @return array
     */
    public function getTextIds($productId, $storeId)
    {
        $item = [];
        
        $generated = $this->getTextIdsFromGenerated($productId, $storeId);
        if (isset($generated[$productId])) {
            if (isset($generated[$productId][Fields::PAGE_TITLE])) {
                $item['page_title'] = $generated[$productId][Fields::PAGE_TITLE];
            }
            if (isset($generated[$productId][Fields::PAGE_DESCRIPTION])) {
                $item['page_description'] = $generated[$productId][Fields::PAGE_DESCRIPTION];
            }
            if (isset($generated[$productId][Fields::PRODUCT_DESCRIPTION])) {
                $item['product_description'] = $generated[$productId][Fields::PRODUCT_DESCRIPTION];
            }
            if (isset($generated[$productId][Fields::EXCERPT])) {
                $item['short_product_description'] = $generated[$productId][Fields::EXCERPT];
            }
            if (isset($generated[$productId][Fields::OPEN_GRAPH])) {
                $item['open_graph'] = $generated[$productId][Fields::OPEN_GRAPH];
            }
        }

        return $item;
    }

    /**
     * Get text ids from generated
     *
     * @param int $productId
     * @param int $storeId
     * @param string $type
     * @return array
     */
    public function getTextIdsFromGenerated($productId, $storeId, $type = 'Product')
    {
        $response = $this->getGenerated($productId, $storeId, $type);
        $generated = [];

        if (isset($response['records'])) {
            foreach ($response['records'] as $record) {
                $generated = $this->processRecord($record, $storeId, $generated);
            }
        }

        return $generated;
    }

    /**
     * Process record
     *
     * @param array $record
     * @param int $storeId
     * @param array $generated
     * @return array
     */
    public function processRecord($record, $storeId, $generated)
    {
        foreach ($record['stores'] as $store) {
            if ($store['storeId'] == $storeId) {
                $generated = $this->processStore($store, $record, $generated);
            }
        }

        return $generated;
    }

    /**
     * Process store
     *
     * @param array $store
     * @param array $record
     * @param array $generated
     * @return array
     */
    public function processStore($store, $record, $generated)
    {
        if (isset($store['texts']) && !empty($store['texts'])) {
            if (isset($store['texts'][0]['id'])) {
                $generated[$record['recordId']][$store['field']] = $store['texts'][0]['id'];
            }
        }

        return $generated;
    }

    /**
     * Save text
     *
     * @param int $storeId
     * @param int $productId
     * @param string $pageTitle
     * @param string $pageDescription
     * @param string $productDescription
     * @param string $productShortDescription
     * @param string $openGraph
     * @param array $selectedFields
     * @param bool $isReviewed
     * @param bool $isAutomatic
     * @return void
     */
    public function saveText(
        $storeId,
        $productId,
        $pageTitle,
        $pageDescription,
        $productDescription,
        $productShortDescription,
        $openGraph,
        $selectedFields,
        $isReviewed = true,
        $isAutomatic = false
    ) {
        $ids = $this->getTextIds($productId, $storeId);

        $fields = [];

        if (in_array('page_title', $selectedFields) && isset($ids['page_title'])
            && !empty($pageTitle)) {
            $fields[] = [
                "textId" => $ids['page_title'],
                "field" => Fields::PAGE_TITLE,
                "value" => $pageTitle,
                "publish" => true,
                "reviewed" => $isReviewed,
                "isAutomatic" => $isAutomatic,
                "platform" => "Magento"
            ];
        }
        if (in_array('page_description', $selectedFields) && isset($ids['page_description'])
            && !empty($pageDescription)) {
            $fields[] = [
                "textId" => $ids['page_description'],
                "field" => Fields::PAGE_DESCRIPTION,
                "value" => $pageDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "isAutomatic" => $isAutomatic,
                "platform" => "Magento"
            ];
        }
        if (in_array('product_description', $selectedFields) && isset($ids['product_description'])
            && !empty($productDescription)) {
            $fields[] = [
                "textId" => $ids['product_description'],
                "field" => Fields::PRODUCT_DESCRIPTION,
                "value" => $productDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "isAutomatic" => $isAutomatic,
                "platform" => "Magento"
            ];
        }
        if (in_array('short_product_description', $selectedFields) && isset($ids['short_product_description'])
            && !empty($productShortDescription)) {
            $fields[] = [
                "textId" => $ids['short_product_description'],
                "field" => Fields::EXCERPT,
                "value" => $productShortDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "isAutomatic" => $isAutomatic,
                "platform" => "Magento"
            ];
        }
        if (in_array('open_graph', $selectedFields) && isset($ids['open_graph'])
            && !empty($openGraph)) {
            $fields[] = [
                "textId" => $ids['open_graph'],
                "field" => Fields::OPEN_GRAPH,
                "value" => $openGraph,
                "publish" => true,
                "reviewed" => $isReviewed,
                "isAutomatic" => $isAutomatic,
                "platform" => "Magento"
            ];
        }

        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            'type' => 'Product',
            'language' => $language,
            'recordId' => $productId,
            'storeId' => $storeId,
            'fields' => $fields
        ];

        if (!empty($fields)) {
            $this->apiManager->save($params);
        }
    }

    /**
     * Get texts feedback
     *
     * @param array $generated
     * @return array
     */
    public function getTextsFeedback($generated = [])
    {
        $mapping = [
            Fields::PAGE_TITLE => 'page_title',
            Fields::PAGE_DESCRIPTION => 'page_description',
            Fields::PRODUCT_DESCRIPTION => 'product_description',
            Fields::EXCERPT => 'short_product_description',
            Fields::OPEN_GRAPH => 'open_graph',
        ];

        if (!empty($generated) &&
            isset($generated['records']) &&
            !empty($generated['records']) &&
            isset($generated['records'][0]['stores'])
        ) {
            $feedback = [];
            foreach ($generated['records'][0]['stores'] as $field) {
                $fieldTexts = $field['texts'] ?? [];
                if (isset($fieldTexts[0]['feedback'])) {
                    $feedback[$mapping[$field['field']]] = $fieldTexts[0]['feedback'] ?? [];
                }
            }
            return $feedback;
        }

        return [];
    }

    /**
     * Get text fields statuses
     *
     * @param int $productId
     * @param int $storeId
     * @param array $generated
     * @return array
     */
    public function getTextFieldsStatuses($productId, $storeId, $generated = [])
    {
        if (!empty($generated) &&
            isset($generated['records']) &&
            !empty($generated['records']) &&
            isset($generated['records'][0]['stores'])
        ) {
            foreach ($generated['records'][0]['stores'] as $field) {
                $fieldTexts = $field['texts'] ?? [];
                $fieldTextsFirstHistory = $fieldTexts[0]['history'][0]['publish'] ?? false;
                $statuses['generated'][$field['field']] = !empty($fieldTexts);
                $statuses['transferred'][$field['field']] = !empty($fieldTexts) ? $fieldTextsFirstHistory : false;
            }

            return [
                'page_title_generated' => $statuses['generated'][Fields::PAGE_TITLE] ?? false,
                'page_description_generated' => $statuses['generated'][Fields::PAGE_DESCRIPTION] ?? false,
                'product_description_generated' => $statuses['generated'][Fields::PRODUCT_DESCRIPTION] ?? false,
                'short_product_description_generated' => $statuses['generated'][Fields::EXCERPT] ?? false,
                'open_graph_generated' => $statuses['generated'][Fields::OPEN_GRAPH] ?? false,
                'page_title_transferred' => $statuses['transferred'][Fields::PAGE_TITLE] ?? false,
                'page_description_transferred' => $statuses['transferred'][Fields::PAGE_DESCRIPTION] ?? false,
                'product_description_transferred' => $statuses['transferred'][Fields::PRODUCT_DESCRIPTION] ?? false,
                'short_product_description_transferred' => $statuses['transferred'][Fields::EXCERPT] ?? false,
                'open_graph_transferred' => $statuses['transferred'][Fields::OPEN_GRAPH] ?? false
            ];
        }

        $history = $this->getHistory($productId, $storeId);

        return [
            'page_title_generated' => $this->getFieldStatus(
                $history,
                Fields::PAGE_TITLE,
                'Generate'
            ),
            'page_description_generated' => $this->getFieldStatus(
                $history,
                Fields::PAGE_DESCRIPTION,
                'Generate'
            ),
            'product_description_generated' => $this->getFieldStatus(
                $history,
                Fields::PRODUCT_DESCRIPTION,
                'Generate'
            ),
            'short_product_description_generated' => $this->getFieldStatus(
                $history,
                Fields::EXCERPT,
                'Generate'
            ),
            'open_graph_generated' => $this->getFieldStatus(
                $history,
                Fields::OPEN_GRAPH,
                'Generate'
            ),
            'page_title_transferred' => $this->getFieldStatus(
                $history,
                Fields::PAGE_TITLE,
                'Transferred,Magento'
            ),
            'page_description_transferred' => $this->getFieldStatus(
                $history,
                Fields::PAGE_DESCRIPTION,
                'Transferred,Magento'
            ),
            'product_description_transferred' => $this->getFieldStatus(
                $history,
                Fields::PRODUCT_DESCRIPTION,
                'Transferred,Magento'
            ),
            'short_product_description_transferred' => $this->getFieldStatus(
                $history,
                Fields::EXCERPT,
                'Transferred,Magento'
            ),
            'open_graph_transferred' => $this->getFieldStatus(
                $history,
                Fields::OPEN_GRAPH,
                'Transferred,Magento'
            )
        ];
    }

    /**
     * Get field status
     *
     * @param array $history
     * @param string $field
     * @param string $action
     * @return bool
     */
    public function getFieldStatus($history, $field, $action)
    {
        switch ($action) {
            case 'Generate':
                foreach ($history as $item) {
                    if (strcasecmp($item['textType'], $field) == 0 &&
                        $item['action'] == $action
                    ) {
                        return true;
                    }
                }
                return false;
            case 'Transferred,Magento':
                $transferred = false;
                foreach ($history as $item) {
                    if (strcasecmp($item['textType'], $field) !== 0) {
                        continue;
                    }

                    switch ($item['action']) {
                        case $action:
                            $transferred = true;
                            break 1;
                        case 'Generate':
                        case 'Update':
                            break 2;
                        default:
                            break;
                    }
                }
                return $transferred;
            default:
                return false;
        }
    }

    /**
     * Get history
     *
     * @param int $recordId
     * @param int $storeId
     * @param string $type
     *
     * @return bool
     */
    public function getHistory($recordId, $storeId, $type = 'Product')
    {
        $data = [
            'type' => $type,
            'storeId' => $storeId,
            'recordId' => $recordId,
        ];
        $language = $this->storeHelper->getFormattedLanguage($storeId);
        $data['language'] = $language;

        $histories = [];
        $history = $this->apiManager->getHistory($data);

        if (isset($history['histories']) && !empty($history['histories'])) {
            $histories = $history['histories'];
        }

        if (isset($history['continuationToken'])) {
            do {
                $data['continuationToken'] = $history['continuationToken'];
                $history = $this->apiManager->getHistory($data);
                
                if (isset($history['histories']) && !empty($history['histories'])) {
                    $histories = array_merge($histories, $history['histories']);
                }
            } while (isset($history['continuationToken']));
        }

        return $histories;
    }

    /**
     * Get review status
     *
     * @param int $productId
     * @param int $storeId
     * @param string $type
     *
     * @return array
     */
    public function getReviewStatus($productId, $storeId, $type = 'Product')
    {
        $data = [
            'type' => $type,
            'all' => 'true',
            'recordId' => $productId,
            'storeId' => $storeId
        ];
        $data['language'] = $this->storeHelper->getRegionIndependentLanguage($storeId);

        $reviews = [];
        $response = $this->apiManager->getReviews($data);

        if (isset($response['reviews']) && !empty($response['reviews'])) {
            foreach ($response['reviews'] as $review) {
                $filteredReview = array_filter($review, function ($value) {
                    return $value !== null;
                });
                if (isset($review['comment'])) {
                    $filteredReview['comment'] = preg_replace("/(\r\n|\n\r|\n|\r)/", "<br />", $review['comment']);
                }
                foreach ($filteredReview['fields'] as $key => $field) {
                    $filteredReview['fields'][$key] = array_filter($field, function ($value) {
                        return $value !== null;
                    });
                }

                $reviews[] = $filteredReview;
            }
        }
        return $reviews;
    }

    /**
     * Get all reviews
     *
     * @param int $productId
     * @param string $type
     *
     * @return array
     */
    public function getAllReviewStatus(
        $productId,
        $type = 'Product'
    ) {
        $data = [
            'type' => $type,
            'recordId' => $productId,
            'all' => 'true'
        ];

        $reviews = [];
        $response = $this->apiManager->getReviews($data);
        
        if (isset($response['reviews']) && !empty($response['reviews'])) {
            foreach ($response['reviews'] as $review) {
                $filteredReview = array_filter($review, function ($value) {
                    return $value !== null;
                });
                if (isset($review['comment'])) {
                    $filteredReview['comment'] = preg_replace("/(\r\n|\n\r|\n|\r)/", "<br />", $review['comment']);
                }
                foreach ($filteredReview['fields'] as $key => $field) {
                    $filteredReview['fields'][$key] = array_filter($field, function ($value) {
                        return $value !== null;
                    });
                }

                $reviews[] = $filteredReview;
            }
        }

        return $reviews;
    }

    /**
     * Get texts keywords used
     *
     * @param array $generated
     * @return array
     */
    public function getTextsKeywordsUsed($generated = [])
    {
        $mapping = [
            Fields::PAGE_TITLE => 'page_title',
            Fields::PAGE_DESCRIPTION => 'page_description',
            Fields::PRODUCT_DESCRIPTION => 'product_description',
            Fields::EXCERPT => 'short_product_description',
            Fields::OPEN_GRAPH => 'open_graph',
        ];

        if (!empty($generated) &&
            isset($generated['records']) &&
            !empty($generated['records']) &&
            isset($generated['records'][0]['stores'])
        ) {
            $keywords = [];
            foreach ($generated['records'][0]['stores'] as $field) {
                $fieldTexts = $field['texts'] ?? [];
                if (isset($fieldTexts[0]['keywords'])) {
                    $keywords[$mapping[$field['field']]] = $fieldTexts[0]['keywords'] ?? [];
                }
            }
            return $keywords;
        }

        return [];
    }
}
