<?php

namespace PortalPeople\OrderWebhook\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Store\Model\ScopeInterface;
use Psr\Log\LoggerInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\OrderRepositoryInterface;

/**
 * Webhook sender service
 */
class WebhookSender
{
    const CONFIG_PATH_ENABLED = 'order_webhook/general/enabled';
    const CONFIG_PATH_API_URL = 'order_webhook/general/api_url';
    const CONFIG_PATH_API_KEY = 'order_webhook/general/api_key';
    const CONFIG_PATH_TIMEOUT = 'order_webhook/general/timeout';
    const CONFIG_PATH_RETRY_ATTEMPTS = 'order_webhook/general/retry_attempts';
    const CONFIG_PATH_LOG_ENABLED = 'order_webhook/general/log_enabled';
    const CONFIG_PATH_NGAUGE_BASE_URL = 'order_webhook/ngauge/base_url';

    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * @var Curl
     */
    private $curl;

    /**
     * @var Json
     */
    private $jsonSerializer;

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

    /**
     * @var OrderDataBuilder
     */
    private $orderDataBuilder;

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;

    /**
     * @param ScopeConfigInterface $scopeConfig
     * @param Curl $curl
     * @param Json $jsonSerializer
     * @param LoggerInterface $logger
     * @param OrderDataBuilder $orderDataBuilder
     * @param OrderRepositoryInterface $orderRepository
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        Curl $curl,
        Json $jsonSerializer,
        LoggerInterface $logger,
        OrderDataBuilder $orderDataBuilder,
        OrderRepositoryInterface $orderRepository
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->curl = $curl;
        $this->jsonSerializer = $jsonSerializer;
        $this->logger = $logger;
        $this->orderDataBuilder = $orderDataBuilder;
        $this->orderRepository = $orderRepository;
    }

    /**
     * Send order data to webhook endpoint
     *
     * @param OrderInterface $order
     * @param string $eventType
     * @return bool
     */
    public function sendOrderData(OrderInterface $order, string $eventType): bool
    {
        if (!$this->isEnabled($order->getStoreId())) {
            return false;
        }

        $apiUrl = $this->getApiUrl($order->getStoreId());
        if (empty($apiUrl)) {
            $this->log('API URL is not configured');
            return false;
        }

        $orderData = $this->orderDataBuilder->build($order, $eventType);
        $jsonData = $this->jsonSerializer->serialize($orderData);

        $retryAttempts = (int)$this->getRetryAttempts($order->getStoreId());
        $attempt = 0;

        while ($attempt <= $retryAttempts) {
            try {
                $response = $this->sendRequest($apiUrl, $jsonData, $order->getStoreId());
                
                if ($response['success']) {
                    $this->log("Webhook sent successfully for order {$order->getIncrementId()} on attempt " . ($attempt + 1));
                    $this->saveNgaugeResponseFields($order, $response['response']);
                    return true;
                }
                
                $this->log("Webhook failed for order {$order->getIncrementId()} on attempt " . ($attempt + 1) . ": " . $response['error']);
                
            } catch (\Exception $e) {
                $this->log("Exception sending webhook for order {$order->getIncrementId()} on attempt " . ($attempt + 1) . ": " . $e->getMessage());
            }
            
            $attempt++;
            
            if ($attempt <= $retryAttempts) {
                sleep(pow(2, $attempt)); // Exponential backoff
            }
        }

        $this->log("All retry attempts failed for order {$order->getIncrementId()}");
        return false;
    }

    /**
     * Send HTTP request to webhook endpoint
     *
     * @param string $url
     * @param string $jsonData
     * @param int $storeId
     * @return array
     */
    private function sendRequest(string $url, string $jsonData, int $storeId): array
    {
        $timeout = (int)$this->getTimeout($storeId);
        $apiKeyEncrypted = $this->getApiKey($storeId);
        /** @var \Magento\Framework\Encryption\EncryptorInterface $encryptor */
        $encryptor = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Encryption\EncryptorInterface::class);
        $apiKey = $encryptor->decrypt($apiKeyEncrypted);
        
        $this->curl->setTimeout($timeout);

        // Build headers array and set them once so we don't overwrite Content-Type
        $headers = [
            'Content-Type' => 'application/json',
            'User-Agent' => 'Magento2-OrderWebhook/1.0.0'
        ];

        if (!empty($apiKey)) {
            $headers['Authorization'] = 'Bearer ' . $apiKey;
        }

        $this->curl->setHeaders($headers);

        $this->curl->post($url, $jsonData);

        $httpCode = $this->curl->getStatus();
        $response = $this->curl->getBody();

        if ($httpCode >= 200 && $httpCode < 300) {
            return ['success' => true, 'response' => $response];
        }

