<?php

// https://github.com/newsletter2go/newsletter2go-api-php

// curl 'https://api-sandbox.newsletter2go.com/oauth/v2/token' -H 'Authorization: Basic cg1pe8cz_V2XteW_eE5tov_d4CQlWB0_ac6eFjFoXJ%3Acc3nbccj' --data 'username=n2gotest1701%40mailbeez.com&password=mailbeez12&grant_type=https%3A%2F%2Fnl2go.com%2Fjwt'$custom_headerclear


namespace NL2GO;


final class Api
{
    private static $instance;


    const GRANT_TYPE = "https://nl2go.com/jwt";
    const N2GO_REFRESH_GRANT_TYPE = 'https://nl2go.com/jwt_refresh';

    const BASE_URL = "https://api.newsletter2go.com";
//    const BASE_URL = "https://api-sandbox.newsletter2go.com";

    const BASE_UI_URL = "https://ui.newsletter2go.com";
//    const BASE_UI_URL = "https://ui-sandbox.newsletter2go.com";

    const METHOD_GET = "GET";
    const METHOD_POST = "POST";
    const METHOD_PATCH = "PATCH";
    const METHOD_DELETE = "DELETE";

    private $user_auth_key = "authkey";
    private $access_token = "";
    private $refresh_token = "";

    private $sslVerification = true;

    var $integrationsData = null;
    var $companiesData = null;
    var $companiesPlanData = null;
    var $senderidsData = null;
    var $campaignsData;
    var $listName = 'MailBeez Integration';
    var $listId;
    var $entryPoint;


