<?php

declare(strict_types=1);

namespace Autogedal\CookieLimit\Plugin;

use Magento\Framework\Stdlib\Cookie\PhpCookieManager;
use Magento\Framework\Stdlib\Cookie\SensitiveCookieMetadata;
use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata;
use Magento\Framework\Phrase;
use Psr\Log\LoggerInterface;
use Autogedal\CookieLimit\Helper\Config;

/**
 * Plugin to enhance cookie limit management in PhpCookieManager
 */
class PhpCookieManagerPlugin
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var Config
     */
    private $config;

    /**
     * @param LoggerInterface $logger
     * @param Config $config
     */
    public function __construct(LoggerInterface $logger, Config $config)
    {
        $this->logger = $logger;
        $this->config = $config;
    }

    /**
     * Around plugin for setSensitiveCookie to check cookie limits and prevent setting if exceeded
     *
     * @param PhpCookieManager $subject
     * @param callable $proceed
     * @param string $name
     * @param string $value
     * @param SensitiveCookieMetadata|null $metadata
     * @return void
     */
    public function aroundSetSensitiveCookie(
        PhpCookieManager $subject,
        callable $proceed,
        $name,
        $value,
        SensitiveCookieMetadata $metadata = null
    ) {
        if (!$this->checkCookieCountLimit($name)) {
            // Silently skip cookie setting to prevent overflow
            return;
        }
        return $proceed($name, $value, $metadata);
    }

    /**
     * Around plugin for setPublicCookie to check cookie limits and prevent setting if exceeded
     *
     * @param PhpCookieManager $subject
     * @param callable $proceed
     * @param string $name
     * @param string $value
     * @param PublicCookieMetadata|null $metadata
     * @return void
     */
    public function aroundSetPublicCookie(
        PhpCookieManager $subject,
        callable $proceed,
        $name,
        $value,
        PublicCookieMetadata $metadata = null
    ) {
        if (!$this->checkCookieCountLimit($name)) {
            // Silently skip cookie setting to prevent overflow
            return;
        }
        return $proceed($name, $value, $metadata);
    }

    /**
     * Check if setting a new cookie would exceed the maximum cookie limit
     * Returns false if cookie should not be set to prevent overflow
     *
     * @param string $name
     * @return bool
     */
    private function checkCookieCountLimit($name)
    {
        $maxCookies = $this->config->getMaxCookies();
        
        $numCookies = count($_COOKIE);

        if (!isset($_COOKIE[$name])) {
            $numCookies++;
        }

        if ($numCookies > $maxCookies) {
            $this->logger->warning(
                new Phrase('Unable to send the cookie. Maximum number of cookies would be exceeded. Cookie setting prevented to maintain application stability.'),
                [
                    'cookies' => array_keys($_COOKIE),
                    'attempted_cookie' => $name,
                    'current_count' => count($_COOKIE),
                    'configured_limit' => $maxCookies
                ]
            );
            
            return false;
        }
        
        return true;
    }
}