<?php

declare(strict_types=1);

namespace Acms\Services\Crypto;

use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\Random;

/**
 * AES(CBC) による文字列の暗復号。鍵は SaltRepository が保持するアプリ固定ソルトから導出する。
 */
class Encrypter
{
    /**
     * @var SaltRepository
     */
    private $saltRepository;

    /**
     * @param SaltRepository $saltRepository
     */
    public function __construct(SaltRepository $saltRepository)
    {
        $this->saltRepository = $saltRepository;
    }

    /**
     * 暗号化キーを取得
     *
     * @return string
     */
    private function getEncryptKey(): string
    {
        // 必ず16/24/32バイトに揃える（ここでは32バイトに統一）
        return hash('sha256', $this->saltRepository->getAppSalt(), true);
    }

    /**
     * @return string
     */
    public function getEncryptIv()
    {
        $cipher = new AES('cbc');
        $cipher->setKey($this->getEncryptKey());

        return Random::string(($cipher->getBlockLength() >> 3));
    }

    /**
     * @param string $string
     * @param string $iv
     * @return string
     */
    public function encrypt($string, $iv)
    {
        $cipher = new AES('cbc');
        $cipher->setKey($this->getEncryptKey());
        $cipher->setIV($iv);

        return base64_encode($cipher->encrypt($string));
    }

    /**
     * @param string $cipherText
     * @param string $iv
     * @return string
     * @throws \LengthException IVの長さが不正な場合、または暗号文の長さがブロックサイズの倍数でない場合
     */
    public function decrypt($cipherText, $iv)
    {
        $cipher = new AES('cbc');
        $cipher->setKey($this->getEncryptKey());
        $cipher->setIV($iv);

        return $cipher->decrypt(base64_decode($cipherText)); // @phpstan-ignore-line
    }
}
