<?php
/**
 * Magento 2 Google Analytics 4 (GA4) GTM
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Anowave license that is
 * available through the world-wide-web at this URL:
 * https://www.anowave.com/license-agreement/
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade this extension to newer
 * version in the future.
 *
 * @category 	Anowave
 * @package 	Anowave_Ec
 * @copyright 	Copyright (c) 2026 Anowave (https://www.anowave.com/)
 * @license  	https://www.anowave.com/license-agreement/
 */

namespace Anowave\Ec\Model\Facebook;

use Anowave\Ec\vendor\FacebookAds\Api;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\ActionSource;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\Event;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\EventRequest;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\UserData;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\CustomData;
use Anowave\Ec\vendor\FacebookAds\Object\ServerSide\Content;
use Anowave\Ec\vendor\FacebookAds\Logger\CurlLogger;
use Anowave\Ec\Model\Facebook\Conversions\Endpoint;
use Anowave\Ec\Model\Cookie\Onetrust;
use Anowave\Ec\Model\Cookie\Cookiebot;
use Anowave\Ec\Model\Cookie\BuiltIn;
use Anowave\Ec\Model\System\Config\Source\Consent\Engine;

class ConversionsApi extends Endpoint
{
    /**
     * Default action source
     * 
     * @var string
     */
    const ACTION_SOURCE = 'website';
    
    /**
     * @var \Anowave\Ec\vendor\FacebookAds\Api
     */
    protected $api;
    
    /**
     * Set cookie 
     * 
     * @var \Anowave\Ec\Model\Cookie\Facebook
     */
    protected $cookie;
    
    /**
     * Check if Facebook Conversions API is enabled
     * 
     * @var string
     */
    private $enable = false;
    /**
     * Pixel ID
     *
     * @var string
     */
    private $pixel_id = null;
    
    /**
     * Test event code 
     * 
     * @var string
     */
    private $test_event_code = null;
    
    /**
     * User data 
     * 
     * @var array
     */
    private $user_data = [];

    /**
     * List if allowed events
     */
    private $allowed_events = [];
    
    /**
     * Is cookie directive support enabled
     * @var string
     */
    private $cookie_directive = false;

    /**
     * Current GDPR solution
     */
    private $cookie_directive_engine = 0;
    
    /**
     * Is cookie consent granted
     * 
     * @var string
     */
    private $cookie_directive_constent_granted = false;
    
    /**
     * @var \Anowave\Ec\Model\Logger
     */
    private $logger;

    /**
     * @var Onetrust
     */
    private ?Onetrust $onetrust = null;

    private ?Cookiebot $cookiebot = null;

    private ?BuiltIn $cookieBuiltIn =  null;
    
    /**
     * Constructor 
     * 
     * @param unknown $pixel_id
     * @param unknown $access_token
     * @param \Anowave\Ec\Model\Cookie\Facebook $cookie
     */
    public function __construct
    (
        $pixel_id, 
        $access_token, 
        $test_event_code, 
        \Anowave\Ec\Model\Cookie\Facebook $cookie, 
        array $user_data = [], 
        $cookie_directive = false, 
        $cookie_directive_engine = 0, 
        $cookie_directive_constent_granted = false, 
        ?\Anowave\Ec\Model\Logger $logger = null,
        ?Onetrust $onetrust = null,
        ?Cookiebot $cookiebot = null,
        ?BuiltIn $cookieBuiltIn = null
    )
    {
        /**
         * Set pixel id 
         * 
         * @var string $pixel_id
         */
        $this->pixel_id = $pixel_id;
        
        /**
         * Instantiate API 
         * 
         * @var \Anowave\Ec\vendor\FacebookAds\Api
         */
        
        if ($this->pixel_id && $access_token)
        {
            /**
             * Instantiate API 
             * 
             * @var \Anowave\Ec\Model\Facebook\ConversionsApi $api
             */
            $this->api = Api::init(null, null, $access_token);
            
            /**
             * Set logger
             */
            $this->api->setLogger(new CurlLogger());
        }
        else 
        {
            $this->enable = false;
        }
        
        /**
         * Set test event code
         * 
         * @var \Anowave\Ec\Model\Facebook\ConversionsApi $test_event_code
         */
        if ($test_event_code)
        {
            $this->test_event_code = $test_event_code;   
        }
        
        /**
         * Set cookie 
         * 
         * @var \Anowave\Ec\Model\Cookie\Facebook $cookie
         */
        $this->cookie = $cookie;
        
        /**
         * Set user data 
         * 
         * @var [] $user_data
         */
        $this->user_data = $user_data;
        
        /**
         * Set cookie directive enabled flag 
         * 
         * @var \Anowave\Ec\Model\Facebook\ConversionsApi $cookie_directive
         */
        $this->cookie_directive = $cookie_directive;

        /**
         * Set current cookie directive engine
         */
        $this->cookie_directive_engine = (int) $cookie_directive_engine;
        
        /**
         * Set granted flag 
         * 
         * @var \Anowave\Ec\Model\Facebook\ConversionsApi $cookie_directive_constent_granted
         */
        $this->cookie_directive_constent_granted = $cookie_directive_constent_granted;
        
        /**
         * Set logger
         * 
         * @var \Anowave\Ec\Model\Facebook\ConversionsApi $logger
         */
        $this->logger = $logger;

        $this->onetrust = $onetrust;

        $this->cookiebot = $cookiebot;

        $this->cookieBuiltIn = $cookieBuiltIn;
    }
    