    private function __construct($checkIntegration = true)
    {
        $this->access_token = mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_ACCESS_TOKEN');
        $this->campaignsData = (array)json_decode(mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_CAMPAIGN_DATA'), true);
        $this->domainsData = (array)json_decode(mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_DOMAINS_DATA'), true);
        $this->listId = mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_LIST_ID');

        if ($checkIntegration) {
            $this->checkIntegration(mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_AUTH_KEY'), mh_lng('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_ACCESS_TOKEN'), mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_REFRESH_TOKEN'));
        }
    }

    static public function buildEntrypoint()
    {

        // todo
        // refactoring
// production


// curl -X POST --header 'Content-Type: application/x-www-form-urlencoded' --header 'Accept: application/json' -d 'username=n2gotest1701%40mailbeez.com&password=mailbeez12&grant_type=https%3A%2F%2Fnl2go.com%2Fjwt' 'https://ui-sandbox.newsletter2go.com/oauth/v2/token'

// development
// get accesstoken, refreshtoken for local environment

// curl 'https://api-sandbox.newsletter2go.com/oauth/v2/token' -u 'cg1pe8cz_V2XteW_eE5tov_d4CQlWB0_ac6eFjFoXJ:Acc3nbccj' --data 'username=n2gotest1701%40mailbeez.com&password=mailbeez12&grant_type=https%3A%2F%2Fnl2go.com%2Fjwt'

        mh_define('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_LANGUAGE', 'en');

        define('N2GO_LANGUAGE', mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_LANGUAGE'));
        define('N2GO_REF', 'd09a2eb1cb251883284c6dce5d37a7bf');
        define('N2GO_USER', 'testuser');
        define('N2GO_COMPANY', 'company');
        define('N2GO_WEBSITE', $_SERVER['HTTP_HOST']);
        define('N2GO_ENTRYPOINT', str_replace(FILENAME_HIVE, '', MAILBEEZ_MAILHIVE_URL_DIRECT));// for api exposed to N2Go
        define('N2GO_CALLBACK', MAILBEEZ_MAILHIVE_URL_DIRECT . '?m=service_newsletter2go_api_v2&ma=oauth&apiKey=' . mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_SECRET'));

        $params = array(
            'language' => N2GO_LANGUAGE,
            'ref' => N2GO_REF,
            'user' => N2GO_USER,
            'company' => N2GO_COMPANY,
            'apiKey' => mh_cfg('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_SECRET'),
            'website' => N2GO_WEBSITE,
            'entrypoint' => N2GO_ENTRYPOINT, // for api exposed to N2Go
            'callback' => urlencode(N2GO_CALLBACK)
        );
        $api_params = http_build_query($params);

        mh_define('MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_REGISTER_ENTRYPOINT', self::BASE_UI_URL . '/integrations/connect/MB/?' . $api_params);

        return MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_REGISTER_ENTRYPOINT;
    }


    // https://github.com/kamranahmedse/design-patterns-for-humans#-singleton
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /*

    private function getToken($user_email, $user_pw)
    {

        $endpoint = "/oauth/v2/token";

        $data = array(
            "username"   => $user_email,
            "password"   => $user_pw,
            "grant_type" => static::GRANT_TYPE

        );

        $response = $this->_curl('Basic ' . base64_encode($this->user_auth_key), $endpoint, $data, "POST");

        $this->access_token = $response->access_token;
        $this->refresh_token = $response->refresh_token;
    }

    */
    public function setSSLVerification($enable)
    {
        $this->sslVerification = $enable;
    }


    public function checkIntegration($authKey, $accessToken, $refreshToken, $initcheck = true)
    {
        if ($initcheck) {

            $this->access_token = $accessToken;

            $response = $this->getCompanies();

            if ($response->http_status == 403 || $response->http_status == 401) { //access_token is deprecated
                $this->companiesData = null;

                $newAccessToken = $this->refreshAccessToken($authKey, $refreshToken);
                return $this->checkIntegration($authKey, $newAccessToken, $refreshToken, false);
            } else if ($response->http_status == 400 || $response->http_status == 404) {
                $this->companiesData = null;

                //integration is not found
                return false;
            } else {
                //get user-integration
                $integration = $response->value[0];
                if ($integration->connection_success) {
                    //integration works
                    return true;
                } else {
                    //integration testconnection failed
                    return false;
                }
            }
        } else {
            // avoiding loop
//            echo "error";
        }
    }


    /**
     * Create request to Newsletter2Go API
     * to refresh access token
     *
     * @param string $authKey - authorization key that was received from * callback
     * @param string $refreshToken - refresh token that was received from callback
     * @return true
     */
    public function refreshAccessToken($authKey, $refreshToken)
    {
        $auth = base64_encode($authKey);
        // Encode authorization key value with base64.
        // Create array of elements: refresh token and refresh grant type
        // used to send in POST request

        $refreshPost = array(
            'refresh_token' => $refreshToken,
            'grant_type' => self::N2GO_REFRESH_GRANT_TYPE);

        $url = self::BASE_URL . '/oauth/v2/token'; // Create URL for cURL call
        // Create Authorization parameters for header
        $header = array(
            'Authorization: Basic ' . $auth,
        );

        $curl = curl_init($url); // Initialize a new session and return a cURL handle for use
        // Set options to the given cURL session handle
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $refreshPost);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

        // Execute the given cURL session
        $json_response = curl_exec($curl);
        $info = curl_getinfo($curl);
        // Close the cURL session and free all resources
        curl_close($curl);
        // Convert JSON encoded string

        $response = json_decode($json_response);

        if (isset($response->access_token) && !empty($response->access_token)) {
            // Save access token value into database if value is set and not empty
            self::setAccessToken($response->access_token);
            $this->access_token = $response->access_token;
        }
        if (isset($response->refresh_token) && !empty($response->refresh_token)) {
            // Save refresh token value into database if value is set and not   empty
            self::setRefreshToken($response->refresh_token);
            $this->refresh_token = $response->refresh_token;

        }
        return true;
    }

    public function ping()
    {
        $raw = $this->getCompanies();

        if (is_array($raw) && sizeof($raw) == 0) {
            return false;
        }

        return !( (400 == $raw->http_status) || (401 == $raw->http_status));
    }


    public function getIntegrations()
    {
        $endpoint = "/users/integrations";

        $data = array();

        if (!$this->integrationsData) {
            try {
                $this->integrationsData = $this->curl($endpoint, $data);
            } catch (\Exception $e) {
                return null;
            }
        }
        return $this->integrationsData;
    }


    public function getIntegrationData()
    {

        if (isset($this->integrationData)) {
            return $this->integrationData;
        }
        $this->integrationData = array();
        $allIntegrations = $this->getIntegrations();
        if (is_object($allIntegrations) && sizeof($allIntegrations->value) > 0) {
            foreach ($allIntegrations->value as $integration) {
                $endpoint = "/users/integrations/" . $integration->id;

                try {
                    $integrationInfo = $this->curl($endpoint, array());
                } catch (\Exception $e) {
                    $integrationInfo = null;
                    $this->integrationData = null;
                    return $this->integrationData;
                }


                if ($integrationInfo->value[0]->abbreviation == 'MB') {
                    $this->integrationData = $integrationInfo->value[0];
                    break;
                }
            }
        }
        return $this->integrationData;

    }


    public function getCompanies()
    {
        $endpoint = "/companies";

        $data = array();

        if (!$this->companiesData) {
            try {
                $this->companiesData = $this->curl($endpoint, $data);
            } catch (\Exception $e) {
                //print_r($e);
                return array();
            }
        }
        return $this->companiesData;
    }

    // todo
    //
    public function getCompaniesPlan()
    {
        $endpoint = "/companies/plan";

        $data = array();

        if (!$this->companiesPlanData) {
            try {
                $this->companiesPlanData = $this->curl($endpoint, $data);
            } catch (\Exception $e) {
                return array();
            }
        }
        return $this->companiesPlanData;
    }

    public function getStatus()
    {
        return $this->getCompanies()->value[0]->state;

    }

    public function getCredits()
    {
        $data = array(
            'credits_email' => $this->getCompanies()->value[0]->credits_email,
            'credits_freemail' => $this->getCompanies()->value[0]->credits_freemail,
            'credits_abo' => $this->getCompanies()->value[0]->credits_abo,
        );
        $data['credits_total'] = $data['credits_email'] + $data['credits_abo'] + $data['credits_freemail'];
        return $data;
    }


    public function postSenderidAdd($domain)
    {
        $endpoint = "/senderid";
        $data = array(
            'domain' => $domain
        );
        try {
            $result = $this->curl($endpoint, $data, static::METHOD_POST);
        } catch (\Exception $e) {
            $result = array();
        }


        return $result;
    }


    public function postSenderidCheck($id)
    {
        $endpoint = "/senderid/$id/check";
        $data = array();

        try {
            $result = $this->curl($endpoint, $data, static::METHOD_POST);
        } catch (\Exception $e) {
            $result = array();
        }

        return $result;
    }

    public function getSenderidData($id)
    {
        // check sender id status
        $checkresult = $this->postSenderidCheck($id);

        $endpoint = "/senderid/$id";
        $data = array();

        try {
            $result = $this->curl($endpoint, $data, static::METHOD_GET);
        } catch (\Exception $e) {
            $result = array();
        }

        return $result;
    }


    public function getSenderids()
    {
        $endpoint = "/senderid";

        $data = array();
        $senderData = array();

        if (!$this->senderidsData) {

            try {
                $raw = $this->curl($endpoint, $data);
            } catch (\Exception $e) {
                $raw = array();
            }

            if (is_array($raw->value)) {
                foreach ($raw->value as $entry) {
                    $entry_res = $this->getSenderidData($entry->id);
                    $senderData[] = (array)$entry_res->value[0];
                }
            }
            $this->senderidsData = $senderData;
        }
        return $this->senderidsData;
    }

    public function checkSenderids()
    {
        $senderIds = $this->getSenderids();
        foreach ($senderIds as $senderId) {
            if (!$senderId['is_verified']) {
                return false;
            }
        }
        return true;
    }

    public function getSenderidsData()
    {

    }


    public function getUsers()
    {
        $endpoint = "/users";

        $data = array(
            "_expand" => true
        );
        try {
            $result = $this->curl($endpoint, $data);
        } catch (\Exception $e) {
            $result = array();
        }
        return $result;
    }

    public function getNewsletters($listId)
    {
        $endpoint = "/lists/$listId/newsletters";

        try {
            $result = $this->curl($endpoint, array());
        } catch (\Exception $e) {
            $result = array();
        }
        return $result;

    }

    public function getOrCreateListId()
    {
        if ($this->listId != '') {
            return $this->listId;
        }

        // check if list with name already exists
        $listsData = $this->getLists();

        foreach ($listsData->value as $listData) {
            if ($listData->name == $this->listName) {
                $this->listId = $listData->id;
                break;
            }
        }
        if ($this->listId == '') {
            $endpoint = "/lists";
            $data = array(
                "name" => $this->listName
            );

            try {
                $result = $this->curl($endpoint, $data, static::METHOD_POST);
            } catch (\Exception $e) {
                $result = array();
            }

            $this->listId = $result->value[0]->id;
        }

        mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_LIST_ID',
            'configuration_value' => $this->listId,
        ));

        return $this->listId;
    }

