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

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Model\BulkGenerateRequestsFactory;
use WriteTextAI\WriteTextAI\Api\BulkGenerateRequestsRepositoryInterface;
use WriteTextAI\WriteTextAI\Model\ApiManager;
use WriteTextAI\WriteTextAI\Model\Api\Keywords;

class BulkGenerateRequestsManager
{
    /**
     * @var BulkGenerateRequestsFactory
     */
    protected $BulkGenerateRequestsFactory;

    /**
     * @var BulkGenerateRequestsRepositoryInterface
     */
    protected $BulkGenerateRequestsRepository;

    /**
     * @var ApiManager
     */
    protected $apiManager;

    /**
     * @var Keywords
     */
    protected $keywords;

    /**
     * Constructor
     *
     * @param BulkGenerateRequestsFactory $BulkGenerateRequestsFactory
     * @param BulkGenerateRequestsRepositoryInterface $BulkGenerateRequestsRepository
     * @param ApiManager $apiManager
     * @param Keywords $keywords
     */
    public function __construct(
        BulkGenerateRequestsFactory $BulkGenerateRequestsFactory,
        BulkGenerateRequestsRepositoryInterface $BulkGenerateRequestsRepository,
        ApiManager $apiManager,
        Keywords $keywords
    ) {
        $this->BulkGenerateRequestsFactory = $BulkGenerateRequestsFactory;
        $this->BulkGenerateRequestsRepository = $BulkGenerateRequestsRepository;
        $this->apiManager = $apiManager;
        $this->keywords = $keywords;
    }

    /**
     * Normalize record ID
     *
     * @param mixed $recordId
     * @return string|null
     */
    private function normalizeRecordId($recordId): ?string
    {
        if ($recordId === null) {
            return null;
        }

        if (is_array($recordId)) {
            return implode(',', $recordId);
        }

        return (string) $recordId;
    }

    /**
     * Set bulk request
     *
     * @param string $requestId
     * @param string $user
     * @param string $entityType
     * @param string $storeId
     * @param string $recordId = null
     * @return void
     */
    public function setBulkRequest($requestId, $user, $entityType, $storeId, $recordId = null)
    {
        $BulkGenerateRequests = $this->BulkGenerateRequestsFactory->create();

        $recordId = $this->normalizeRecordId($recordId);

        $existing = $BulkGenerateRequests->getCollection()
            ->addFieldToFilter('user', $user)
            ->getFirstItem();

        if ($existing->getId()) {
            // Update the existing record
            $existing->setRequestId($requestId);
            $existing->setEntityType($entityType);
            $existing->setStoreId($storeId);
            $existing->setRecordId($recordId);
            $BulkGenerateRequests = $existing;
        } else {
            // Record not found, create a new one
            $BulkGenerateRequests->setData([
                "user" => $user,
                "request_id" => $requestId,
                "entity_type" => $entityType,
                "store_id" => $storeId,
                "record_id" => $recordId
            ]);
        }

        $this->BulkGenerateRequestsRepository->save($BulkGenerateRequests);
    }

    /**
     * Delete bulk request
     *
     * @param string $requestId
     * @return void
     */
    public function delete($requestId)
    {
        $BulkGenerateRequests = $this->BulkGenerateRequestsFactory->create();

        $existing = $BulkGenerateRequests->getCollection()
            ->addFieldToFilter('request_id', $requestId)
            ->getFirstItem();

        if ($existing->getId()) {
            $this->BulkGenerateRequestsRepository->delete($existing);
        }
    }

    /**
     * Delete bulk request by user
     *
     * @param string $user
     * @return void
     */
    public function deleteByUser($user)
    {
        $BulkGenerateRequests = $this->BulkGenerateRequestsFactory->create();

        $existing = $BulkGenerateRequests->getCollection()
            ->addFieldToFilter('user', $user)
            ->getFirstItem();

        if ($existing->getId()) {
            $this->BulkGenerateRequestsRepository->delete($existing);
        }
    }

