<?php defined('MW_PATH') || exit('No direct script access allowed');

/**
 * CampaignHelper
 * 
 * @package MailWizz EMA
 * @author Serban George Cristian <cristian.serban@mailwizz.com> 
 * @link http://www.mailwizz.com/
 * @copyright 2013-2014 MailWizz EMA (http://www.mailwizz.com)
 * @license http://www.mailwizz.com/license/
 * @since 1.0
 */
 
class CampaignHelper
{    
    private static $_trackingUrls = array();
    
    /**
     * CampaignHelper::parseContent()
     * 
     * This should be always connected with the CampaignTemplate model class::getAvailableTags().
     * Will parse the content tags and transform them
     * 
     * It is used in: 
     * console/components/behaviors/CampaignSenderBehavior.php
     * frontend/controllers/CampaignsController.php
     * 
     * @param string $content
     * @param Campaign $campaign
     * @param ListSubscriber $subscriber
     * @param bool $appendBeacon
     * @return array
     */
    public static function parseContent($content, Campaign $campaign, ListSubscriber $subscriber, $appendBeacon = false)
    {
        $content    = StringHelper::decodeSurroundingTags($content);
        $list       = $campaign->list;
        $options    = Yii::app()->options;

        // parse template content;
        $tagFilter      = new EmailTemplateTagFilter();
        $searchReplace  = array();

        // custom fields tags first so that we can override them with ours if conflict.
        if (!empty($subscriber->subscriber_id)) {
            
            $criteria = new CDbCriteria();
            $criteria->select = 't.field_id, t.tag';
            $criteria->compare('list_id', $list->list_id);
            $fields = ListField::model()->findAll($criteria);
        
            foreach ($fields as $field) {
                $pattern = '/\['.preg_quote($field->tag, '/').'\]/';
                if (!preg_match($pattern, $content) && !preg_match($pattern, $campaign->subject) && !preg_match($pattern, $campaign->to_name)) {
                    continue;
                }
                $valueModel = ListFieldValue::model()->findAllByAttributes(array(
                    'subscriber_id'    => (int)$subscriber->subscriber_id,
                    'field_id'        => (int)$field->field_id,
                ));
                $value = array();
                foreach ($valueModel as $model) {
                    $value[] = $model->value;
                }
                $searchReplace['['.$field->tag.']'] = implode(', ', $value);
            }    
        }

        // common tags;
        $searchReplace['[LIST_NAME]']           = $list->name;
        $searchReplace['[LIST_DESCRIPTION]']    = $list->description;
        $searchReplace['[LIST_FROM_NAME]']      = $list->default->from_name;
        $searchReplace['[LIST_FROM_EMAIL]']     = $list->default->from_email;
        $searchReplace['[LIST_SUBJECT]']        = $list->default->subject;

        // date related
        $searchReplace['[CURRENT_YEAR]']    = date('Y');
        $searchReplace['[CURRENT_MONTH]']   = date('m');
        $searchReplace['[CURRENT_DAY]']     = date('d');
        $searchReplace['[CURRENT_DATE]']    = date('m/d/Y');
        
        $searchReplace['[COMPANY_FULL_ADDRESS]'] = !empty($list->company) ? nl2br($list->company->getFormattedAddress()) : null;
        if (!empty($list->company)) {
            $company = $list->company;
            $searchReplace['[COMPANY_NAME]']        = $company->name;
            $searchReplace['[COMPANY_ADDRESS_1]']   = $company->address_1;
            $searchReplace['[COMPANY_ADDRESS_2]']   = $company->address_2;
            $searchReplace['[COMPANY_CITY]']        = $company->city;
            $searchReplace['[COMPANY_ZONE]']        = !empty($company->zone) ? $company->zone->name : null;
            $searchReplace['[COMPANY_ZIP]']         = $company->zip_code;
            $searchReplace['[COMPANY_COUNTRY]']     = !empty($list->company->country) ? $company->country->name : null;    
            $searchReplace['[COMPANY_PHONE]']       = $company->phone;
        }
        
        $searchFor      = array_keys($searchReplace);
        $replaceWith    = array_values($searchReplace);
        
        $to = str_replace($searchFor, $replaceWith, $campaign->to_name);
        $to = $tagFilter->apply($to, $searchReplace);
        $to = preg_replace('/\[(.*)?\]/', '', $to);
        
        $subject = str_replace($searchFor, $replaceWith, $campaign->subject);
        $subject = $tagFilter->apply($subject, $searchReplace);
        $subject = preg_replace('/\[(.*)?\]/', '', $subject);
        
        if (empty($to) && !empty($subscriber->subscriber_id)) {
            $to = $subscriber->email;
        }
        
        if (empty($to)) {
            $to = 'unknown';
        }
        
        if (empty($subject)) {
            $subject = 'unknown';
        }
        
        // campaign tags, to catch the toName and the campaign subject.
        $searchReplace['[CAMPAIGN_SUBJECT]']    = $subject;
        $searchReplace['[CAMPAIGN_TO_NAME]']    = $to;
        $searchReplace['[CAMPAIGN_FROM_NAME]']  = $campaign->from_name;
        $searchReplace['[CAMPAIGN_FROM_EMAIL]'] = $campaign->from_email;
        $searchReplace['[CAMPAIGN_REPLY_TO]']   = $campaign->reply_to;
        $searchReplace['[CAMPAIGN_UID]']        = $campaign->campaign_uid;
        $searchReplace['[SUBSCRIBER_UID]']      = $subscriber->subscriber_uid;

        // parse the unsubscribe url.
        $unsubscribeUrl = $options->get('system.urls.frontend_absolute_url');
        $unsubscribeUrl .= 'lists/' . $list->list_uid . '/unsubscribe';
        if (!empty($subscriber->subscriber_id)) {
            $unsubscribeUrl .= '/' . $subscriber->subscriber_uid . '/' . $campaign->campaign_uid;
        }

        // update profile url
        $updateProfileUrl = null;
        if (!empty($subscriber->subscriber_id)) {
            $updateProfileUrl = $options->get('system.urls.frontend_absolute_url');
            $updateProfileUrl .= 'lists/' . $list->list_uid . '/update-profile/' . $subscriber->subscriber_uid;
        }
        
        // web version url
        $webVersionUrl = null;
        if (!empty($subscriber->subscriber_id)) {
            $webVersionUrl = $options->get('system.urls.frontend_absolute_url');
            $webVersionUrl .= 'campaigns/' . $campaign->campaign_uid . '/web-version/' . $subscriber->subscriber_uid;    
        }
        
        // campaign url
        $campaignUrl = $options->get('system.urls.frontend_absolute_url');
        $campaignUrl .= 'campaigns/' . $campaign->campaign_uid;
        
        $searchReplace['[UNSUBSCRIBE_URL]']     = $unsubscribeUrl;
        $searchReplace['[UPDATE_PROFILE_URL]']  = $updateProfileUrl;
        $searchReplace['[WEB_VERSION_URL]']     = $webVersionUrl;
        $searchReplace['[CAMPAIGN_URL]']        = $campaignUrl;

        // and run a massive search replace action against the email content!
        $content = str_replace(array_keys($searchReplace), array_values($searchReplace), $content);
        $content = $tagFilter->apply($content, $searchReplace);

        if ($appendBeacon && !empty($subscriber->subscriber_id)) {
            $beaconUrl = $options->get('system.urls.frontend_absolute_url');
            $beaconUrl .= 'campaigns/' . $campaign->campaign_uid . '/track-opening/' . $subscriber->subscriber_uid;
            $beaconImage = CHtml::image($beaconUrl, '', array('width' => 1, 'height' => 1));
            $content = str_replace('</body>', $beaconImage . "\n" . '</body>', $content);
        }
        
        return array($to, $subject, $content);
    }
    