    public function getOrCreateCampaign($name)
    {
        if (!is_array($this->campaignsData)) {
            $this->campaignsData = array();
        }

        if (isset($this->campaignsData[$name])) {
            return $this->campaignsData[$name];
        }

        $listId = $this->getOrCreateListId();

        $result = $this->createNewsletter($listId, 'transaction', $name, '', '');
        $campaignId = $result->value[0]->id;

        $this->campaignsData[$name] = $campaignId;

        mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_CAMPAIGN_DATA',
            'configuration_value' => json_encode($this->campaignsData),
        ));

        return $campaignId;
    }

    public function postSendMailBeez($campaignName, $subject, $html, $recipientEmail, $recipientName, $senderEmail, $senderName, $replyEmail, $replyName)
    {

        $campaignId = $this->getOrCreateCampaign($campaignName);

        $endpoint = "/newsletters/$campaignId/send/mailbeez";
        $data = array(
            "newsletter" => array(
                "html" => $html,
                "subject" => $subject,
                "header_from_email" => $senderEmail,
                "header_from_name" => $senderName,
                "header_reply_email" => $replyEmail,
                "header_reply_name" => $replyName,

            ),
            "recipients" => array(
                array(
                    "email" => $recipientEmail,
                    "first_name" => $recipientName)
            )
        );
        $this->checkOrAddSenderDomain($senderEmail);

        try {
            $result = $this->curl($endpoint, $data, static::METHOD_POST);
        } catch (\Exception $e) {
            $result = array();
        }


        // status: 201 - ok
        // with error
        // code:
        // 10031 - Not allowed to sent
        // 10048 - not enough credits
        //
        /* error result:
          (
             [status] => 500
             [code] => 500
             [errorMessage] => The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
             [http_status] => 500
         )
         */
        return $result;
    }

    public function checkOrAddSenderDomain($senderEmail)
    {
        list ($e, $domain) = explode('@', $senderEmail);

        if (!is_array($this->domainsData)) {
            $this->domainsData = array();
        }

        if (isset($this->domainsData[$domain])) {
            return $this->domainsData[$domain];
        }

        $this->postSenderidAdd($domain);

        $this->domainsData[$domain] = $domain;

        mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_DOMAINS_DATA',
            'configuration_value' => json_encode($this->domainsData),
        ));
        return true;
    }

    public function createNewsletter($listId, $type, $name, $subject, $html)
    {
        if (!in_array($type, array("transaction", "default", "ab", "recurring", "doi"))) {
            throw new \Exception("Mailing type not supported");
        }
        $endpoint = "/lists/$listId/newsletters";

        try {
            $result = $this->curl($endpoint, array("type" => $type, "name" => $name, "subject" => $subject, "html" => $html), static::METHOD_POST);
        } catch (\Exception $e) {
            //print_r($e);
            $result = array();
        }

        return $result;
    }

    public function updateNewsletter($newsletterId, $name, $subject, $html)
    {
        $endpoint = "/newsletters/$newsletterId";

        try {
            $result = $this->curl($endpoint, array("name" => $name, "subject" => $subject, "html" => $html), static::METHOD_PATCH);
        } catch (\Exception $e) {
            $result = array();
        }

        return $result;
    }

    public function deleteNewsletter($newsletterId)
    {
        $endpoint = "/newsletters/$newsletterId";
        try {
            $result = $this->curl($endpoint, array(), static::METHOD_DELETE);
        } catch (\Exception $e) {
            $result = array();
        }

        return $result;
    }

    public function getLists()
    {
        $endpoint = "/lists";

        $data = array(
            "_expand" => true
        );

        try {
            $result = $this->curl($endpoint, $data);
        } catch (\Exception $e) {
            $result = array();
        }

        return $result;
    }


    static function setOauthData()
    {
        $valid = false;
        if (isset($_POST["auth_key"]) && !empty($_POST["auth_key"])) {
            $authKey = $_POST["auth_key"];
            // Save auth key value into database
            $valid = self::setAuthKey($authKey);
        }
        // get callback parameter for access token
        if (isset($_POST["access_token"]) && !empty($_POST["access_token"])) {
            $accessToken = $_POST["access_token"];
            // Save access token value into database
            $valid = self::setAccessToken($accessToken);
        }
        // get callback parameter for refresh token
        if (isset($_POST["refresh_token"]) && !empty($_POST["refresh_token"])) {
            $refreshToken = $_POST["refresh_token"];
            // Save refresh token value into database
            $valid = self::setRefreshToken($refreshToken);
        }
        return $valid;
    }

    static function setAuthKey($authKey)
    {
        return mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_AUTH_KEY',
            'configuration_value' => $authKey,
        ));
    }

    static function setAccessToken($accessToken)
    {
        return mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_ACCESS_TOKEN',
            'configuration_value' => $accessToken,
        ));
    }

    static function setRefreshToken($refreshToken)
    {
        return mh_update_config_value(array(
            'configuration_key' => 'MAILBEEZ_CONFIG_EMAIL_ENGINE_N2G_API_V2_REFRESH_TOKEN',
            'configuration_value' => $refreshToken,
        ));
    }


    static function unlink() {
        self::setAuthKey('');
        self::setAccessToken('');
        self::setRefreshToken('');
    }

    static function sendJson($data, $http_status = 200)
    {
        $http_status = (isset($data['status'])) ? $data['status'] : $http_status;

        switch ($http_status) {
            case 200:
                $status = '200 Ok';
                break;
            case 400:
                $status = '400 Bad request';
                break;
            case 401:
                $status = '401 Unauthorized';
                break;
            case 404:
                $status = '404 Object not found';
                break;
            case 501:
                $status = '501 Not Implemented';
                break;
            default:
                $status = '200 Ok';
                break;
        }

        header($_SERVER["SERVER_PROTOCOL"] . " $status");
        header('Content-Type: application/json');
        echo json_encode($data);

    }


    /**
     * @param $endpoint string the endpoint to call (see docs.newsletter2go.com)
     * @param $data array tha data to submit. In case of POST and PATCH its submitted as the body of the request. In case of GET and PATCH it is used as GET-Params. See docs.newsletter2go.com for supported parameters.
     * @param string $type GET,PATCH,POST,DELETE
     * @return \stdClass
     * @throws \Exception
     */
    public function curl($endpoint, $data, $type = "GET", $custom_header = null)
    {
        if (!isset($this->access_token) || strlen($this->access_token) == 0) {
            throw new \Exception("N2Go Authentication failed");
        }
        return $this->_curl('Bearer ' . $this->access_token, $endpoint, $data, $type, $custom_header);
    }

    private function _curl($authorization, $endpoint, $data, $type = "GET", $custom_header = null)
    {

        $ch = curl_init();

        $data_string = json_encode($data);

        $get_params = "";

        if ($type == static::METHOD_POST || $type == static::METHOD_PATCH) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
            $post = true;
        } else {
            if ($type == static::METHOD_GET || $type == static::METHOD_DELETE) {
                $post = false;
                $get_params = "?" . http_build_query($data);
            } else {
                throw new \Exception("Invalid HTTP method: " . $type);
            }
        }

        curl_setopt($ch, CURLOPT_POST, $post);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
        curl_setopt($ch, CURLOPT_URL, static::BASE_URL . $endpoint . $get_params);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_setopt($ch, CURLOPT_TIMEOUT, 30);


        if ($custom_header) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $custom_header);

        } else {

            curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: application/json',
                'Authorization: ' . $authorization,
                'Content-Length: ' . ($type == static::METHOD_GET || $type == static::METHOD_DELETE) ? 0 : strlen($data_string)
            ));
        }


        if (!$this->sslVerification) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        }
        $response = curl_exec($ch);
        $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);


        $result = json_decode($response);
        if (is_object($result)) {
            $result->http_status = $httpStatus;

        }

        return $result;
    }
}