<?php

namespace TbiBank\FusionPay\Controller\Api;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableProductType;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\UrlInterface as UrlBuilder;
use Magento\Catalog\Helper\Image as ImageHelper;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use TbiBank\FusionPay\Model\Cryptor;
use TbiBank\FusionPay\Helper\Data as FusionPayHelper;
use TbiBank\FusionPay\Logger\FusionPayLogger;

class RegisterProductApplication extends Action
{
    private $resultJsonFactory;
    private $productRepository;
    private $configurableProductType;
    private $curlClient;
    private $urlBuilder;
    private $cryptor;
    private $fusionPayHelper;
    private $imageHelper;
    private $filesystem;
    private $tbiCalculatorEndpoint;
    private $encryptionKey;
    private $resellerCode;
    private $resellerKey;
    protected $logger;

    public function __construct(
        Context $context,
        JsonFactory $resultJsonFactory,
        ProductRepositoryInterface $productRepository,
        ConfigurableProductType $configurableProductType,
        Curl $curlClient,
        UrlBuilder $urlBuilder,
        ImageHelper $imageHelper,
        Cryptor $cryptor,
        FusionPayHelper $fusionPayHelper,
        FusionPayLogger $logger,
        Filesystem $filesystem
    ) {
        parent::__construct($context);
        $this->resultJsonFactory = $resultJsonFactory;
        $this->productRepository = $productRepository;
        $this->configurableProductType = $configurableProductType;
        $this->curlClient = $curlClient;
        $this->urlBuilder = $urlBuilder;
        $this->imageHelper = $imageHelper;
        $this->cryptor = $cryptor;
        $this->fusionPayHelper = $fusionPayHelper;
        $this->logger = $logger;
        $this->filesystem = $filesystem;
        $this->initializeConfigurations();
    }

    /**
     * Initializes API endpoint and security configurations.
     */
    private function initializeConfigurations(): void
    {
        $this->tbiCalculatorEndpoint = $this->fusionPayHelper->getConstValue('TBI_CALC_URL');
        $this->encryptionKey = $this->fusionPayHelper->getConfigValue('tbi_settings/general_settings/encryption_key');
        $this->resellerCode = $this->fusionPayHelper->getConfigValue('tbi_settings/general_settings/reseller_code');
        $this->resellerKey = $this->fusionPayHelper->getConfigValue('tbi_settings/general_settings/reseller_key');
    }

    public function execute()
    {
        $result = $this->resultJsonFactory->create();
        $params = $this->getRequest()->getParams();
        $defaultErrorUrl = $this->fusionPayHelper->getConstValue('TBI_PLATFORM_URL') . '/application/error/error-notified';

        try {
            if (!isset($params['product'], $params['qty'])) {
                throw new \Exception('Required parameters are missing.');
            }

            $productId = (int) $params['product'];
            $qty = (int) $params['qty'];
            $selectedConfigurableOption = $params['selected_configurable_option'] ?? null;
            $superAttributes = $params['super_attribute'] ?? [];
            $customOptions = $params['options'] ?? [];

            $product = $this->productRepository->getById($productId);
            
            if ($product->getTypeId() === ConfigurableProductType::TYPE_CODE) {
                $productDetails = $this->getConfigurableProductDetails($product, $selectedConfigurableOption, $superAttributes);
            } else {
                $productDetails = $this->getProductDetailsWithCustomOptions($product, $customOptions);
            }
           
            $minVisible = $this->fusionPayHelper->getConfigValue('tbi_settings/label_settings/minimum_visible');
            $minBNPL = $this->fusionPayHelper->getConfigValue('tbi_settings/label_settings/bnpl_min');
            if($productDetails['price'] * $qty < $minBNPL && $productDetails['price'] * $qty < $minVisible){
                return $result->setData(['success' => false, 'message' => 'Product price is too low.']);
            }
        } catch (NoSuchEntityException $e) {
            return $result->setData(['success' => false, 'message' => 'Product not found.']);
        } catch (\Exception $e) {
            return $result->setData(['success' => false, 'message' => $e->getMessage()]);
        }

        $image = $productDetails['image'];
        $calculatorImage = $productDetails['calculator_image'] ?? $image;
        $price = $productDetails['price'];
        $name  = $productDetails['name'];

        $items = array();
        $items_count = 0;

        $category = "0";

        $items[$items_count] = array(

            "name"                  =>  $name,
            "description"           =>  "",
            "qty"                   =>  $qty,
            "price"                 =>  $price,
            "category"              =>  $category,
            "imagelink"             =>  $image,
            "params"                =>  $params

        );

        $cart_total = intval($qty) * floatval($price);

        $applicationData = array(
            "orderid"               =>  "0",
            "items"                 =>  $items,
            "successRedirectURL"    =>  $this->getHomePageUrl(),
            "failRedirectURL"       =>  $this->getHomePageUrl()
        );

        // /$this->encryptionKey

        $body_to_send = [
            'code'         =>  $this->resellerCode,
            'key'          =>  $this->resellerKey,
            "amount"       =>  $cart_total,
            "origin"       =>  "plugin",
            "page"         =>  "product"
        ];

        $encryptedImage = $this->encryptProductImage($calculatorImage);
        if ($encryptedImage !== '') {
            $body_to_send['product_image'] = $encryptedImage;
        }

        try {
            $response = $this->makeCalculatorCall($body_to_send);
            return $result->setData($response);
        } catch (\Exception $e) {
            return $result->setData(['url' => $defaultErrorUrl, 'debug' => $e->getMessage()]);
        }
    }