    public function trackPageView()
    {
        return $this;
    }
    
    /**
     * Track view content 
     * 
     * @param array $content_ids
     * @param string $currency
     * @param float $value
     * @param string $category
     * @param string $content_name
     */
    public function trackViewContent(array $content_ids = [], string $currency = '', float $value = 0, string $category = '', string $content_name = '')
    {
        /**
         *  A bug in the API require a string, not array
         */
        $content_id = array_pop($content_ids);

        $custom_data = (new CustomData())
                            ->setContentType('product')
                            ->setContentIds($content_id)
                            ->setNumItems(1)
                            ->setCurrency($currency)
                            ->setValue($value)
                            ->setContentCategory($category)
                            ->setContentName($content_name);
 
        $this->track('ViewContent', $custom_data, true);
    }
    
    /**
     * Track initiate checkout 
     * 
     * @param array $content_ids
     * @param string $currency
     * @param float $value
     */
    public function trackInitiateCheckout(array $content_ids = [], string $currency = '', float $value = 0)
    {
        $custom_data = (new CustomData())
                            ->setContentType('product')
                            ->setContentIds($content_ids)
                            ->setNumItems(count($content_ids))
                            ->setCurrency($currency)
                            ->setValue($value)
                            ->setContentName('checkout');
                
        return $this->track('InitiateCheckout', $custom_data, true);
    }

    /**
     * Track ViewCategory
     * 
     * @param array $content_ids
     * @param string $category_name
     * @param mixed $currency
     * @param float $value
     * 
     * @return [type]
     */
    public function trackViewCategory(array $content_ids = [], string $currency = '', string $category_name = '', float $value = 0)
    {
        $custom_data = (new CustomData())
                            ->setContentType('product')
                            ->setContentIds($content_ids)
                            ->setNumItems(count($content_ids))
                            ->setCurrency($currency)
                            ->setValue($value)
                            ->setContentCategory($category_name)
                            ->setContentName($category_name);

        return $this->track('ViewCategory', $custom_data, true);
    }
    
