<?php
/**
* @package XenCentral Ad Server
* @author Skydevelop EU
* @copyright Drnoyan & Nalyan LDA, Portugal, EU
* @license http://www.dnf.pt/eula.html
* @link http://www.skydevelop.com
* @revision 97
* @version 2.0.0 beta 3 rev. 3
*/


class XenCentral_AdServer_ControllerPublic_Index
        extends XenForo_ControllerPublic_Abstract
        implements XenCentral_Framework_ControllerPublic_PaymentApiInterface
{
    public function actionIndex()
    {
        // route to manage banners controller
        return $this->responseReroute('XenCentral_AdServer_ControllerPublic_Banner', 'index');
    }

    public function actionStats()
    {
        return $this->responseReroute('XenCentral_AdServer_ControllerPublic_Stats', 'index');
    }

    public function actionPurchasePaymentInstructions()
    {
        $this->getRequest()->setParam('purchase_handler', __CLASS__);
        return $this->responseReroute('XenCentral_Framework_ControllerPublic_PaymentApi', 'payment-instructions');
    }

    public function actionRedirect()
    {
        $bannerId = $this->getInput()->filterSingle('bannerId', XenForo_Input::UINT);
        $skipZone = $this->getInput()->filterSingle('skipZone', XenForo_Input::BOOLEAN);
        $url = $this->getInput()->filterSingle('url', XenForo_Input::STRING);

        $referer=$this->getInput()->filterSingle('_ref', XenForo_Input::STRING);
        if(!$referer) {
            $referer = $this->_request->getServer('HTTP_REFERER');
        }

        $banners = $this->_getBannerModel()->getActiveBanners();

        $this->_getStatsModel()->setLastBannerClickLogId(0);
        $this->_getStatsModel()->setLastCampaignClickLogId(0);

        if (isset($banners[$bannerId])) {
            // store log only if correct and active banner is specified
            $banner = $banners[$bannerId];
            unset($banners);

            $zone = $this->_getZoneModel()->getZoneByIdFromCache($banner['zoneId']);
            if ($zone->isCustomZone()) {
                // user campaign
                $skipZone = 0; // force logging zone click as well

                if ($zone->isValidHttpReferer($referer) == false) {
                    return $this->responseError(new XenForo_Phrase(
                            'xcas_banner_wrong_website'
                    ));
                }

                $activeOwner = $zone->getActiveOwnerUserInfo($zone->getCostPerClick());

                if (!$activeOwner) {
                    // shouldn't happen, make sure to update banner cache and response with error message
                    XenCentral_AdServer_Cron::updateBannerStatus($banner['bannerId']);

                    return $this->responseError(new XenForo_Phrase(
                            'xcas_banner_not_available'
                    ));
                }
            }

            $bannerClickId = $this->_getStatsModel()->logBannerClick($banner['bannerId']);
            if (!$skipZone) {
                $zoneClickId = $this->_getStatsModel()->logZoneClick($banner['zoneId']);
            } else {
                $zoneClickId = 0;
            }

            if ($bannerClickId) {
                // log campaign click only if clicked from another domain
                if (
                        $zone->isCustomZone()
                        AND $zone->isServerDomain($referer) == false
                ) {
                    // charge the user and update banner cache
                    $this->_getStatsModel()->logCampaignClick(
                            $bannerId,
                            $activeOwner['user_id'],
                            $bannerClickId,
                            $zoneClickId,
                            $zone->getCostPerClick()
                    );
                    $activeOwner['ad_credit'] -= $zone->getCostPerClick();
                    $this->_getUserModel()->updateAdServerUserInfo($activeOwner);

                    if ($activeOwner['ad_credit'] < $zone->getCostPerClick() * 2) {
                        // running out of credit, make sure to update banner status
                        XenCentral_AdServer_Cron::updateBannerStatus($bannerId);
                    }
                }
            }

            // correct the URL, just in case...
            $url = $banner['url'];
        }

        return $this->responseView('XenCentral_AdServer_ViewPublic_Redirect', '', array(
                'url' => $url
        ));
    }

    public function actionPurchaseCancel()
    {
        // redirect to ad list again
        return $this->responseRedirect(
                XenForo_ControllerResponse_Redirect::SUCCESS,
                XenForo_Link::buildPublicLink('ads')
        );
    }

    public function actionPurchaseReturn()
    {
        $paymentMethod = $this->_assertValidPaymentMethod();

        if ($paymentMethod->validateRequest()) {
            $transactionId = $paymentMethod->getTransactionId();
            $processed = $paymentMethod->transactionProcessed();
            if (!$processed) {
                // try to process transaction
                $processed = false;
                try {
                    $processed = $this->_processTransaction($paymentMethod);
                } catch (Exception $ex) {

                }
                if ($processed) {
                    $paymentMethod->saveTransaction();
                    $processed = $paymentMethod->transactionProcessed();
                } else {
                    return $this->responseMessage($paymentMethod->getErrors());
                }
            }
            if (!$processed) {
                // still not processed would mean some error
                // this would most likely mean IPN was not received yet
                return $this->responseMessage(new XenForo_Phrase('xcfw_your_payment_was_validated_but_processor_not_confirmed', array(
                        'transactionId' => $transactionId
                )));
            }

            if ($processed['status'] === 'payment') {
                $info = $paymentMethod->getAdditionalInfo();
                return $this->responseView('XenCentral_AdServer_ViewPublic_PaymentComplete', 'xcfw_redirect', array(
                        'phrase' => new XenForo_Phrase('xcas_your_purchase_is_successfully_activated'),
                        'link' => XenForo_Link::buildPublicLink('ads')
                ));
            } else {
                return $this->responseMessage(
                        new XenForo_Phrase('xcfw_payment_status_is_x',
                                array('status' => $processed['status'], 'transactionId' => $transactionId)
                        )
                );
            }
        }

        if ($paymentMethod->hasErrors()) {
            XenCentral_PaymentApi_Module::getInstance()->logError($paymentMethod);
        }
        // again, simply thank you page,
        // as some payment providers will not submit payment info to this page
        return $this->responseMessage(new XenForo_Phrase('xcfw_thank_you_for_payment'));
    }

    public function actionPurchaseNotify()
    {
        $paymentMethod = $this->_assertValidPaymentMethod();

        $response = new Zend_Controller_Response_Http();

        if ($paymentMethod->validateRequest()) {
            $processedTransaction = $paymentMethod->transactionProcessed();
            if (
                    $processedTransaction == false // the first notification
                    OR
                    $processedTransaction['status'] != $paymentMethod->getType()
                    AND $processedTransaction['status'] != 'payment' // some update BEFORE the payment was processed
            ) {
                // new transaction or transaction type was updated (pending was approved)
                if ($this->_processTransaction($paymentMethod)) {
                    $paymentMethod->saveTransaction();
                    $response->setHttpResponseCode(200);
                    if ($paymentMethod->getResponseRequired()) {
                        $response->setBody(new XenForo_Phrase('xcfw_payment_is_successfully_processed'));
                    }
                } else {
                    $response->setHttpResponseCode(500);
                    if ($paymentMethod->getResponseRequired()) {
                        $response->setBody($paymentMethod->getLastError());
                    }
                }
            }
        } else {
            $response->setHttpResponseCode(500);
            if ($paymentMethod->getResponseRequired()) {
                $response->setBody(new XenForo_Phrase('xcfw_there_was_a_problem_validating_the_payment'));
            }
        }

        if ($paymentMethod->hasErrors()) {
            XenCentral_PaymentApi_Module::getInstance()->logError($paymentMethod);
        }

        $response->sendResponse();
        exit;
    }

    public function actionTransactions()
    {
        return $this->responseReroute('XenCentral_AdServer_ControllerPublic_Account', 'transactions');
    }

    public function actionCredits()
    {
        return $this->responseReroute('XenCentral_AdServer_ControllerPublic_Account', 'credits');
    }

    public function actionAdvertiserConfiguration()
    {
        return $this->responseReroute('XenCentral_AdServer_ControllerPublic_Account', 'advertiser-configuration');
    }

    /**
     * @param $paymentMethod XenCentral_PaymentApi_Method
     * @throws XenForo_ControllerResponse_Exception
     */
    protected function _processTransaction($paymentMethod)
    {
        $paymentInfo = $paymentMethod->getAdditionalInfo();

        if ($paymentMethod->getType() == 'payment') {
            if (!empty($paymentInfo['banner'])) {
                // activate banner
                return $this->_processBannerPurchase($paymentMethod);
            } else {
                // in case we need to get payment for anything else...
            }
        } else if ($paymentMethod->getType() == 'refund') {
            if (!empty($paymentInfo['banner'])) {
                // process banner refund
                return $this->_processBannerRefund($paymentMethod);
            } else {
                // in case we need to handle refund for anything else...
            }
        } else {
            // the other type we have are pending transactions, we do not do anything for them yet
        }
        return false;
    }

    /**
     * @param $paymentMethod XenCentral_PaymentApi_Method
     * @return bool
     */
    protected function _processBannerPurchase($paymentMethod)
    {
        $paymentInfo = $paymentMethod->getAdditionalInfo();

        $banner = $paymentInfo['banner'];
        $package = $paymentInfo['package'];

        // get banner information again, just in case it was outdated
        $banner = $this->_getBannerModel()->getBannerById($banner['bannerId']);

        $zone = $this->_getZoneModel()->getZoneByIdFromCache($banner['zoneId']);

        $zone->prepareZoneInformation();

        if (
        !$zone->isUserZone()
        ) {
            // this should not happen
            $paymentMethod->setError('invalid_zone', new XenForo_Phrase('xcas_zone_does_not_accept_user_banners'));
            return false;
        }

        if (!$zone->getFreeSlots()) {
            $paymentMethod->setError('no_free_slots', new XenForo_Phrase('xcas_no_free_slots_available_expires_at_x', array(
                    'x' => $zone->getFirstExpirationDate()
            )));
            return false;
        }

        $newData = array();

        if ($package['unit'] == 'impression') {
            $newData['impressions_left'] = max($banner['impressions_left'], 0) + $package['length'];
        } else {
            $length = strtotime(
                            '+ ' .
                            $package['length'] .
                            ' ' . $package['unit'] .
                            ($package['length'] != 1 ? 's' : '')
                    ) - time();

            if (!$banner['active_to'] OR $banner['active_to'] < time()) {
                $newData['active_from'] = time();
                $newData['active_to'] = time() + $length;
            } else {
                // banner is already active, extend its activity period
                $newData['active_to'] = $banner['active_to'] + $length;
            }
        }

        $writer = XenForo_DataWriter::create('XenCentral_AdServer_DataWriter_Banner');
        $writer->setExistingData($banner['bannerId']);
        $writer->bulkSet($newData);
        $writer->save();

        XenCentral_AdServer_Cron::updateBannerStatus();

        // log purchase
        $this->_getPurchaseLogModel()->logPayment(
                'banner',
                $banner['bannerId'],
                $banner['user_id'],
                time(),
                $paymentMethod->getInfoId(),
                $paymentMethod->getTransactionId()
        );

        return true;
    }

    /**
     * @param $paymentMethod XenCentral_PaymentApi_Method
     */
    protected function _processBannerRefund($paymentMethod)
    {
        $paymentInfo = $paymentMethod->getAdditionalInfo();

        $banner = $paymentInfo['banner'];
        $package = $paymentInfo['package'];

        // get banner information again, just in case it was outdated
        $banner = $this->_getBannerModel()->getBannerById($banner['bannerId']);
        $newData = array();
        $newData['active_to'] = 0;
        if ($banner['status'] == 'active') {
            if ($package['unit'] == 'impression') {
                $newData['impressions_left'] = max($banner['impressions_left'] - $package['length'], 0);
            } else {
                $length = strtotime(
                                '+ ' .
                                $package['length'] .
                                ' ' . $package['unit'] .
                                ($package['length'] != 1 ? 's' : '')
                        ) - time();
                if ($banner['active_to'] > time()) {
                    $newData['active_to'] = $banner['active_to'] - $length;
                }
            }
        }
        if ($newData['active_to'] < time()) {
            $newData['active_to'] = 0;
            $newData['active_from'] = 0;
        }

        $writer = XenForo_DataWriter::create('XenCentral_AdServer_DataWriter_Banner');
        $writer->setExistingData($banner['bannerId']);
        $writer->bulkSet($newData);
        $writer->save();

        XenCentral_AdServer_Cron::updateBannerStatus();

        // log purchase
        $this->_getPurchaseLogModel()->logPayment(
                'banner',
                $banner['bannerId'],
                $banner['user_id'],
                time(),
                $paymentMethod->getInfoId(),
                $paymentMethod->getTransactionId()
        );

        return true;
    }

    protected function _checkCsrf($action)
    {
        if ($action == 'PurchaseNotify' OR $action == 'PurchaseReturn') {
            return;
        }

        parent::_checkCsrf($action);
    }

    /**
     * @return XenCentral_PaymentApi_Method
     */
    protected function _assertValidPaymentMethod()
    {
        return $paymentMethod = $this->getHelper('XenCentral_Framework_ControllerHelper_PaymentApi')->assertValidPaymentMethod(
                $this->_getOptionsModel()->debugMode()
        );
    }

    protected function _assertViewingPermissions($action)
    {
        if ($action != 'PurchaseNotify') {
            // avoid any problems with closed boards
            parent::_assertViewingPermissions($action);
        }
    }


    /**
     * @return XenCentral_AdServer_Model_Options
     */
    protected function _getOptionsModel()
    {
        return $this->getModelFromCache('XenCentral_AdServer_Model_Options');
    }

    /**
     * @return XenCentral_AdServer_Model_Stats
     */
    protected function _getStatsModel()
    {
        return $this->getModelFromCache('XenCentral_AdServer_Model_Stats');
    }

    /**
     * @return XenCentral_AdServer_Model_Banner
     */
    protected function _getBannerModel()
    {
        return $this->getModelFromCache('XenCentral_AdServer_Model_Banner');
    }

    /**
     * @return XenCentral_AdServer_Model_Zone
     */
    protected function _getZoneModel()
    {
        return $this->getModelFromCache('XenCentral_AdServer_Model_Zone');
    }

    /**
     * @return XenCentral_AdServer_Model_XenForo_User
     */
    protected function _getUserModel()
    {
        return $this->getModelFromCache('XenForo_Model_User');
    }


    /**
     * @return XenCentral_AdServer_Model_PurchaseLog
     */
    protected function _getPurchaseLogModel()
    {
        return $this->getModelFromCache('XenCentral_AdServer_Model_PurchaseLog');
    }
}