    /**
     * CampaignHelper::transformLinksForTracking()
     * 
     * @param string $content
     * @param Campaign $campaign
     * @param ListSubscriber $subscriber
     * @param bool $canSave
     * @return string
     */
    public static function transformLinksForTracking($content, Campaign $campaign, ListSubscriber $subscriber, $canSave = false)
    {
        $content    = StringHelper::decodeSurroundingTags($content);
        $list       = $campaign->list;
        $mustSave   = false;
        
        if (!isset(self::$_trackingUrls[$campaign->campaign_id])) {
            
            self::$_trackingUrls[$campaign->campaign_id] = array();
            
            $urlModelsCount = 0;
            if ($canSave) {
                $urlModelsCount = CampaignUrl::model()->countByAttributes(array(
                    'campaign_id' => $campaign->campaign_id,
                ));    
            }

            $mustSave       = $urlModelsCount == 0;
            $baseUrl        = Yii::app()->options->get('system.urls.frontend_absolute_url');
            $trackingUrl    = $baseUrl . 'campaigns/[CAMPAIGN_UID]/track-url/[SUBSCRIBER_UID]';
    
            // (\042|\047) are octal quotes.
            $pattern = '/href(\s+)?=(\s+)?(\042|\047)(\s+)?(.*?)(\s+)?(\042|\047)/i';
            if (!preg_match_all($pattern, $content, $matches)) {
                return $content;
            }
            
            $urls = $matches[5];
            $urls = array_map('trim', $urls);
            // combine url with markup
            $urls = array_combine($urls, $matches[0]);
            $foundUrls = array();
            
            foreach ($urls as $url => $markup) {
    
                // external url which may contain one or more tags(sharing maybe?)
                if (preg_match('/https?.*/i', $url, $matches) && filter_var($url, FILTER_VALIDATE_URL)) {
                    $_url = trim($matches[0]);
                    $foundUrls[$_url] = $markup;
                    continue;
                }
                
                // local tag to be transformed
                if (preg_match('/^\[([A-Z_]+)_URL\]$/', $url, $matches)) {
                    $_url = trim($matches[0]);
                    $foundUrls[$_url] = $markup;
                    continue;
                }
            }
            
            if (empty($foundUrls)) {
                return $content;
            }
   
            $prefix = $campaign->campaign_uid;
            $sort   = array();
            
            foreach ($foundUrls as $url => $markup) {

                $urlHash = sha1($prefix . $url);
                $track   = $trackingUrl . '/' . $urlHash;
                $length  = strlen($url);
                
                self::$_trackingUrls[$campaign->campaign_id][] = array(
                    'url'       => $url,
                    'hash'      => $urlHash,
                    'track'     => $track,
                    'length'    => $length,
                    'markup'    => $markup,
                );
                
                $sort[] = $length;
            }
            
            unset($foundUrls);    
            // make sure we order by the longest url to the shortest
            array_multisort($sort, SORT_DESC, SORT_NUMERIC, self::$_trackingUrls[$campaign->campaign_id]);
        }
        
        if (!empty(self::$_trackingUrls[$campaign->campaign_id])) {
            
            $searchReplace = array();
            foreach (self::$_trackingUrls[$campaign->campaign_id] as $urlData) {
                $searchReplace[$urlData['markup']] = 'href="'.$urlData['track'].'"';
            }
            
            $content = str_replace(array_keys($searchReplace), array_values($searchReplace), $content);
            unset($searchReplace);

            // save the url tags.
            if ($mustSave && $canSave) {
                
                foreach (self::$_trackingUrls[$campaign->campaign_id] as $urlData) {
                    
                    $urlModel = CampaignUrl::model()->findByAttributes(array(
                        'campaign_id' => (int)$campaign->campaign_id,
                        'hash'        => $urlData['hash'],
                    ));
                    
                    if (!empty($urlModel)) {
                        continue;
                    }
                    
                    $urlModel = new CampaignUrl();
                    $urlModel->campaign_id = $campaign->campaign_id;
                    $urlModel->destination = $urlData['url'];
                    $urlModel->hash = $urlData['hash'];
                    $urlModel->save();
                }
            }
        }
        
        // return transformed
        return $content;
    }
    
