<?php
declare(strict_types=1);

namespace WriteTextAI\WriteTextAI\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Stdlib\DateTime\DateTime;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

/**
 * Class ApiTokenHelper
 * Helper for generating and validating API tokens
 */
class ApiTokenHelper extends AbstractHelper
{
    const XML_PATH_TOKEN_SECRET = 'writetextai/api/token_secret';
    const XML_PATH_TOKEN_EXPIRY = 'writetextai/api/token_expiry_hours';
    const DEFAULT_TOKEN_EXPIRY_HOURS = 24;

    /**
     * @var EncryptorInterface
     */
    private $encryptor;

    /**
     * @var DateTime
     */
    private $dateTime;

    /**
     * @param Context $context
     * @param EncryptorInterface $encryptor
     * @param DateTime $dateTime
     */
    public function __construct(
        Context $context,
        EncryptorInterface $encryptor,
        DateTime $dateTime
    ) {
        parent::__construct($context);
        $this->encryptor = $encryptor;
        $this->dateTime = $dateTime;
    }

    /**
     * Generate a secure API token
     *
     * @param string $identifier Unique identifier for the third-party system
     * @param int|null $expiryHours Token expiry in hours (null for no expiry)
     * @return string
     */
    public function generateToken(string $identifier, ?int $expiryHours = null): string
    {
        $expiryHours = $expiryHours ?? $this->getTokenExpiryHours();
        $currentTime = $this->dateTime->gmtTimestamp();
        $expiryTime = $expiryHours > 0 ? $currentTime + ($expiryHours * 3600) : 0;
        
        $tokenData = [
            'identifier' => $identifier,
            'issued_at' => $currentTime,
            'expires_at' => $expiryTime,
            'salt' => bin2hex(random_bytes(16))
        ];

        $tokenString = base64_encode(json_encode($tokenData));
        $signature = $this->generateSignature($tokenString);
        
        return base64_encode($tokenString . '.' . $signature);
    }

    /**
     * Validate API token
     *
     * @param string $token
     * @return bool
     */
    public function validateToken(string $token): bool
    {
        try {
            $decodedToken = base64_decode($token);
            if (!$decodedToken) {
                return false;
            }

            $parts = explode('.', $decodedToken);
            if (count($parts) !== 2) {
                return false;
            }

            [$tokenString, $signature] = $parts;
            
            // Validate signature
            if (!$this->verifySignature($tokenString, $signature)) {
                return false;
            }

            $tokenData = json_decode(base64_decode($tokenString), true);
            if (!$tokenData) {
                return false;
            }

            // Check required fields
            if (!isset($tokenData['identifier'], $tokenData['issued_at'], $tokenData['expires_at'])) {
                return false;
            }

            // Check expiry
            if ($tokenData['expires_at'] > 0 && $this->dateTime->gmtTimestamp() > $tokenData['expires_at']) {
                return false;
            }

            return true;
        } catch (\Exception $e) {
            $this->_logger->error('Token validation error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Extract token data
     *
     * @param string $token
     * @return array|null
     */
    public function getTokenData(string $token): ?array
    {
        if (!$this->validateToken($token)) {
            return null;
        }

        try {
            $decodedToken = base64_decode($token);
            $parts = explode('.', $decodedToken);
            $tokenString = $parts[0];
            
            return json_decode(base64_decode($tokenString), true);
        } catch (\Exception $e) {
            $this->_logger->error('Token data extraction error: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Generate signature for token
     *
     * @param string $data
     * @return string
     */
    private function generateSignature(string $data): string
    {
        $secret = $this->getTokenSecret();
        return hash_hmac('sha256', $data, $secret);
    }

    /**
     * Verify token signature
     *
     * @param string $data
     * @param string $signature
     * @return bool
     */
    private function verifySignature(string $data, string $signature): bool
    {
        $expectedSignature = $this->generateSignature($data);
        return hash_equals($expectedSignature, $signature);
    }

    /**
     * Get token secret from configuration
     *
     * @return string
     */
    private function getTokenSecret(): string
    {
        $secret = $this->scopeConfig->getValue(
            self::XML_PATH_TOKEN_SECRET,
            ScopeInterface::SCOPE_STORE
        );

        if (!$secret) {
            // Generate a default secret based on Magento's encryption key
            $secret = $this->encryptor->getHash('writetextai_api_token_secret');
        }

        return $secret;
    }

    /**
     * Get token expiry hours from configuration
     *
     * @return int
     */
    private function getTokenExpiryHours(): int
    {
        return (int) $this->scopeConfig->getValue(
            self::XML_PATH_TOKEN_EXPIRY,
            ScopeInterface::SCOPE_STORE
        ) ?: self::DEFAULT_TOKEN_EXPIRY_HOURS;
    }

    /**
     * Generate a token for a specific third-party system
     *
     * @param string $systemName
     * @param string $description
     * @param int|null $expiryHours
     * @return array
     */
    public function generateSystemToken(
        string $systemName,
        string $description = '',
        ?int $expiryHours = null
    ): array {
        $identifier = 'system_' . $systemName . '_' . time();
        $token = $this->generateToken($identifier, $expiryHours);
        
        return [
            'token' => $token,
            'identifier' => $identifier,
            'system_name' => $systemName,
            'description' => $description,
            'generated_at' => $this->dateTime->gmtDate(),
            'expires_at' => $expiryHours > 0 ?
                $this->dateTime->gmtDate('Y-m-d H:i:s', time() + ($expiryHours * 3600)) :
                'Never',
            'usage_instructions' => [
                'header' => 'Authorization: Bearer ' . $token,
                'endpoint' => '/rest/V1/custom/transfer/request',
                'method' => 'POST'
            ]
        ];
    }
}
