<?php

namespace MailjetWp\MailjetIframe;

/**
 *
 * @package MailjetIframes API v.3
 */
class MailjetIframe {

    public const ON                 = 'on';
    public const OFF                = 'off';
    public const PAGE_STATS         = 'stats';
    public const PAGE_CAMPAIGNS     = 'campaigns';
    public const PAGE_CONTACTS      = 'contacts';
    public const PAGE_AUTOMATION    = 'workflow';
    public const PAGE_WIDGET        = 'widget';
    public const PAGE_TEMPLATES     = 'templates';
    public const PAGE_EDIT_TEMPLATE = 'template/{param}/build';
    public const SESSION_NAME       = 'MailjetIframeToken';
    public const SESSION_SET        = 'MailjetIframeTokenSet';
    /**
     *
     * @var MailjetApi
     */
    private MailjetApi $mailjetApi;
    /**
     *
     * @var array
     */
    private array $locales = array( 'fr_FR', 'en_US', 'en_GB', 'en_EU', 'de_DE', 'es_ES', 'it_IT' );
    /**
     *
     * @var array
     */
    private array $allowedPages = array(
        self::PAGE_STATS,
        self::PAGE_CONTACTS,
        self::PAGE_CAMPAIGNS,
        self::PAGE_AUTOMATION,
        self::PAGE_WIDGET,
        self::PAGE_TEMPLATES,
        self::PAGE_EDIT_TEMPLATE,
    );

