<?php

namespace Velis\Mail\Protocol;

use Exception;
use RuntimeException;
use Velis\App;
use Zend\Mail\Protocol\Smtp;

class SmtpOAuth2 extends Smtp
{
    /**
     * Username for authentication
     * @var string
     */
    protected $username;

    /**
     * Client ID for authentication
     * @var string
     */
    protected $clientId;

    /**
     * Client Secret for authentication
     * @var string
     */
    protected $clientSecret;

    protected XOauth2Auth $xoauth2Auth;

    /**
     * Constructor.
     *
     * The first argument may be an array of all options. If so, it must include
     * the 'host' and 'port' keys in order to ensure that all required values
     * are present.
     *
     * @param  string|array $host
     * @param  null|int $port
     * @param  null|array   $config
     * @throws Exception\InvalidArgumentException
     */
    public function __construct($host = '127.0.0.1', $port = null, array $config = null)
    {
        if (is_array($host)) {
            $this->username = $host['username'];
            $this->clientId = $host['client_id'];
            $this->clientSecret = $host['client_secret'];
        }

        $this->xoauth2Auth = new XOauth2Auth(App::$di->get('msTokenService'));

        parent::__construct($host, $port, $config);
    }


    /**
     * @param string $remote
     * @return void
     */
    protected function _connect($remote): void
    {
        $this->socket = $this->createSocketConnection($remote);
        $this->helo($this->host);

        $this->_send('AUTH XOAUTH2');
        $this->_expect(334);
        $this->_send(
            $this->xoauth2Auth->encodeXoauth2Sasl($this->username, $this->clientId, $this->clientSecret)
        );
        $this->_expect(235);
    }

    /**
     * @param string $remote
     * @return resource
     */
    private function createSocketConnection($remote)
    {
        $context = stream_context_create([
            'ssl' => [
                'verify_peer' => false,
                'verify_peer_name' => false,
                'allow_self_signed' => true,
            ],
        ]);
        $socket = stream_socket_client(
            $remote,
            $errno,
            $errstr,
            30,
            STREAM_CLIENT_CONNECT,
            $context
        );

        if (!$socket) {
            throw new RuntimeException(
                'Could not connect to SMTP host "' . $this->_host . '"',
                0,
                new RuntimeException($errstr, $errno)
            );
        }
        return $socket;
    }

    protected function _disconnect()
    {
        try {
            parent::_disconnect();
        } catch (Exception $e) {
            // ignore
        }
    }
}
