<?php
namespace Autogedal\Extend\CustomerData;

use Magento\Customer\CustomerData\SectionSourceInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Psr\Log\LoggerInterface;
use Magento\Customer\Model\Session as CustomerSession;

class AnswerLikes implements SectionSourceInterface
{
    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var \Magediary\ProductQuestion\Model\AnswerFactory
     */
    protected $answerFactory;

    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @var CustomerSession
     */
    protected $customerSession;

    /**
     * Maximum allowed JSON size (in bytes)
     */
    const MAX_JSON_SIZE = 1024;

    /**
     * Rate limiting - max requests per minute per session
     */
    const MAX_REQUESTS_PER_MINUTE = 30;

    public function __construct(
        RequestInterface $request,
        \Magediary\ProductQuestion\Model\AnswerFactory $answerFactory,
        ProductRepositoryInterface $productRepository,
        LoggerInterface $logger,
        CustomerSession $customerSession
    ) {
        $this->request = $request;
        $this->answerFactory = $answerFactory;
        $this->productRepository = $productRepository;
        $this->logger = $logger;
        $this->customerSession = $customerSession;
    }

    /**
     * @return array
     */
    public function getSectionData()
    {
        try {
            if (!$this->checkRateLimit()) {
                $this->logger->warning('Rate limit exceeded for answer likes section', [
                    'session_id' => $this->customerSession->getSessionId(),
                    'ip' => $this->request->getClientIp()
                ]);
                return ['error' => 'Rate limit exceeded'];
            }

            $productId = $this->getValidatedProductId();

            if (!$productId) {
                return ['error' => 'Invalid product ID'];
            }

            if (!$this->isProductAccessible($productId)) {
                $this->logger->info('Unauthorized access attempt to product answers', [
                    'product_id' => $productId,
                    'session_id' => $this->customerSession->getSessionId(),
                    'ip' => $this->request->getClientIp()
                ]);
                return ['error' => 'Product not accessible'];
            }

            $answers = $this->getAnswersForProduct($productId);
            return $this->formatAnswerData($answers);

        } catch (\Exception $e) {
            $this->logger->error('Error in AnswerLikes getSectionData', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return ['error' => 'Unable to load answer data'];
        }
    }

    /**
     * Get and validate product ID from request
     *
     * @return int|null
     */
    protected function getValidatedProductId()
    {
        $productId = $this->request->getParam('product_id');
        if ($productId) {
            $productId = filter_var($productId, FILTER_VALIDATE_INT);
            if ($productId && $productId > 0) {
                return $productId;
            }
        }

        $sectionsData = $this->getValidatedSectionsData();
        if ($sectionsData && isset($sectionsData['answer_likes']['product_id'])) {
            $productId = filter_var($sectionsData['answer_likes']['product_id'], FILTER_VALIDATE_INT);
            if ($productId && $productId > 0) {
                return $productId;
            }
        }

        return null;
    }

    /**
     * Safely decode and validate sections_data parameter
     *
     * @return array|null
     */
    protected function getValidatedSectionsData()
    {
        $raw = $this->request->getParam('sections_data');

        if (!$raw || !is_string($raw)) {
            return null;
        }

        if (strlen($raw) > self::MAX_JSON_SIZE) {
            $this->logger->warning('Sections data exceeds size limit', [
                'size' => strlen($raw),
                'limit' => self::MAX_JSON_SIZE
            ]);
            return null;
        }

        try {
            $json = urldecode($raw);

            if (strlen($json) > self::MAX_JSON_SIZE) {
                return null;
            }

            $json = mb_convert_encoding($json, 'UTF-8', 'UTF-8');

            $sectionsData = json_decode($json, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->logger->warning('Invalid JSON in sections_data', [
                    'error' => json_last_error_msg()
                ]);
                return null;
            }

            if (!is_array($sectionsData)) {
                return null;
            }

            return $sectionsData;
        } catch (\Exception $e) {
            $this->logger->warning('Error decoding sections_data', [
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * Check if product exists and is accessible to current user
     *
     * @param int $productId
     * @return bool
     */
    protected function isProductAccessible($productId)
    {
        try {
            $product = $this->productRepository->getById($productId);

            if (!$product->getStatus() || $product->getVisibility() == 1) {
                return false;
            }

            return true;
        } catch (NoSuchEntityException $e) {
            return false;
        } catch (\Exception $e) {
            $this->logger->error('Error checking product accessibility', [
                'product_id' => $productId,
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Get answers for product with additional security checks
     *
     * @param int $productId
     * @return array
     */
    protected function getAnswersForProduct($productId)
    {
        try {
            $collection = $this->answerFactory->create()->getCollection()
                ->addFieldToFilter('product_id', ['eq' => $productId])
                ->addFieldToFilter('status', ['eq' => 1])
                ->setPageSize(100)
                ->setCurPage(1);

            return $collection->getItems();
        } catch (\Exception $e) {
            $this->logger->error('Error loading answers for product', [
                'product_id' => $productId,
                'error' => $e->getMessage()
            ]);
            return [];
        }
    }

    /**
     * Format answer data for safe output
     *
     * @param array $answers
     * @return array
     */
    protected function formatAnswerData($answers)
    {
        $data = ['likes' => []];

        if (!empty($answers)) {
            foreach ($answers as $answer) {
                $answerId = (int) $answer->getId();
                $likes = (int) $answer->getLikes();

                if ($answerId > 0) {
                    $data['likes'][$answerId] = [
                        'count' => max(0, $likes),
                        'answer_id' => $answerId
                    ];
                }
            }
        }

        return $data;
    }

    /**
     * Simple rate limiting check
     *
     * @return bool
     */
    protected function checkRateLimit()
    {
        $sessionId = $this->customerSession->getSessionId();
        $cacheKey = 'answer_likes_rate_limit_' . $sessionId;

        $requests = $this->customerSession->getData($cacheKey) ?: [];
        $currentTime = time();

        // Clean old requests (older than 1 minute)
        $requests = array_filter($requests, function($timestamp) use ($currentTime) {
            return ($currentTime - $timestamp) < 60;
        });

        // Check if limit exceeded
        if (count($requests) >= self::MAX_REQUESTS_PER_MINUTE) {
            return false;
        }

        $requests[] = $currentTime;
        $this->customerSession->setData($cacheKey, $requests);

        return true;
    }
}