        return [
            'success' => false,
            'error' => "HTTP {$httpCode}: {$response}",
            'response' => $response
        ];
    }

    /**
     * Check if webhook is enabled
     *
     * @param int $storeId
     * @return bool
     */
    private function isEnabled(int $storeId): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::CONFIG_PATH_ENABLED,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    /**
     * Get API URL
     *
     * @param int $storeId
     * @return string
     */
    private function getApiUrl(int $storeId): string
    {
        return (string)$this->scopeConfig->getValue(
            self::CONFIG_PATH_API_URL,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    /**
     * Get API key
     *
     * @param int $storeId
     * @return string
     */
    private function getApiKey(int $storeId): string
    {
        return (string)$this->scopeConfig->getValue(
            self::CONFIG_PATH_API_KEY,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }

    /**
     * Get timeout value
     *
     * @param int $storeId
     * @return int
     */
    private function getTimeout(int $storeId): int
    {
        return (int)$this->scopeConfig->getValue(
            self::CONFIG_PATH_TIMEOUT,
            ScopeInterface::SCOPE_STORE,
            $storeId
        ) ?: 30;
    }

    /**
     * Get retry attempts
     *
     * @param int $storeId
     * @return int
     */
    private function getRetryAttempts(int $storeId): int
    {
        return (int)$this->scopeConfig->getValue(
            self::CONFIG_PATH_RETRY_ATTEMPTS,
            ScopeInterface::SCOPE_STORE,
            $storeId
        ) ?: 3;
    }

    /**
     * Return configuration details for display (e.g. CLI info command)
     *
     * @param int $storeId
     * @return array
     */
    public function getConfigInfo(int $storeId = 0): array
    {
        $apiKeyEncrypted = $this->getApiKey($storeId);
        $apiKey = '';
        if (!empty($apiKeyEncrypted)) {
            try {
                /** @var \Magento\Framework\Encryption\EncryptorInterface $encryptor */
                $encryptor = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Encryption\EncryptorInterface::class);
                $decrypted = $encryptor->decrypt($apiKeyEncrypted);
                $apiKey = strlen($decrypted) > 4
                    ? str_repeat('*', strlen($decrypted) - 4) . substr($decrypted, -4)
                    : str_repeat('*', strlen($decrypted));
            } catch (\Exception $e) {
                $apiKey = '(error decrypting)';
            }
        }

        return [
            'enabled'        => $this->isEnabled($storeId) ? 'Yes' : 'No',
            'api_url'        => $this->getApiUrl($storeId) ?: '(not set)',
            'api_key'        => $apiKey ?: '(not set)',
            'timeout'        => $this->getTimeout($storeId) . 's',
            'retry_attempts' => $this->getRetryAttempts($storeId),
            'ngauge_base_url'=> (string)$this->scopeConfig->getValue(self::CONFIG_PATH_NGAUGE_BASE_URL, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId) ?: '(not set)',
            'log_enabled'    => $this->scopeConfig->isSetFlag(self::CONFIG_PATH_LOG_ENABLED, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId) ? 'Yes' : 'No',
        ];
    }

    /**
     * Parse webhook response and save NGauge order fields back to the Magento order
     *
     * @param OrderInterface $order
     * @param string $responseBody
     * @return void
     */
    private function saveNgaugeResponseFields(OrderInterface $order, string $responseBody): void
    {
        try {
            $data = $this->jsonSerializer->unserialize($responseBody);

            $no   = $data['ngauge_order_no']   ?? $data['order_no']   ?? null;
            $id   = $data['ngauge_order_id']   ?? $data['order_id']   ?? null;
            $link = $data['ngauge_order_link'] ?? $data['order_link'] ?? null;

            if ($no === null && $id === null && $link === null) {
                return;
            }

            // If the link is relative, prepend the configured NGauge base URL
            if ($link !== null && !preg_match('#^https?://#i', $link)) {
                $baseUrl = rtrim(
                    (string)$this->scopeConfig->getValue(self::CONFIG_PATH_NGAUGE_BASE_URL, ScopeInterface::SCOPE_STORE, $order->getStoreId()),
                    '/'
                );
                $link = $baseUrl . '/' . ltrim($link, '/');
            }

            if ($no   !== null) { $order->setData('ngauge_order_no',   $no); }
            if ($id   !== null) { $order->setData('ngauge_order_id',   (string)$id); }
            if ($link !== null) { $order->setData('ngauge_order_link', $link); }

            $this->orderRepository->save($order);
            $this->log("Saved NGauge fields for order {$order->getIncrementId()} — no:{$no} id:{$id}");
        } catch (\Exception $e) {
            $this->log("Failed to save NGauge response fields for order {$order->getIncrementId()}: " . $e->getMessage());
        }
    }

    /**
     * Log message if logging is enabled
     *
     * @param string $message
     * @param int $storeId
     * @return void
     */
    private function log(string $message, int $storeId = 0): void
    {
        if ($this->scopeConfig->isSetFlag(self::CONFIG_PATH_LOG_ENABLED, ScopeInterface::SCOPE_STORE, $storeId)) {
            $this->logger->info('[OrderWebhook] ' . $message);
        }
    }
}