    /**
     * Delete all bulk requests
     *
     * @return void
     */
    public function deleteAll()
    {
        $BulkGenerateRequests = $this->BulkGenerateRequestsFactory->create();
        $BulkGenerateRequests->getCollection()->walk('delete');
    }

    /**
     * Get bulk requests
     *
     * @return bool
     */
    public function getBulkRequests()
    {
        $BulkGenerateRequests = $this->BulkGenerateRequestsFactory->create();
        $BulkGenerateRequests = $BulkGenerateRequests->getCollection();

        $requests = [];
        foreach ($BulkGenerateRequests as $BulkGenerateRequest) {
            try {
                $apiResponse = $this->apiManager->getBulkRequestById($BulkGenerateRequest->getRequestId());
                if (!empty($apiResponse)) {
                    $failed = isset($apiResponse['failed']) ? $apiResponse['failed'] : 0;
                    $completed = $apiResponse['completed'] !== $failed
                        ? $apiResponse['completed'] - $failed
                        : $apiResponse['completed'];
                    
                    $status = [
                        'id' => $BulkGenerateRequest->getRequestId(),
                        'message' => $this->getMessage(
                            $apiResponse['status'],
                            $apiResponse['failed'] ?? 0,
                            $apiResponse['total'] ?? 0
                        ),
                        'percent' => floor($apiResponse['completed'] / $apiResponse['total'] * 100),
                        'completed' => $completed,
                        'status' => $apiResponse['status'],
                        'queuedIds' => $apiResponse['queuedIds'],
                        'completedIds' => $apiResponse['completedIds'],
                        'user' => $apiResponse['userName'],
                        'subType' => $apiResponse['subType'] ?? 2,
                        'failed' => $apiResponse['failed'] ?? 0,
                        'failedIds' => $apiResponse['failedIds'] ?? [],
                        'total' => $apiResponse['total'] ?? 0,
                        'runningIds' => $apiResponse['runningIds'] ?? []
                    ];
                }
            } catch (\WriteTextAI\WriteTextAI\Exception\ApiException $e) {
                if ($e->getCode() !== 404) {
                    throw $e;
                }
                
                $getOptimizationResponse = $this->keywords->getOptimizationList(
                    $BulkGenerateRequest->getStoreId(),
                    ['Failed'],
                    null,
                    $BulkGenerateRequest->getEntityType(),
                    [$BulkGenerateRequest->getRecordId()]
                );
                $apiResponse = [
                    'status' => 'Completed',
                    'completed' => 1,
                    'total' => 1,
                    'queuedIds' => [],
                    'completedIds' => [],
                    'userName' => $BulkGenerateRequest->getUser(),
                    'estimatedEndTime' => null
                ];
                if (isset($getOptimizationResponse['result'])) {
                    foreach ($getOptimizationResponse['result'] as $key => $value) {
                        if ($value['status'] === 'Failed') {
                            $apiResponse = [
                                'id' => $BulkGenerateRequest->getRequestId(),
                                'message' => __('Error encountered'),
                                'status' => 'Completed',
                                'percent' => $value['completedSteps'] / $value['totalSteps'] * 100,
                                'completed' => 1,
                                'total' => 1,
                                'queuedIds' => [],
                                'completedIds' => [],
                                'user' => $BulkGenerateRequest->getUser(),
                                'subType' => 1,
                                'failed' => 1,
                                'failedIds' => [$value['recordId']],
                                'userName' => $BulkGenerateRequest->getUser(),
                                'estimatedEndTime' => null,
                                'runningIds' => []
                            ];
                        }
                    }
                }
            }

            $remaining = $this->calculateRemainingTime($apiResponse['estimatedEndTime']);
            $message = $this->getMessage($apiResponse, $remaining);
            $failed = isset($apiResponse['failed']) ? $apiResponse['failed'] : 0;
            $status = $apiResponse['status'];

            if ($status === 'Failed') {
                $status = 'Completed';
            }
            
            if ($apiResponse['status'] === 'Failed'
                && $apiResponse['completed'] === 1 && $apiResponse['total'] === 1) {
                $failed = 1;
            }

            $completed = $apiResponse['completed'] !== $failed
                ? $apiResponse['completed'] - $failed
                : $apiResponse['completed'];

            $requests[] = [
                'id' => $BulkGenerateRequest->getRequestId(),
                'message' => $message,
                'completed' => $completed,
                'percent' => floor($apiResponse['completed'] / $apiResponse['total'] * 100),
                'status' => $status,
                'queuedIds' => $apiResponse['queuedIds'],
                'completedIds' => $apiResponse['completedIds'],
                'user' => $apiResponse['userName'],
                'subType' => $apiResponse['subType'] ?? 0,
                'failed' => $failed,
                'failedIds' => $apiResponse['failedIds'] ?? [],
                'total' => $apiResponse['total'] ?? 0,
                'runningIds' => $apiResponse['runningIds'] ?? []
            ];
        }

        return $requests;
    }

