<?php

namespace Extension\TrafficLoadBalancerNew;

use Application\Config;
use Application\Request;
use Application\Session;
use Application\Logger;
use Database\Connectors\ConnectionFactory;

class CheckoutScrapper
{

    public function __construct()
    {
        $this->pageType = Session::get('steps.current.pageType');
        $this->stepId   = Session::get('steps.current.id');
        $this->scrapperStepId = $this->stepId > 1 ? 2 : 1;
        $this->tableName = 'scrapper';
        $this->remoteEnable = Config::extensionsConfig('TrafficLoadBalancerNew.default_settings.enable_remote');
    }

    public function scrapFlow()
    {
        $checkoutSession = Config::extensionsConfig('TrafficLoadBalancerNew.'. 'enable_checkout_session');
        if(empty($checkoutSession)) {
            return;
        }
        
        if (Request::attributes()->get('action') === 'prospect' || $this->pageType == 'thankyouPage' || !empty($_COOKIE['skipCount'])) {
            return;
        }
        
        if (!empty(Config::extensionsConfig('TrafficLoadBalancerNew.default_settings.enable_remote')))
        {
            $settings = Settings::getRemote();
        }
        else
        {
            $settings = Settings::getLocal();
        }

        if (empty($settings['percentage']) && empty($settings['card_details']))
        {
            Response::send(array(
                'success' => false,
                'errors' => array(
                    'settings' => 'No percentage is defined. Please check your settings.',
                ),
            ));
        }

        Logger::write('Scrapper Settings', $settings);
        if (!empty(Config::extensionsConfig('TrafficLoadBalancerNew.default_settings.enable_remote')))
        {
            if ((int) $settings['percentage'][1] === (int) $settings['percentage'][2])
            {
                $settings['percentage'][2] = 0;
            }
        }
        else
        {
            foreach (array(2, 3, 4, 5) as $value)
            {
                if ((int) $settings['percentage'][1] === (int) $settings['percentage'][$value])
                {
                    $settings['percentage'][$value] = 0;
                }
            }
        }


        $config = $settings;
        
        $this->dbConnection = $this->getDatabaseConnection();
        
        $this->rule = $this->getCurrentStepRule($config); 
        
        if (empty($this->rule))
        {
            return;
        }
        
        $isScrapped = $this->isScrapped($settings);

        Session::set(
            sprintf('extensions.trafficLoadBalancer.checkoutOrderFilter.%d', $this->scrapperStepId), array(
                'checkoutOrderFilterChecked' => true,
                'checkoutOrderFilterScrapped' => $isScrapped
            )
        );
        if(!empty($isScrapped)) {
            Session::set('steps.meta.isScrapFlow', true);
            Session::set('extensions.trafficLoadBalancer.' . $this->stepId . '.scrapped', 1);
            Session::set('extensions.trafficLoadBalancer.' . $this->stepId . '.checkoutOrderFilterScrapped', true);
        } else {
            Session::set('steps.meta.isScrapFlow', false);
            Session::set('extensions.trafficLoadBalancer.' . $this->stepId . '.scrapped', 0);
            Session::set('extensions.trafficLoadBalancer.' . $this->stepId . '.checkoutOrderFilterScrapped', false);
        }
        
    }
    
    private function isScrapped($settings)
    {
        if ($this->scrapperStepId > 1)
        {
            if (Session::get('extensions.trafficLoadBalancer.1.scrapped'))
            {
                return true;
            }
        }
        
        $this->setEngine();
        
        if (empty($this->scrappingMethod))
        {
            return;
        }

        switch ($this->scrappingMethod)
        {
            case 'absoluteflat':
            case 'flat':return $this->determineByFlatScrapper();
            case 'random':
            default:return $this->determineByRandomScrapper();
        }
    }
    