    /**
     *
     * @var string
     */
    private string $url = 'https://app.mailjet.com/';
    /**
     *
     * @var string
     */
    private string $callback = '';
    /**
     *
     * @var string
     */
    private string $locale = 'fr_FR';
    /**
     *
     * @var integer
     */
    private int $sessionExpiration = 3600;
    /**
     *
     * @var array
     */
    private array $tokenAccessAvailable = array( 'campaigns', 'contacts', 'stats', 'email_automation', 'widget', 'transactional' );
    /**
     *
     * @var string
     */
    private string $tokenAccess = '';
    /**
     *
     * @var string
     */
    private string $documentationProperties = self::ON;
    /**
     *
     * @var string
     */
    private string $newContactListCreation = self::ON;
    /**
     *
     * @var string
     */
    private string $menu = self::ON;
    /**
     * Flag to mark if to display the black campaign name title bar in the iframe
     *
     * @access  private
     * @var string 'on'/'off'
     */
    private string $showBar = self::ON;
    /**
     * Flag to mark if "create campaign" button in the campaign page should be displayed
     *
     * @access  private
     * @var string 'on'/'off'
     */
    private string $crc = self::OFF;
    /**
     * Flag to mark if MJ sending policy should be removed or not
     *
     * @access  private
     * @var string 'on'/'off'
     */
    private string $sp = self::OFF;
    /**
     *
     * @var string
     */
    private string $initialPage = self::PAGE_STATS;
    /**
     * Flag to mark if footer should be displayed
     *
     * @access  private
     * @var     string $_showFooter
     */
    private string $showFooter = self::ON;
    /**
     *
     * @param string $apiKey
     * @param string $secretKey
     * @param bool   $startSession
     */
    public function __construct( string $apiKey, string $secretKey, bool $startSession = \true ) {
        if (\true === $startSession) {
            $this->startSession();
        }
        $this->mailjetApi = new MailjetApi($apiKey, $secretKey);
    }
    /**
     * Turn email footer signature on/off
     *
     * @param string $flag
     */
    public function turnFooter( $flag ) {
        $this->showFooter = $flag;
        return $this;
    }
    /**
     *
     * @param int $seconds
     * @return MailjetIframe
     * @throws \MailjetIframe\MailjetException
     */
    public function setTokenExpiration( int $seconds = 600 ): MailjetIframe {
        if ( ! \is_numeric($seconds)) {
            throw new MailjetException('Token expiration should be a valid number.');
        }
        if ($seconds <= 0) {
            throw new MailjetException('Token expiration should be greater than 0');
        }
        $this->sessionExpiration = $seconds;
        return $this;
    }
    /**
     *
     * @param string  $callback
     * @param boolean $isEncoded
     * @return MailjetIframe
     */
    public function setCallback( $callback = '', $isEncoded = \false ): MailjetIframe {
        if ($isEncoded) {
            $this->callback = $callback;
        } else {
            $this->callback = \urldecode($callback);
        }
        return $this;
    }
    /**
     *
     * @param string $locale
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function setLocale( $locale = 'fr_FR' ): MailjetIframe {
        if ( ! \in_array($locale, $this->locales)) {
            throw new MailjetException("{$locale} is not supported.");
        }
        $this->locale = $locale;
        return $this;
    }

    /**
     * @param array $access
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function setTokenAccess( array $access = array() ): MailjetIframe {
        foreach ($access as $value) {
            if ( ! \in_array($value, $this->tokenAccessAvailable)) {
                throw new MailjetException("{$value} is not a valid token access.");
            }
        }
        $this->tokenAccess = \implode(', ', $access);
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnDocumentationProperties( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('Documentation properties requires a valid on/off parameter.');
        }
        $this->documentationProperties = $flag;
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnNewContactListCreation( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('New contact list creation requires a valid on/off parameter.');
        }
        $this->newContactListCreation = $flag;
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnMenu( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('Menu requires a valid on/off parameter.');
        }
        $this->menu = $flag;
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnBar( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('Bar requires a valid on/off parameter.');
        }
        $this->showBar = $flag;
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnCreateCampaignButton( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('Create campaign requires a valid on/off parameter.');
        }
        $this->crc = $flag;
        return $this;
    }
    /**
     * @param string $flag
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function turnSendingPolicy( string $flag = self::ON ): MailjetIframe {
        if ( ! $this->isAllowedOnOffParameter($flag)) {
            throw new MailjetException('Sending policy requires a valid on/off parameter.');
        }
        $this->sp = $flag;
        return $this;
    }
    /**
     * @param string $page
     * @param string $param
     * @return MailjetIframe
     * @throws MailjetException
     */
    public function setInitialPage( string $page = self::PAGE_STATS, $param = null ): MailjetIframe {
        if ( ! \in_array($page, $this->allowedPages)) {
            throw new MailjetException("{$page} is unknown.");
        }
        if (\strpos($page, '{param}') !== \false) {
            if ( ! isset($param)) {
                throw new MailjetException("{$page} need a parameter");
            }
            $page = \str_replace('{param}', $param, $page);
        }
        $this->initialPage = $page;
        return $this;
    }
    /**
     *
     * @return string
     */
    public function getHtml( $isPassportIframe = \false ): string {
        $iframeUrl = $this->getIframeUrl($isPassportIframe);
        $html      = <<<HTML

<iframe
  width="100%s"
  height="100%s"
  frameborder="0" style="border:0"
  src="%s">
</iframe>

HTML;
        return \sprintf($html, '%', '%', $iframeUrl);
    }
    /**
     *
     * @param mixed $parameter
     * @return bool
     */
    private function isAllowedOnOffParameter( $parameter ): bool {
        if ($parameter !== self::ON && $parameter !== self::OFF) {
            return \false;
        }
        return \true;
    }
    /**
     *
     * @return string
     * @throws MailjetException
     */
    private function getToken() {
        if ( ! isset($_SESSION[ self::SESSION_NAME ])) {
            $_SESSION[ self::SESSION_NAME ] = $this->generateToken();
            $_SESSION[ self::SESSION_SET ]  = \time();
        } elseif (\time() - $_SESSION[ self::SESSION_SET ] >= $this->sessionExpiration) {
                $_SESSION[ self::SESSION_NAME ] = $this->generateToken();
                $_SESSION[ self::SESSION_SET ]  = \time();
        }
        return $_SESSION[ self::SESSION_NAME ];
    }
    /**
     *
     * @return string
     * @throws MailjetException
     */
    private function generateToken() {
        $params = array(
			'method'        => 'JSON',
			'AllowedAccess' => $this->tokenAccess,
			'IsActive'      => 'true',
			'TokenType'     => 'iframe',
			'APIKeyALT'     => $this->mailjetApi->getAPIKey(),
			'ValidFor'      => $this->sessionExpiration,
		);
        // get the response
        $response = $this->mailjetApi->apitoken($params)->getResponse();
        if ( ! $response) {
            throw new MailjetException('The Mailjet API does not respond.');
        }
        if ($response->Count <= 0) {
            throw new MailjetException('The Mailjet API object not found.');
        }
        if ( ! isset($response->Data[0]->Token)) {
            throw new MailjetException('The Mailjet API returned invalid response.');
        }
        return $response->Data[0]->Token;
    }
    /**
     *
     * @return string
     */
    private function getIframeUrl( $isPassportIframe = \false ): string {
        $url = $this->url . $this->initialPage . '?t=' . $this->getToken();
        if ($isPassportIframe) {
            $url .= '&lang=' . $this->locale;
        } else {
            $url .= '&locale=' . $this->locale;
        }
        if ($this->callback !== '') {
            $url .= '&cb=' . $this->callback;
        }
        if ($this->documentationProperties === self::OFF) {
            $url .= '&d=hide';
        }
        if ($this->newContactListCreation === self::OFF) {
            $url .= '&l=yes';
        }
        if ($this->menu === self::OFF) {
            $url .= '&show_menu=none';
        }
        if ($this->showBar === self::ON) {
            $url .= '&show_bar=yes';
        }
        if ($this->crc === self::OFF) {
            $url .= '&crc=hide';
        }
        if ($this->sp === self::ON) {
            $url .= '&sp=display';
        }
        $url .= $this->showFooter === self::OFF ? '&sftr=empty' : '';
        return $url;
    }
    /**
     * @return void
     */
    private function startSession(): void {
        if (\false === $this->isSessionStarted()) {
            \session_start();
        }
    }
    /**
     * @return bool
     */
    private function isSessionStarted(): bool {
        if (php_sapi_name() !== 'cli') {
            if (\version_compare(\phpversion(), '7.4', '>=')) {
                return \session_status() === \PHP_SESSION_ACTIVE;
            }

            return ! (session_id() === '');
        }
        return \false;
    }
}
