<?php

namespace Autogedal\Extend\Controller\Answer;

use Magediary\ProductQuestion\Model\ResourceModel\ActivityLog;
use Magediary\ProductQuestion\Model\ResourceModel\Answer;
use Magediary\ProductQuestion\Model\Email\MailAbuseInterface;
use Magento\Customer\Model\Session;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Json\Helper\Data;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\App\Cache\Type\Config as CacheTypeConfig;
use Magento\Framework\Cache\FrontendInterface;
use Psr\Log\LoggerInterface;
use Magento\Framework\DataObject;
use Magento\Framework\UrlInterface;
use Magento\Framework\Validator\StringLength;
use Magento\Framework\Validator\Range;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;

class Trigger implements HttpPostActionInterface, CsrfAwareActionInterface
{
    /**
     * Allowed action types
     */
    const ALLOWED_TYPES = ['like', 'dislike', 'abuse'];
    
    /**
     * Rate limiting cache key prefix
     */
    const RATE_LIMIT_CACHE_PREFIX = 'pq_rate_limit_';
    
    /**
     * Rate limit: max actions per minute
     */
    const RATE_LIMIT_MAX_ACTIONS = 10;
    
    /**
     * Rate limit time window in seconds
     */
    const RATE_LIMIT_WINDOW = 60;

    /**
     * @var \Magento\Framework\View\Result\Page
     */
    protected $resultPageFactory;

    /**
     * @var Data
     */
    protected $jsonHelper;

    /**
     * @var JsonFactory
     */
    protected $resultJsonFactory;

    /**
     * @var Answer
     */
    protected $resourceAnswer;

    /**
     * @var ActivityLog
     */
    protected $resourceLog;

    /**
     * @var MailAbuseInterface
     */
    protected $mailAbuse;

    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var Session
     */
    public $customerSession;

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

    /**
     * @var Context
     */
    private $context;

    /**
     * @var FrontendInterface
     */
    private $cache;

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