    /**
     * Track purchase
     * 
     * @param array $content_ids
     * @param string $currency
     * @param float $value
     */
    public function trackPurchase(\Magento\Sales\Model\Order $order, array $content_ids = [], string $currency = 'EUR', float $value = 0)
    {
        $this->user_data = [];
        
        /**
         * Handle non-logged purchase
         */
        if (!$this->user_data)
        {
            try 
            {
                $address = $order->getShippingAddress();
                
                if ($address)
                {
                    if ($address->getEmail())
                    {
                        $this->user_data['email'] = strtolower(trim($address->getEmail()));
                    }
                    
                    if ($address->getFirstname())
                    {
                        $this->user_data['first_name'] = strtolower(trim($address->getFirstname()));
                    }
                    
                    if ($address->getLastname())
                    {
                        $this->user_data['last_name'] = strtolower(trim($address->getLastname()));
                    }
                    
                    if ($address->getTelephone())
                    {
                        $this->user_data['phone'] =  preg_replace('/[^0-9]+/i', '', $address->getTelephone());
                    }
                    
                    if ($address->getCity())
                    {
                        $this->user_data['city'] = strtolower(preg_replace('/[^a-zA-Z]/i','',$address->getCity()));
                    }
                    
                    if ($address->getPostcode())
                    {
                        $this->user_data['zip_code'] = $address->getPostcode();
                    }
                    
                    if ($address->getCountryId())
                    {
                        $this->user_data['country_code'] = strtolower($address->getCountryId());
                    }
                    
                    if ($address->getRegionCode())
                    {
                        $region = preg_replace('/[^a-z]/i','', $address->getRegionCode());
                        
                        if ($region)
                        {
                            $this->user_data['state'] = $region;
                        }
                    }
        
                    $this->user_data = array_map(function($data)
                    {
                        return hash('sha256', $data);
                        
                    }, $this->user_data);
                }
            }
            catch (\Exception $e)
            {
                
            }
        }

        $contents = [];

        foreach($order->getAllVisibleItems() as $item)
        {

            $contents[] = (new Content())
                            ->setProductId($item->getSku())
                            ->setItemPrice((float) $item->getPrice())
                            ->setQuantity($item->getQtyOrdered())
                            ->setTitle($item->getName());
        }
        
        $custom_data = (new CustomData())
                            ->setContentType('product')
                            ->setContents($contents)
                            ->setContentIds($content_ids)
                            ->setNumItems(count($content_ids))
                            ->setCurrency($currency)
                            ->setValue($value);   
        
        return $this->track('Purchase', $custom_data, true);
    }
    
    /**
     * Track add to cart event 
     * 
     * @param string $content_name
     * @param array $content_ids
     * @param string $currency
     * @param float $value
     */
    public function trackAddToCart($content_name, array $content_ids = [], $currency = 'EUR', float $value = 0)
    {
        $custom_data = (new CustomData())
                            ->setContentName($content_name)
                            ->setContentType('product')
                            ->setContentIds($content_ids)
                            ->setCurrency($currency)
                            ->setValue($value);
        
        return $this->track('AddToCart', $custom_data);
    }
    
    /**
     * Track event 
     * 
     * @param string $eventName
     * @param CustomData $custom_data
     * @param string $cookie
     * @return array
     */
    public function track($eventName, $custom_data = null, $force = false, $cleanup = false) : array 
    { 
        if (!in_array($eventName, $this->allowed_events))
        {
            return $this->getFailure(sprintf('%s event is not allowed', $eventName));
        }

        /**
         * Check if tracking is active
         */
        if (!$this->enable)
        {
            return $this->getFailure('Not enabled');
        }

        if (!$this->api)
        {
            return $this->getFailure('API is not enabled');
        }
        
        try 
        {
            if (Engine::ENGINE_BUILT_IN === $this->cookie_directive_engine)
            {
                if (!$this->cookieBuiltIn->hasConsent())
                {
                    return $this->getFailure('Consent not granted');
                }
            }

            if (Engine::ENGINE_ONETRUST === $this->cookie_directive_engine)
            {
                if (!$this->onetrust->hasConsent())
                {
                    $this->user_data = [];
                }
            }

            if (Engine::ENGINE_COOKIEBOT === $this->cookie_directive_engine)
            {
                if (!$this->cookiebot->hasConsent())
                {
                    $this->user_data = [];

                    return $this->getFailure('Consent not granted');
                }
            }

            $client_ip = $this->getClientIp();

            $user_data = (new UserData($this->user_data))->setClientIpAddress($client_ip)->setClientUserAgent($_SERVER['HTTP_USER_AGENT']);

            if (isset($_COOKIE['_fbp']))
            {
                $_fbp = $_COOKIE['_fbp'];

                if (!$this->is90daysOlder($_fbp))
                {
                    $user_data->setFbp($_fbp);
                }
                else 
                {
                    unset($_COOKIE['_fbp']);    
                }
            }
            
            if (isset($_COOKIE['_fbc']))
            {
                $_fbc = $_COOKIE['_fbc'];

                if (!$this->is90daysOlder($_fbp))
                {
                    $user_data->setFbc($_fbc);
                }
                else 
                {
                    unset($_COOKIE['_fbc']);
                }
            }
            
            $event = (new Event())
                            ->setEventName($eventName)
                            ->setEventTime(time())
                            ->setUserData($user_data)
                            ->setActionSource(ActionSource::WEBSITE);


            if(php_sapi_name() !== "cli")
            {
                if (isset($_SERVER['HTTP_REFERER']))
                {
                    $event->setEventSourceUrl($_SERVER['HTTP_REFERER']);
                }
            }
            
            if ($custom_data)
            {
                 $event->setCustomData($custom_data);
            }

            if (!isset($_COOKIE['facebook_latest_uuid']) || $force)
            {
                $uuid = (object)
                [
                    'uuid' => $this->uuid(), 
                    'event' => $eventName
                ];
                
                if ($this->cookie_directive)
                {
                    if ($this->cookie_directive_constent_granted)
                    {
                        /**
                         * Set cookie
                         */
                        $this->cookie->set(json_encode($uuid));
                    }
                }
                else 
                {
                    /**
                     * Set cookie
                     */
                    $this->cookie->set(json_encode($uuid));
                }
            }
            else 
            {
                $uuid = json_decode($_COOKIE['facebook_latest_uuid']); 
            }
        
            if ($uuid && $uuid->uuid && $uuid->event === $eventName)
            {
                /**
                 * Set event id
                 */
                $event->setEventId($uuid->uuid);
            }

            $request = (new EventRequest($this->pixel_id))->setEvents([$event]);

            if ($this->test_event_code)
            {
                /**
                 * Set test mode
                 */
                $request->setTestEventCode($this->test_event_code);
            }
            
            /**
             * Execute response
             * 
             * @var \Anowave\Ec\vendor\FacebookAds\Object\ServerSide\EventResponse $response
             */
            $response = $request->execute();

            if ($cleanup)
            {
                $this->cookie->delete();
            }
            
            return $this->getSuccess();
        }
        catch (\Exception $e)
        {
            $this->logger->log("CAPI ERROR (event: {$eventName}): {$e->getMessage()}");

            return $this->getFailure('CAPI exception thrown. Check logs for more information.');
        }
        
        return $this->getFailure('No action performed');
    }


