<?php

namespace CookieSession\Encrypter;

abstract class AbstractEncrypter
{
    protected $method;
    protected $secret;
    protected $iv;
    protected $private_key_str;
    protected $private_key_passphrase;
    protected $public_key_str;
    protected $private_key = null;
    protected $public_key = null;
    protected $signature_algo;

    protected static $sig_algos = [
        OPENSSL_ALGO_DSS1,
        OPENSSL_ALGO_SHA1,
        OPENSSL_ALGO_SHA224,
        OPENSSL_ALGO_SHA256,
        OPENSSL_ALGO_SHA384,
        OPENSSL_ALGO_SHA512,
        OPENSSL_ALGO_RMD160,
        OPENSSL_ALGO_MD5
    ];

    protected static $sig_algos_labelled = [

        [
            'label' => 'SHA1',
            'value' => OPENSSL_ALGO_SHA1
        ],

        [
            'label' => 'SHA224',
            'value' => OPENSSL_ALGO_SHA224
        ],

        [
            'label' => 'SHA256',
            'value' => OPENSSL_ALGO_SHA256
        ],

        [
            'label' => 'SHA384',
            'value' => OPENSSL_ALGO_SHA384
        ],

        [
            'label' => 'SHA512',
            'value' => OPENSSL_ALGO_SHA512
        ],

        [
            'label' => 'RMD160',
            'value' => OPENSSL_ALGO_RMD160
        ],

        [
            'label' => 'MD5',
            'value' => OPENSSL_ALGO_MD5
        ],

        [
            'label' => 'DSS1 (for DSA keys)',
            'value' => OPENSSL_ALGO_DSS1
        ]
    ];


    public function __construct($method = null, $secret = null, $iv = null, $sig_algo = OPENSSL_ALGO_SHA1)
    {
        $this->method = $method;
        $this->secret = $secret;
        $this->iv = $iv;
        $this->signature_algo = $sig_algo;
    }


    /**
     * @return array
     */
    public static function getValidSignatureAlgorithms()
    {
        return static::$sig_algos;
    }


    /**
     * @return array
     */
    public static function getValidSignatureAlgorithmsLabelled()
    {
        return static::$sig_algos_labelled;
    }

    /**
     * @param $method
     *
     * @return bool
     */
    public function setMethod($method)
    {
        $this->method = $method;

        return true;
    }


    /**
     * @param $secret
     *
     * @return $this
     */
    public function setSecret($secret)
    {
        $this->secret = hash('sha256', $secret);

        return $this;
    }


    /**
     * @param $iv
     *
     * @return $this
     */
    public function setIV($iv)
    {
        $this->iv = $iv;

        return $this;
    }


    /**
     * @return null
     */
    public function getIV()
    {
        return $this->iv;
    }


    /**
     * @return string
     */
    public function getIVEncoded()
    {
        return base64_encode($this->iv);
    }

    /**
     * @return $this
     * @throws \Exception
     */
    public function autoSetIV()
    {
        $iv_length = openssl_cipher_iv_length($this->method);

        if(false === $iv_length)
        {
            throw new \Exception(sprintf("Unable to get cipher iv length for the specified cipher: %s", $this->method));
        }

        $iv = openssl_random_pseudo_bytes($iv_length);

        $this->iv = $iv;

        return $this;
    }



    /**
     * @param int $algo
     *
     * @return $this
     */
    public function setSignatureAlgo($algo = OPENSSL_ALGO_SHA1)
    {
        $this->signature_algo = $algo;

        return $this;
    }


    /**
     * @param $key
     *
     * @return $this
     */
    public function setPrivateKey($key, $passphrase = '')
    {
        $this->private_key_str = $key;
        $this->private_key_passphrase = $passphrase;

        return $this;
    }


    /**
     * @param $key
     *
     * @return $this
     */
    public function setPublicKey($key)
    {
        $this->public_key_str = $key;

        return $this;
    }


    /**
     * @return bool|null|resource
     * @throws \Exception
     */
    public function getPrivateKey()
    {
        if(is_null($this->private_key))
        {
            $priv_key = openssl_get_privatekey($this->private_key_str, $this->private_key_passphrase);

            if(false === $priv_key)
            {
                throw new \Exception("Unable to retrieve the private key from given .PEM formatted string. Error reported: " . openssl_error_string());
            }

            $this->private_key = $priv_key;
        }

        return $this->private_key;
    }


    /**
     * @return null|resource
     * @throws \Exception
     */
    public function getPublicKey()
    {
        if(is_null($this->public_key))
        {
            $pub_key = openssl_get_publickey($this->public_key_str);

            if(false === $pub_key)
            {
                throw new \Exception("Unable to retrieve the public key from given certificate. Error reported: " . openssl_error_string());
            }

            $this->public_key = $pub_key;
        }

        return $this->public_key;
    }
}