<?php
/**
 * Pimcore
 *
 * This source file is subject to the GNU General Public License version 3 (GPLv3)
 * For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
 * files that are distributed with this source code.
 *
 * @copyright  Copyright (c) 2009-2016 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GNU General Public License version 3 (GPLv3)
 */

namespace Pimcore\Tool;

use Pimcore\Mail;
use Pimcore\Tool;
use Pimcore\Model\Object;
use Pimcore\Model\Document;
use Pimcore\Model;

class Newsletter
{

    /**
     * @var Object\ClassDefinition
     */
    protected $class;

    /**
     * @param Model\Tool\Newsletter\Config $newsletter
     * @param Object\Concrete $object
     */
    public static function sendMail($newsletter, $object, $emailAddress = null, $hostUrl = null)
    {
        $params = array(
            "gender" => $object->getGender(),
            'firstname' => $object->getFirstname(),
            'lastname' => $object->getLastname(),
            "email" => $object->getEmail(),
            'token' => $object->getProperty("token"),
            "object" => $object
        );

        $mail = new Mail();
        $mail->setIgnoreDebugMode(true);

        if (\Pimcore\Config::getSystemConfig()->newsletter->usespecific) {
            $mail->init("newsletter");
        }

        if (!Tool::getHostUrl() && $hostUrl) {
            $mail->setHostUrl($hostUrl);
        }

        if ($emailAddress) {
            $mail->addTo($emailAddress);
        } else {
            $mail->addTo($object->getEmail());
        }
        $mail->setDocument(Document::getById($newsletter->getDocument()));
        $mail->setParams($params);

        // render the document and rewrite the links (if analytics is enabled)
        if ($newsletter->getGoogleAnalytics()) {
            if ($content = $mail->getBodyHtmlRendered()) {
                include_once("simple_html_dom.php");

                $html = str_get_html($content);
                if ($html) {
                    $links = $html->find("a");
                    foreach ($links as $link) {
                        if (preg_match("/^(mailto)/", trim(strtolower($link->href)))) {
                            continue;
                        }

                        $glue = "?";
                        if (strpos($link->href, "?")) {
                            $glue = "&";
                        }
                        $link->href = $link->href . $glue . "utm_source=Newsletter&utm_medium=Email&utm_campaign=" . $newsletter->getName();
                    }
                    $content = $html->save();

                    $html->clear();
                    unset($html);
                }

                $mail->setBodyHtml($content);
            }
        }

        $mail->send();
    }

    /**
     * @param null $classId
     * @throws \Exception
     */
    public function __construct($classId = null)
    {
        $class = null;
        if (is_string($classId)) {
            $class = Object\ClassDefinition::getByName($classId);
        } elseif (is_int($classId)) {
            $class = Object\ClassDefinition::getById($classId);
        } elseif ($classId !== null) {
            throw new \Exception("No valid class identifier given (class name or ID)");
        }

        if ($class instanceof Object\ClassDefinition) {
            $this->setClass($class);
        }
    }

    /**
     * @return string
     */
    protected function getClassName()
    {
        return "\\Pimcore\\Model\\Object\\" . ucfirst($this->getClass()->getName());
    }

    /**
     * @param array $params
     * @return bool
     */
    public function checkParams($params)
    {
        if (!array_key_exists("email", $params)) {
            return false;
        }

        if (strlen($params["email"]) < 6 || !strpos($params["email"], "@") || !strpos($params["email"], ".")) {
            return false;
        }

        return true;
    }