    /**
     * CampaignHelper::html2text()
     * 
     * @param string $content
     * @return string
     */
    public static function htmlTotext($content)
    {
        static $html2text;
        
        if ($html2text === null) {
            Yii::import('common.vendors.Html2Text.*');
            $html2text = new Html2Text();
            
            if (!MW_IS_CLI) {
                $appName = Yii::app()->apps->getCurrentAppName();
                $options = Yii::app()->options;
                $html2text->set_base_url($options->get('system.urls.'.$appName.'_absolute_url'));
            }
        }
        
        $html2text->set_html($content);
        
        return $html2text->get_text();
    }
    
    /**
     * CampaignHelper::getSpamScore()
     * 
     * @param Campaign $campaign
     * @return mixed
     */
    public static function getSpamScore(Campaign $campaign)
    {
        if (empty($campaign->template) || empty($campaign->template->content)) {
            return false;
        }
        
        if (!function_exists('curl_init')) {
            return false;
        }
        
        if (!($server = DeliveryServer::pickServer())) {
            return false;
        }
        
        $subscriber = ListSubscriber::model()->findByAttributes(array(
            'list_id'   => $campaign->list->list_id,
            'status'    => ListSubscriber::STATUS_CONFIRMED,
        ));
        
        $fromName       = $campaign->from_name;
        $toName         = $campaign->from_name;
        $emailSubject   = $campaign->subject;
        $emailContent   = $campaign->template->content;
        $emailAddress   = $campaign->reply_to;
        
        if (!empty($subscriber)) {
            
            if (!empty($campaign->option) && $campaign->option->xml_feed == CampaignOption::TEXT_YES) {
                $emailContent = CampaignXmlFeedParser::parseContent($emailContent);
            }
            
            if (!empty($campaign->option) && $campaign->option->json_feed == CampaignOption::TEXT_YES) {
                $emailContent = CampaignJsonFeedParser::parseContent($emailContent);
            }

            $emailData = self::parseContent($emailContent, $campaign, $subscriber, false);
            list(, $emailSubject, $emailContent) = $emailData;
        }

        $emailPlainText = null;
        if (!empty($campaign->option) && $campaign->option->plain_text_email == CampaignOption::TEXT_YES) {
            $emailPlainText = self::htmlToText($emailContent);
        }
        
        $emailParams = array(
            'from'      => array($server->getFromEmail() => $campaign->from_name),
            'replyTo'   => array($campaign->reply_to => $campaign->from_name),
            'to'        => array($emailAddress => $toName),
            'subject'   => $emailSubject,
            'body'      => $emailContent,
            'plainText' => $emailPlainText,
        );
        
        $emailParams = $server->getParamsArray($emailParams);
        $mailer      = Yii::app()->mailer;

        $mailer->reset();
        
        $message = $mailer->setMessage($emailParams)->getMessage();
        $message = $message->toString();

        $headers = array(
			'Accept: application/json',
			'Content-Type: application/json'
		);
        
        $encodedData = CJSON::encode(array(
            'email'     => $message, 
            'options'   => 'short'
        ));
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'http://spamcheck.postmarkapp.com/filter');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_POSTFIELDS, $encodedData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        
        $response = curl_exec($ch);
        
        if (curl_error($ch) != '') {
            return false;
        }
        
        return CJSON::decode($response);
    }
    
    /**
     * CampaignHelper::resetTrackingUrls()
     * 
     * @return
     */
    public static function resetTrackingUrls()
    {
        self::$_trackingUrls = array();
    }
}