    /**
     * Calculate remaining time
     *
     * @param string $estimatedEndTime
     * @return array
     */
    protected function calculateRemainingTime($estimatedEndTime)
    {
        $remaining = 0;
        $isMinutes = false;
        $result = [];
        $currentDatetime;
        $interval;

        if ($estimatedEndTime !== null) {
            $estimatedEndTime = new \DateTime($estimatedEndTime);
            $currentDatetime = new \DateTime();
            $interval = $estimatedEndTime->getTimestamp() - $currentDatetime->getTimestamp();
            // Convert seconds to minutes if it's more than or equal to 60 seconds
            $remaining = ceil($interval / 60);

            if ($remaining >= 60) {
                // Convert minutes to hours if it's more than or equal to 60 minutes
                $remaining = ceil($remaining / 60);
                $isMinutes = true;
            }
        }

        $result['remaining'] = $remaining;
        $result['is_minutes'] = $isMinutes;

        return $result;
    }

    /**
     * Get message
     *
     * @param array $apiResponse
     * @param array $remainingObj
     *
     * @return {String}
     */
    protected function getMessage($apiResponse, $remainingObj)
    {
        $message = '';
        $text = '';
        $failed = $apiResponse['failed'] ?? 0;
        $total = $apiResponse['total'] ?? 0;
        $status = $apiResponse['status'] ?? '';
        $recordType = $apiResponse['recordType'] ?? 'Product';
        $remaining = $remainingObj['remaining'] ?? 0;
        $isMinutes = $remainingObj['is_minutes'] ?? false;
        $runningIds = $apiResponse['runningIds'] ?? [];

        if ($failed === $total && $failed > 0) {
            return sprintf(__('Error encountered'));
        }
        switch ($status) {
            case 'Pending':
                $message = __('Processing in queue...');
                break;
            case 'Running':
                if ($remaining === 0) {
                    $message = __('Generating text...');
                    break;
                }
                if ($isMinutes) {
                    $text = __('Generating text... (Estimated time remaining %s minute/s)');
                } else {
                    $text = __('Generating text... (Estimated time remaining %s second/s)');
                }
                $message = sprintf($text, $remaining);
                break;
            case 'Completed':
                $scopeLabel = (string) $recordType === 'Category' ? __('categories') : __('products');
                $message = __('WriteText.ai is done generating text for your selected %1.', $scopeLabel);
                break;
            case 'Cancelling':
                $message = __('Some products have already been queued up for processing.'
                    . ' Cancelling unqueued items only…');
                break;
            case 'Cancelled':
                $message = __('This process has been cancelled.');
                break;
            case 'TimedOut':
                $message = __('WriteText.ai is currently experiencing a high volume of requests and has timed out.');
                break;
            case 'Failed':
                $message = __('Error encountered');
                break;
        }

        return $message;
    }
}