    /**
     * Get client IP 
     * 
     * @return string
     */
    private function getClientIp(): string
    {
        if (!empty($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP)) 
        {
            return $_SERVER['HTTP_CF_CONNECTING_IP'];
        }

        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) 
        {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

            foreach ($ips as $ip) 
            {
                $ip = trim($ip);

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) 
                {
                    return $ip;
                }
            }
        }

        $ip = $_SERVER['REMOTE_ADDR'] ?? '';

        return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '';
    }

    /**
     * Check if Facebook cookies are older than 90 days
     * 
     * @param string $value
     * 
     * @return bool
     */
    private function is90daysOlder(string $value = '') : bool  
    {
        $parts = explode(chr(46), $value);

        $timestamp = (int) ($parts[2] ?? null);

        if ($timestamp)
        {
            $timestamp /= 1000;

            $time = microtime(true) - $timestamp;

            return $time > (60 * 60 * 24 * 90);
        }
        
        return false;
    }
    
    /**
     * Enable 
     * 
     * @return \Anowave\Ec\Model\Facebook\ConversionsApi
     */
    public function enable(array $events = [])
    {
        $this->enable = true;

        $this->allowed_events = $events;
        
        return $this;
    }
    
    /**
     * Disable
     * 
     * @return \Anowave\Ec\Model\Facebook\ConversionsApi
     */
    public function disable()
    {
        $this->enable = false;
        
        return $this;
    }
    
    /**
     * Check if Facebook Conversions API is enabled 
     * 
     * @return bool
     */
    public function isEnabled() : bool
    {
        return $this->enable;
    }

    public function hasConsent() : bool 
    {
        switch($this->cookie_directive_engine)
        {
            case Engine::ENGINE_BUILT_IN: return $this->cookieBuiltIn->hasConsent();
            case Engine::ENGINE_ONETRUST: return $this->onetrust->hasConsent();
            case Engine::ENGINE_COOKIEBOT: return $this->cookiebot->hasConsent();
        }

        return true;
    }

    private function getSuccess() : array 
    {
        return [
            'success' => true,
            'consent' => $this->hasConsent
        ];
    }

    public function getFailure(string $reason = 'Not specified') : array 
    {
        return [
            'success' => false,
            'consent' => $this->hasConsent(),
            'reason'  => $reason
        ];
    }

}