    /**
     * @param Context $context
     * @param PageFactory $resultPageFactory
     * @param Data $jsonHelper
     * @param JsonFactory $resultJsonFactory
     * @param Session $customerSession
     * @param Answer $resourceAnswer
     * @param ActivityLog $resourceLog
     * @param MailAbuseInterface $mailAbuse
     * @param UrlInterface $urlBuilder
     * @param CacheTypeConfig $cache
     * @param ProductRepositoryInterface $productRepository
     * @param LoggerInterface|null $logger
     */
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory,
        Data $jsonHelper,
        JsonFactory $resultJsonFactory,
        Session $customerSession,
        Answer $resourceAnswer,
        ActivityLog $resourceLog,
        MailAbuseInterface $mailAbuse,
        UrlInterface $urlBuilder,
        CacheTypeConfig $cache,
        ProductRepositoryInterface $productRepository,
        LoggerInterface $logger = null
    ) {
        $this->context = $context;
        $this->customerSession = $customerSession;
        $this->resultPageFactory = $resultPageFactory;
        $this->jsonHelper = $jsonHelper;
        $this->resultJsonFactory = $resultJsonFactory;
        $this->resourceAnswer = $resourceAnswer;
        $this->resourceLog = $resourceLog;
        $this->mailAbuse = $mailAbuse;
        $this->urlBuilder = $urlBuilder;
        $this->cache = $cache;
        $this->productRepository = $productRepository;
        $this->logger = $logger ?: ObjectManager::getInstance()
            ->get(LoggerInterface::class);
    }

    /**
     * @inheritDoc
     */
    public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
    {
        return new InvalidRequestException(
            __('Invalid security token. Please refresh the page and try again.'),
            []
        );
    }

    /**
     * @inheritDoc
     */
    public function validateForCsrf(RequestInterface $request): ?bool
    {
        return null; // Use default CSRF validation
    }

    /**
     * Execute trigger action for like, dislike and abuse
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $resultJson = $this->resultJsonFactory->create();
        
        try {
            // Validate user is logged in
            if (!$this->customerSession->isLoggedIn()) {
                return $resultJson->setData([
                    'success' => false,
                    'alert' => __(
                        'Please <a href="%1">Sign in</a> or <a href="%2">create an account</a> first.',
                        $this->getLoginUrl(),
                        $this->getRegisterUrl()
                    )
                ]);
            }

            $customerId = $this->customerSession->getCustomerId();
            
            // Validate and sanitize input
            $validationResult = $this->validateInput();
            if (!$validationResult['valid']) {
                return $resultJson->setData([
                    'success' => false,
                    'message' => $validationResult['message']
                ]);
            }

            $id = $validationResult['id'];
            $type = $validationResult['type'];
            $productId = $validationResult['product_id'];

            // Check rate limiting
            if (!$this->checkRateLimit($customerId)) {
                return $resultJson->setData([
                    'success' => false,
                    'message' => __('Too many requests. Please try again later.')
                ]);
            }

            // Verify authorization (answer belongs to product)
            if (!$this->verifyAnswerProductRelation($id, $productId)) {
                return $resultJson->setData([
                    'success' => false,
                    'message' => __('Invalid request.')
                ]);
            }

            // Process the action
            $abuse = 0;
            
            if ($type == 'like' &&
                $this->resourceAnswer->isAlreadyLikedByThisUser($id, $customerId, $productId)
            ) {
                $dontUpdateJustRevert = true;
            }
            if ($type == 'like' || $type == 'dislike') {
                $this->resourceLog->revertLikes($id, $customerId);
                $this->resourceAnswer->updateLikeDislikeCount($id);
            }
            
            switch ($type) {
                case 'like':
                    if (!isset($dontUpdateJustRevert)) {
                        $this->resourceLog->addLikeLog($id, $customerId, $productId);
                        $this->resourceAnswer->updateLikeDislikeCount($id);
                    }
                    break;
                case 'dislike':
                    $this->resourceLog->addDislikeLog($id, $customerId, $productId);
                    $this->resourceAnswer->updateLikeDislikeCount($id);
                    break;
                case 'abuse':
                    if ($this->resourceLog->getAbuseCount($id, $customerId) == 0) {
                        $this->resourceLog->reportAbuse($id, $customerId, $productId);
                        $this->sendAbuseEmail($id, $customerId, $productId);
                        $abuse = 1;
                    }
                    break;
            }

            // Update rate limiting counter
            $this->updateRateLimit($customerId);

            $response = [
                'success' => true,
                'likes' => $this->resourceLog->getLikeCount($id),
                'dislikes' => $this->resourceLog->getDislikeCount($id),
                'abuse' => $abuse
            ];

            $this->context->getEventManager()->dispatch(
                'magediary_productquestion_activitylog_trigger',
                ['product_id' => $productId]
            );

            return $resultJson->setData($response);
            
        } catch (\Exception $e) {
            // Log the actual error for debugging
            $this->logger->critical('ProductQuestion Trigger Error: ' . $e->getMessage(), [
                'exception' => $e,
                'customer_id' => $this->customerSession->getCustomerId(),
                'request_params' => $this->context->getRequest()->getParams()
            ]);
            
            // Return generic error message to user
            return $resultJson->setData([
                'success' => false,
                'message' => __('An error occurred while processing your request. Please try again.')
            ]);
        }
    }

    /**
     * Validate and sanitize input parameters
     *
     * @return array
     */
    private function validateInput(): array
    {
        $request = $this->context->getRequest();
        
        // Validate ID
        $id = $request->getParam('id');
        if (!$id || !is_numeric($id) || $id <= 0) {
            return [
                'valid' => false,
                'message' => __('Invalid answer ID.')
            ];
        }
        $id = (int) $id;

        // Validate type
        $type = $request->getParam('type');
        if (!$type || !in_array($type, self::ALLOWED_TYPES, true)) {
            return [
                'valid' => false,
                'message' => __('Invalid action type.')
            ];
        }
        $type = trim($type);

        // Validate product ID
        $productId = $request->getParam('product_id');
        if (!$productId || !is_numeric($productId) || $productId <= 0) {
            return [
                'valid' => false,
                'message' => __('Invalid product ID.')
            ];
        }
        $productId = (int) $productId;

        // Verify product exists
        try {
            $this->productRepository->getById($productId);
        } catch (NoSuchEntityException $e) {
            return [
                'valid' => false,
                'message' => __('Product not found.')
            ];
        }

        return [
            'valid' => true,
            'id' => $id,
            'type' => $type,
            'product_id' => $productId
        ];
    }

    /**
     * Check if user has exceeded rate limit
     *
     * @param int $customerId
     * @return bool
     */
    private function checkRateLimit(int $customerId): bool
    {
        $cacheKey = self::RATE_LIMIT_CACHE_PREFIX . $customerId;
        $cached = $this->cache->load($cacheKey);
        
        if ($cached) {
            $data = json_decode($cached, true);
            $currentTime = time();
            
            // Clean old entries
            $data = array_filter($data, function($timestamp) use ($currentTime) {
                return ($currentTime - $timestamp) < self::RATE_LIMIT_WINDOW;
            });
            
            // Check if limit exceeded
            if (count($data) >= self::RATE_LIMIT_MAX_ACTIONS) {
                return false;
            }
        }
        
        return true;
    }

    /**
     * Update rate limiting counter
     *
     * @param int $customerId
     */
    private function updateRateLimit(int $customerId): void
    {
        $cacheKey = self::RATE_LIMIT_CACHE_PREFIX . $customerId;
        $cached = $this->cache->load($cacheKey);
        $currentTime = time();
        
        if ($cached) {
            $data = json_decode($cached, true);
            // Clean old entries
            $data = array_filter($data, function($timestamp) use ($currentTime) {
                return ($currentTime - $timestamp) < self::RATE_LIMIT_WINDOW;
            });
        } else {
            $data = [];
        }
        
        // Add current timestamp
        $data[] = $currentTime;
        
        // Save back to cache
        $this->cache->save(
            json_encode($data),
            $cacheKey,
            [CacheTypeConfig::CACHE_TAG],
            self::RATE_LIMIT_WINDOW
        );
    }

    /**
     * Verify that the answer belongs to the specified product
     *
     * @param int $answerId
     * @param int $productId
     * @return bool
     */
    private function verifyAnswerProductRelation(int $answerId, int $productId): bool
    {
        // This should query your answer table to verify the relationship
        // Implementation depends on your database schema
        try {
            return $this->resourceAnswer->verifyAnswerProductRelation($answerId, $productId);
        } catch (\Exception $e) {
            $this->logger->error('Failed to verify answer-product relation: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Send abuse email
     *
     * @param int $id
     * @param int $customerId
     * @param int $productId
     */
    private function sendAbuseEmail(int $id, int $customerId, int $productId): void
    {
        try {
            $this->mailAbuse->send(
                new DataObject([
                    'id' => $id,
                    'customer_id' => $customerId,
                    'product_id' => $productId
                ])
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to send abuse email: ' . $e->getMessage());
        }
    }

    /**
     * Retrieve customer login page url
     *
     * @return string
     */
    public function getLoginUrl(): string
    {
        return $this->urlBuilder->getUrl('customer/account/login');
    }

    /**
     * Retrieve customer register form url
     *
     * @return string
     */
    public function getRegisterUrl(): string
    {
        return $this->urlBuilder->getUrl('customer/account/create');
    }
}