    private function getConfigurableProductDetails($product, $selectedConfigurableOption, $superAttributes)
    {
        if ($selectedConfigurableOption) {
            $childProduct = $this->productRepository->getById($selectedConfigurableOption);
            $imageUrl = $this->imageHelper->init($childProduct, 'product_page_image_large')->getUrl();
            return [
                'name' => $childProduct->getName(),
                'price' => $childProduct->getFinalPrice(),
                'image' => $imageUrl,
                'calculator_image' => $this->getTinyProductImageDataUri($childProduct)
            ];
        }

        foreach ($this->configurableProductType->getUsedProducts($product) as $child) {
            if ($this->doAttributesMatch($child, $superAttributes)) {
                $imageUrl = $this->imageHelper->init($child, 'product_page_image_large')->getUrl();
                return [
                    'name' => $child->getName(),
                    'price' => $child->getFinalPrice(),
                    'image' => $imageUrl,
                    'calculator_image' => $this->getTinyProductImageDataUri($child)
                ];
            }
        }

        $imageUrl = $this->imageHelper->init($product, 'product_page_image_large')->getUrl();

        return [
            'name' => $product->getName(),
            'price' => $product->getFinalPrice(),
            'image' => $imageUrl,
            'calculator_image' => $this->getTinyProductImageDataUri($product)
        ];
    }

    private function doAttributesMatch($product, $attributes)
    {
        foreach ($attributes as $attributeId => $valueId) {
            $attributeCode = $product->getResource()->getAttribute($attributeId)->getAttributeCode();
            if ($product->getData($attributeCode) != $valueId) {
                return false;
            }
        }
        return true;
    }


    private function getProductDetailsWithCustomOptions($product, $customOptions)
    {
        $basePrice = $product->getFinalPrice();  // Use the base price of the product
        $finalPrice = $basePrice;
        $extendedName = $product->getName(); // Start with the base product name

        foreach ($product->getOptions() as $option) {
            $optionId = $option->getOptionId();
            if (isset($customOptions[$optionId])) {
                $values = $customOptions[$optionId];
                $values = is_array($values) ? $values : [$values];  // Ensure the values are always treated as an array

                foreach ($option->getValues() as $value) {
                    if (in_array($value->getId(), $values)) {
                        if ($value->getPriceType() == 'fixed') {
                            $finalPrice += $value->getPrice();
                        } else if ($value->getPriceType() == 'percent') {
                            $finalPrice += ($basePrice * ($value->getPrice() / 100));
                        }
                        // Append option title and selected value to the product name
                        $extendedName .= ' - ' . $option->getTitle() . ': ' . $value->getTitle();
                    }
                }
            }
        }

        $imageUrl = $this->imageHelper->init($product, 'product_page_image_large')->getUrl();

        return [
            'name' => $extendedName,
            'price' => $finalPrice,
            'image' => $imageUrl,
            'calculator_image' => $this->getTinyProductImageDataUri($product)
        ];
    }