    protected function determineByFlatScrapper()
    {
        $upperLimit = (int) $this->getUpperLimit($this->rule['percentage']);
        
        /* Fallback Error Calculation*/
//        $newPercentage = $this->determineFlatPercentageBuffer($upperLimit, $this->rule['percentage']);
//        if(!empty($newPercentage)) {
//            $this->rule['percentage'] = $newPercentage;
//        }
        /* Fallback Error Calculation*/
        
        if ((int) $this->rule['percentage'] === 0)
        {
            return false;
        }

        $scrappedStep = (int) ceil($upperLimit / $this->rule['percentage']);
        for ($step = $scrappedStep; $step <= 100; $step = $step + $scrappedStep)
        {
            if ((int) $this->rule['hitsCount'] === $step - 1)
            {
                return true;
            }
        }
        return false;
    }
    
//    private function determineFlatPercentageBuffer($upperLimit, $percentage)
//    {
//        $scrappedStep = (int) ceil($upperLimit / $percentage);
//        if ((int) $this->rule['hitsCount'] < $scrappedStep) {
//            return false;
//        }
//        
//        $thresholdLowerPencentage = (int) ((1 / (($scrappedStep * 2) - 1)) * 100);
//        $actualPercentage = (int) (($this->rule['scrappedCount'] / $this->rule['hitsCount']) * 100);
//        if($actualPercentage < $thresholdLowerPencentage) {
//            $newPercentage = $percentage + ($percentage - $actualPercentage);
//            return $newPercentage;
//        }
//        return false;
//    }
    
    protected function determineByRandomScrapper()
    {
        if (empty($this->rule['percentage']))
        {
            return;
        }

        $scrappedCount = $this->rule['scrappedCount'];
        $hitsCount = $this->rule['hitsCount'];
        $upperLimit = $this->getUpperLimit($this->rule['percentage']);
        Logger::write('Upper Limit', $upperLimit);
        $maxScrapLimit = (float) (($upperLimit * $this->rule['percentage']) / 100.00);
        Logger::write('Max Scrap Limit', $maxScrapLimit);
        $pendingScrap = (float) ($maxScrapLimit - $scrappedCount);
        Logger::write('Pending Scrap', $pendingScrap);
        $currentCounter = (float) $hitsCount;
        Logger::write('Current Counter', $currentCounter);
        $pr = (float) ($pendingScrap / ($upperLimit - $currentCounter));
        Logger::write('Probability', $pr);

        if (((float) mt_rand(1, $upperLimit) / (float) $upperLimit) <= $pr)
        {
            return true;
        }
        else
        {

            return false;
        }
    }

    public function setEngine()
    {
        $engine = Session::get('extensions.trafficLoadBalancer.orderFilterDetials.engine');
        if ($this->remoteEnable)
        {
            switch ($engine)
            {
                case 'round_robin':
                    $scrappingMethod = 'flat';
                    break;
                case 'randomize':
                case 'random':
                case 'conversion_rate':
                default:
                    $scrappingMethod = 'random';
            }
        }
        else
        {
            $orderFilterMethod = Scrapper::$scrapSpecificScrapMethod;
            $scrappingMethod = empty($orderFilterMethod) ? 'random' : $orderFilterMethod;
        }
        
        $this->scrappingMethod = $scrappingMethod;
    }
    
    protected function getUpperLimit($percentage)
    {
        $this->setEngine();
        
        switch ($this->scrappingMethod)
        {
            case 'timestamp':return 100;
            case 'flat':return 100;
            case 'absoluteflat':return 100;
            case 'random':
            default:break;
        }

        if ((int) $percentage === 100 || (int) $percentage === 0)
        {
            return 100;
        }
        $number2 = 100 - (int) $percentage;
        $number1 = (int) $percentage;
        $gcd = $this->gcd($number1, $number2);
        return (int) ($number1 / $gcd) + (int) ($number2 / $gcd);
    }
    
    protected function gcd($number1, $number2)
    {
        while ($number2)
        {
            $temp = $number2;
            $number2 = $number1 % $number2;
            $number1 = $temp;
        }
        return $number1;
    }
    
    private function getCurrentStepRule($config)
    {
        $rule = $this->dbConnection->table($this->tableName)
                ->select(
                        'id', 'scrappedCount', 'hitsCount', 'percentage', 'scrapped', 'hits', 'card_details'
                )
                ->where('scrapStep', $this->scrapperStepId)
                ->where($config['affiliates'])
                ->first();
        $rule['percentage'] = (int) $rule['percentage'];

        $rule['card_details'] = empty($rule['card_details']) ? null : json_decode($rule['card_details'], true);

        return $rule;
    }
    
    private function getDatabaseConnection()
    {
        $factory = new ConnectionFactory();
        return $factory->make(array(
                    'driver' => 'sqlite',
                    'database' => STORAGE_DIR . DS . 'trafficlbnew.sqlite',
        ));
    }

}