    /**
     * @param $params
     * @return mixed
     * @throws \Exception
     */
    public function subscribe($params)
    {
        $onlyCreateVersion = false;
        $className = $this->getClassName();
        $object = new $className;

        // check for existing e-mail
        $existingObject = $className::getByEmail($params["email"], 1);
        if ($existingObject) {
            // if there's an existing user with this email address, do not overwrite the contents, but create a new
            // version which will be published as soon as the contact gets verified (token/email)
            $object = $existingObject;
            $onlyCreateVersion = true;
            //throw new \Exception("email address '" . $params["email"] . "' already exists");
        }

        if (!array_key_exists("email", $params)) {
            throw new \Exception("key 'email' is a mandatory parameter");
        }

        $object->setValues($params);

        if (!$object->getParentId()) {
            $object->setParentId(1);
        }

        $object->setNewsletterActive(true);
        $object->setCreationDate(time());
        $object->setModificationDate(time());
        $object->setUserModification(0);
        $object->setUserOwner(0);
        $object->setPublished(true);
        $object->setKey(\Pimcore\File::getValidFilename($object->getEmail() . "~" . substr(uniqid(), -3)));

        if (!$onlyCreateVersion) {
            $object->save();
        }

        // generate token
        $token = base64_encode(\Zend_Json::encode(array(
            "salt" => md5(microtime()),
            "email" => $object->getEmail(),
            "id" => $object->getId()
        )));
        $token = str_replace("=", "~", $token); // base64 can contain = which isn't safe in URL's
        $object->setProperty("token", "text", $token);

        if (!$onlyCreateVersion) {
            $object->save();
        } else {
            $object->saveVersion();
        }

        $this->addNoteOnObject($object, "subscribe");

        return $object;
    }

    /**
     * @param $object
     * @param $mailDocument
     * @param array $params
     * @throws \Exception
     */
    public function sendConfirmationMail($object, $mailDocument, $params = array())
    {
        $defaultParameters = array(
            "gender" => $object->getGender(),
            'firstname' => $object->getFirstname(),
            'lastname' => $object->getLastname(),
            "email" => $object->getEmail(),
            'token' => $object->getProperty("token"),
            "object" => $object
        );

        $params = array_merge($defaultParameters, $params);

        $mail = new Mail();
        $mail->addTo($object->getEmail());
        $mail->setDocument($mailDocument);
        $mail->setParams($params);
        $mail->send();
    }

    /**
     * @param $token
     * @return bool
     * @throws \Zend_Json_Exception
     */
    public function getObjectByToken($token)
    {
        $originalToken = $token;
        $token = str_replace("~", "=", $token); // base64 can contain = which isn't safe in URL's

        $data = \Zend_Json::decode(base64_decode($token));
        if ($data) {
            if ($object = Object::getById($data["id"])) {
                if ($version = $object->getLatestVersion()) {
                    $object = $version->getData();
                }

                if ($object->getProperty("token") == $originalToken) {
                    if ($object->getEmail() == $data["email"]) {
                        return $object;
                    }
                }
            }
        }
        return false;
    }

    /**
     * @param string $token
     * @return bool
     */
    public function confirm($token)
    {
        $object = $this->getObjectByToken($token);
        if ($object) {
            if ($version = $object->getLatestVersion()) {
                $object = $version->getData();
                $object->setPublished(true);
            }

            $object->setNewsletterConfirmed(true);
            $object->save();

            $this->addNoteOnObject($object, "confirm");

            return true;
        }

        return false;
    }

    /**
     * @param string $token
     * @return bool
     */
    public function unsubscribeByToken($token)
    {
        $object = $this->getObjectByToken($token);
        if ($object) {
            return $this->unsubscribe($object);
        }

        return false;
    }

    /**
     * @param string $email
     * @return bool
     */
    public function unsubscribeByEmail($email)
    {
        $className = $this->getClassName();
        $objects = $className::getByEmail($email);
        if (count($objects)) {
            foreach ($objects as $object) {
                $this->unsubscribe($object);
            }
            return true;
        }

        return false;
    }


    /**
     * @param $object
     * @return bool
     */
    public function unsubscribe($object)
    {
        if ($object) {
            $object->setNewsletterActive(false);
            $object->save();

            $this->addNoteOnObject($object, "unsubscribe");

            return true;
        }
        return false;
    }

    /**
     * @param $object
     * @param $title
     */
    public function addNoteOnObject($object, $title)
    {
        $note = new Model\Element\Note();
        $note->setElement($object);
        $note->setDate(time());
        $note->setType("newsletter");
        $note->setTitle($title);
        $note->setUser(0);
        $note->setData(array(
            "ip" => array(
                "type" => "text",
                "data" => Tool::getClientIp()
            )
        ));
        $note->save();
    }

    /**
     * @param Object\ClassDefinition $class
     */
    public function setClass($class)
    {
        $this->class = $class;
    }

    /**
     * @return Object\ClassDefinition
     */
    public function getClass()
    {
        return $this->class;
    }
}