    /**
     * Build a tiny inline product image for the calculator button.
     *
     * The calculator runs on another domain. Passing Magento media URLs can be
     * blocked by browser/CORS/private-network rules, so for the calculator only
     * we send a small data URI instead of the remote media URL.
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return string
     */
    private function getTinyProductImageDataUri($product)
    {
        if (!$product) {
            return '';
        }

        $imageTypes = [
            'product_thumbnail_image',
            'product_page_image_small',
            'product_page_image_large'
        ];

        foreach ($imageTypes as $imageType) {
            try {
                $imageUrl = $this->imageHelper
                    ->init($product, $imageType)
                    ->resize(48, 48)
                    ->getUrl();

                $dataUri = $this->imageUrlToDataUri((string) $imageUrl);
                if ($dataUri !== '') {
                    return $dataUri;
                }
            } catch (\Exception $e) {
                continue;
            }
        }

        return '';
    }

    /**
     * Convert a Magento media URL to an inline data URI by reading the cached
     * image from the local media filesystem.
     *
     * @param string $imageUrl
     * @return string
     */
    private function imageUrlToDataUri($imageUrl)
    {
        $imageUrl = trim((string) $imageUrl);
        if ($imageUrl === '') {
            return '';
        }

        $path = (string) parse_url($imageUrl, PHP_URL_PATH);
        if ($path === '') {
            return '';
        }

        $mediaMarker = '/media/';
        $pos = strpos($path, $mediaMarker);
        if ($pos === false) {
            return '';
        }

        $relativePath = rawurldecode(substr($path, $pos + strlen($mediaMarker)));
        $relativePath = ltrim($relativePath, '/');
        if ($relativePath === '' || strpos($relativePath, '..') !== false) {
            return '';
        }

        try {
            $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
            if (!$mediaDirectory->isExist($relativePath)) {
                return '';
            }
            $absolutePath = $mediaDirectory->getAbsolutePath($relativePath);
        } catch (\Exception $e) {
            if (!defined('BP')) {
                return '';
            }
            $absolutePath = rtrim(BP, '/') . '/pub/media/' . $relativePath;
        }

        if (!is_readable($absolutePath)) {
            return '';
        }

        $bytes = @file_get_contents($absolutePath);
        if ($bytes === false || $bytes === '') {
            return '';
        }

        $mime = '';
        $info = @getimagesize($absolutePath);
        if (is_array($info) && !empty($info['mime'])) {
            $mime = (string) $info['mime'];
        }

        if ($mime === '') {
            $extension = strtolower((string) pathinfo($absolutePath, PATHINFO_EXTENSION));
            $mimeMap = [
                'jpg' => 'image/jpeg',
                'jpeg' => 'image/jpeg',
                'png' => 'image/png',
                'gif' => 'image/gif',
                'webp' => 'image/webp'
            ];
            $mime = $mimeMap[$extension] ?? '';
        }

        if ($mime === '' || strpos($mime, 'image/') !== 0) {
            return '';
        }

        return 'data:' . $mime . ';base64,' . base64_encode($bytes);
    }

    /**
     * Makes the actual API call to the TBI endpoint.
     *
     * @param array $data
     * @return array
     */
    protected function makeCalculatorCall($data)
    {
        $queryString = http_build_query($data);
        $url = $this->tbiCalculatorEndpoint . '?' . $queryString;

        // Data URI images are intentionally tiny, but after encryption and URL encoding
        // they can still make the calculator URL too long for some servers/proxies.
        // If that happens, skip only the optional product image and keep the modal working.
        if (strlen($url) > 7000 && isset($data['product_image'])) {
            unset($data['product_image']);
            $queryString = http_build_query($data);
            $url = $this->tbiCalculatorEndpoint . '?' . $queryString;
        }

        return ['url' => $url];
    }

    /**
     * @param string $imageUrl
     * @return string
     */
    private function encryptProductImage($imageUrl)
    {
        $imageUrl = trim((string) $imageUrl);
        if ($imageUrl === '' || trim((string) $this->encryptionKey) === '') {
            return '';
        }

        try {
            return Cryptor::Encrypt($imageUrl, $this->encryptionKey);
        } catch (\Exception $e) {
            return '';
        }
    }


    /**
     * Get the URL for the homepage.
     *
     * @return string
     */
    protected function getHomePageUrl()
    {
        return $this->urlBuilder->getUrl('', ['_secure' => true]);
    }
    
    
    
}
