<?php
namespace Lib\Mail;


use Lib\UploadFile;
use Model\bodySql;
use Model\folderSql;
use Model\listsSql;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;

/**
 * 函数
 * @time 2022/8/1 16:02
 * Class MailFun
 * @package App\Mail\lib
 */
class MailFun {


    /**
     * json encode
     * @param $data
     * @param int $option
     * @return false|string
     * @time 2022/8/2 15:57
     */
    public static function json_en($data,$option=\JSON_UNESCAPED_UNICODE){
        return \json_encode($data,$option);
    }

    /**
     * @param $str
     * @param string $encode
     * @return string
     * @author:dc
     * @time 2023/3/27 13:55
     */
    public static function mb_coding(string $str,array $appcode=[]){
        $str = trim($str);
        $str = (new Header())->decode($str);
        // try {
        //     $encode = mb_detect_encoding($str, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5'));
        //     if($encode && $encode != 'UTF-8'){
        //         $str = mb_convert_encoding($str, 'UTF-8', $encode);
        //     }
        // }catch (\Throwable $e){}


        return $str;
    }


    /**
     * 验证是否有附件 BODYSTRUCTURE值
     * @param $BODYSTRUCTURE
     * @return int
     * @author:dc
     * @time 2022/11/1 10:57
     */
    public static function isFile($BODYSTRUCTURE):int {
//        foreach ($BODYSTRUCTURE as $item){
//            if($item[0] === 'APPLICATION'){
//                return 1;
//            }
//        }
//        return 0;
        $json = is_array($BODYSTRUCTURE) ? implode(' ',$BODYSTRUCTURE) : $BODYSTRUCTURE;
        return str_contains(mb_strtolower($json), '"attachment"');
    }

    /**
     * 验证body里面是否有附件
     * @param array $body
     * @return bool
     * @author:dc
     * @time 2023/6/25 15:03
     */
    public static function isBodyFile(array $body){
        foreach ($body as $item){
//            if(v.name && v.filename && v.signName && !v['content-id']){
            if(!empty($item['name']) && !empty($item['filename']) && !empty($item['signName']) && empty($item['content-id'])){
                return 1;
            }
        }
        return 0;
    }

    /**
     * 匹配邮箱
     * @param $str
     * @return mixed|string
     * @author:dc
     * @time 2023/11/24 10:04
     */
    public static function pregEmail($str){
        preg_match('/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/',$str,$email);
        if(empty($email[0])){
            // 邮箱2
            preg_match('/[a-zA-Z0-9._%+-{}]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/',$str,$email);
        }
        return str_replace(['<','>'],'',$email[0]??'');
    }


    /**
     * 邮件收件人/发件人
     * @param $str
     * @return array
     * @author:dc
     * @time 2022/11/8 9:36
     */
    public static function toOrFrom($str){

        $strs = explode(',',$str);
        foreach ($strs as $k=>$s){
            $email = self::pregEmail($s);

            if(empty($email)){
                $s = [
                    'email' =>  '',
                    'name'  =>  $s
                ];
            }else{
                $s = str_replace([$email,'"','<','>','&gt;','&lt;'],'',$s);
                $s = trim($s);
                $s = [
                    'email' =>  self::mb_coding($email),
                    'name'  =>  $s
                ];
            }
            if(empty($s['name'])){
                $s['name'] = explode('@',$s['email'])[0]??'';
            }

            if(!empty($s['email'])){
                $strs[$k] = $s;
            }else{
                unset($strs[$k]);
            }

        }

        return array_map(function ($v){
            if(!empty($v['name'])){
                $v['name'] = MailFun::mb_coding($v['name']);
            }
            return $v;
        },array_values($strs));


    }


    /**
     * 发送邮件
     * @param array $data 数据
     * @param array $email 邮箱信息
     * @return array
     * @throws \Lib\Err
     * @throws \PHPMailer\PHPMailer\Exception
     * @author:dc
     * @time 2023/4/11 9:12
     */
    public static function sendEmail(array $data, array $email){
        $smtp = self::getHostPort($email['smtp'],465);
        // 邮件对象
        $mail = new PHPMailer();
        //Server settings
        $mail->SMTPDebug = SMTP::DEBUG_OFF;//调试输出  SMTP::DEBUG_SERVER;                    //Enable verbose debug output
        $mail->isSMTP();                                            //Send using SMTP
        $mail->Host       = $smtp['host'];                     //Set the SMTP server to send through
        $mail->SMTPAuth   = true;                                   //Enable SMTP authentication
        $mail->Username   = $email['email'];                     //SMTP username
        $mail->Password   = base64_decode($email['password']);                               //SMTP password
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;            //Enable implicit TLS encryption
        $mail->Port       = $smtp['port'];                                    //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
        $mail->CharSet = 'utf-8';
        $mail->Encoding = PHPMailer::ENCODING_QUOTED_PRINTABLE;
        // 设置代理
        if(!empty($email['proxy'])){
            $mail->SMTPOptions['http']= [
                'proxy' => $email['proxy'], // 代理地址
                'request_fulluri' => true,
            ];
        }

        // 是否验证证书
        $mail->SMTPOptions = [
            'ssl' => [
                'verify_peer' => false, // 有的证书和域名不匹配,这里关闭认证
                'verify_peer_name' => false,// 有的证书和域名不匹配,这里关闭认证
            ]
        ];

        // 添加自定义头信息
        if(!empty($data['mail-header']) && is_array($data['mail-header'])){
            foreach ($data['mail-header'] as $k=>$v){
                // 在邮件体中添加自定义标识 预热
                $mail->addCustomHeader($k,$v);
            }
        }


        //Recipients,设置发件人
        $mail->setFrom($email['email'], $data['nickname']??'');// 显示邮件来自谁
        // //设置收件人
        foreach ($data['tos'] as $to){
            $mail->addAddress($to['email'], $to['name']??'');
        }

        //回复到那个邮件
        if(!empty($data['reply_to'])){
            if(is_string($data['reply_to'])){
                $mail->addReplyTo($data['reply_to']);
            }else{
                $mail->addReplyTo($data['reply_to']['email'], $data['reply_to']['name']);     //Add a recipient
            }
        }

        // 抄送
        if(!empty($data['cc'])){
            foreach ($data['cc'] as $to){
                $mail->addCC($to['email'], $to['name']??'');
            }
        }
        // 密送
        if(!empty($data['bcc'])){
            foreach ($data['bcc'] as $to){
                $mail->addBCC($to['email'], $to['name']??'');
            }
        }

        //Attachments 附件
        if(!empty($data['attachment'])){
            foreach ($data['attachment'] as $file){
                $mail->addAttachment($file['path'], $file['filename']);
            }
        }

        // 回执,阅读后收回执的邮箱
        if(!empty($data['receipt'])){
            $mail->ConfirmReadingTo = $email['email'];
        }

        // 是否紧急邮件
//      Options: null (default), 1 = High, 3 = Normal, 5 = low.
        $mail->Priority = $data['priority']??3;

        //Content 主题,标题
        $mail->Subject = $data['subject'];

        $mail->isHTML(true);                                  //Set email format to HTML

        // todo:: 订阅功能只可用作单发,不可群发。否则无法验证订阅人
        if(count($mail->getToAddresses()) == 1){
            $data['body'] = str_replace(
                ['{#subscribe_uuid#}','%7B%23subscribe_uuid%23%7D'], // 订阅用的key 获取用户信息所用
                self::enSubscribeUrl($mail->Username,$mail->getToAddresses()[0][0]??'',$mail->Subject),
                $data['body']
            );
        }
        // 随机字符串使用 这个用来追踪邮件,请不要删除
        $randStr = $email['id'].'_'.substr(time(),-3).rand(100,99999).'_'.base64_en_md5($mail->getToAddresses()[0][0]??'');
        $data['body'] = str_replace(
            '{mail#randStr24#}',
            $randStr,
            $data['body']
        );
        $mail->addCustomHeader('AICC-RANDSTR-UUID',$randStr);

        $mail->Body    = $data['body'];// html格式的内容

        // 发送
        if($mail->send()){
            return [true,$mail->getLastMessageID()];
        }

        return [false,$mail->ErrorInfo];

    }

    /**
     * 文件夹名 编译
     * @param $folder
     * @return string
     * @author:dc
     * @time 2023/3/14 9:37
     */
    public static function folderEncoding($folder):string {
        return mb_convert_encoding($folder,'UTF7-IMAP','UTF-8');
    }

    /**
     * 是否是base64
     * @param $str
     * @return bool
     * @author:dc
     * @time 2023/4/3 10:24
     */
    public static function isBase64($str){
        $oldStr = str_replace(["\n","\r"],'',rtrim($str,'='));
        return $oldStr == rtrim(base64_encode(base64_decode($str)),'=');
    }


    /**
     * 测试登录
     * @param $smtp
     * @param $email
     * @param $password
     * @return bool
     * @author:dc
     * @time 2023/11/7 10:47
     */
    public static function smtpLoginTest($smtp,$email,$password){
        $smtp = self::getHostPort($smtp,465);
        // 邮件对象
        $mail = new PHPMailer();
        //Server settings
        $mail->SMTPDebug = SMTP::DEBUG_OFF;//调试输出  SMTP::DEBUG_SERVER;                    //Enable verbose debug output
        $mail->isSMTP();                                            //Send using SMTP
        $mail->Host       = $smtp['host'];                     //Set the SMTP server to send through
        $mail->SMTPAuth   = true;                                   //Enable SMTP authentication
        $mail->Username   = $email;                     //SMTP username
        $mail->Password   = $password;                               //SMTP password
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;            //Enable implicit TLS encryption
        $mail->Port       = $smtp['port'];                                    //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
        $mail->CharSet = 'utf-8';
        $mail->Encoding = PHPMailer::ENCODING_QUOTED_PRINTABLE;
        // 是否验证证书
//        if(in_array(parse_url($smtp['host'])['host']??'1-1',['mail.laser-tech.com.cn'])){
            $mail->SMTPOptions = [
                'ssl' => [
                    'verify_peer' => false, // 有的证书和域名不匹配,这里关闭认证
                    'verify_peer_name' => false,// 有的证书和域名不匹配,这里关闭认证
                ]
            ];
//        }


        try {
            return $mail->smtpConnect();
        }catch (\Throwable $e){}

        return false;

    }


    /**
     * @param $host
     * @param int $port
     * @return array
     * @author:dc
     * @time 2024/3/6 9:21
     */
    public static function getHostPort($host,$port=0,$ssl = 'ssl://'){
        $host = ltrim($host,'/');
        $arr = explode(":",$host);
        if(str_contains($host, '//')){
            return [
                'host'  =>  $arr[0].':'.$arr[1],
                'port'  =>  empty($arr[2]) ? $port : $arr[2],
            ];
        }else{

            return [
                'host'  =>  $ssl.$arr[0],
                'port'  =>  empty($arr[1]) ? $port : $arr[1],
            ];
        }

    }






    /**
     * 生成订阅按钮
     * @param string $from_email 发件人
     * @param string $to_email 收件人
     * @return string
     * @author:dc
     * @time 2024/8/7 10:56
     */
    public static function enSubscribeUrl(string $from_email,string $to_email,string $subject=''){
        $params = [
            'from'  =>  $from_email,
            'to'  =>  $to_email,
            'subject'  =>  $subject,
        ];

        $params = json_encode($params);

        return md5($from_email.'-'.$to_email.'-aicc')."+".trim(base64_encode($params),'=');
    }

    /**
     * 解析url中的数据
     * @param string $url
     * @author:dc
     * @time 2024/8/7 11:49
     */
    public static function deSubscribeUrl(string $url){
        $url = str_replace(' ','+',$url);
        list($sign,$params) =   explode('+',$url);

        if(!$sign || strlen($sign)!=32 || !$params){
            return  [];
        }

        $params = base64_decode($params.'==');
        $params = @json_decode($params,true);

        if(!$params){
            return  [];
        }

        if($sign != md5($params['from'].'-'.$params['to'].'-aicc')){
            return  [];
        }

        return $params;
    }



}




/**
 * Class Header
 *
 * @package Webklex\PHPIMAP
 */
class Header
{


    /**
     * Fallback Encoding
     *
     * @var string
     */
    public $fallback_encoding = 'UTF-8';


    /**
     * Decode MIME header elements
     * @link https://php.net/manual/en/function.imap-mime-header-decode.php
     * @param string $text The MIME text
     *
     * @return array The decoded elements are returned in an array of objects, where each
     * object has two properties, charset and text.
     */
    public function mime_header_decode(string $text): array
    {
        if (extension_loaded('imap')) {
            $result = \imap_mime_header_decode($text);
            return is_array($result) ? $result : [];
        }
        $charset = $this->getEncoding($text);
        return [(object)[
            "charset" => $charset,
            "text" => $this->convertEncoding($text, $charset)
        ]];
    }

    /**
     * Check if a given pair of strings has been decoded
     * @param $encoded
     * @param $decoded
     *
     * @return bool
     */
    private function notDecoded($encoded, $decoded): bool
    {
        return 0 === strpos($decoded, '=?')
            && strlen($decoded) - 2 === strpos($decoded, '?=')
            && false !== strpos($encoded, $decoded);
    }

    /**
     * Convert the encoding
     * @param $str
     * @param string $from
     * @param string $to
     *
     * @return mixed|string
     */
    public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8")
    {

        $from = EncodingAliases::get($from, $this->fallback_encoding);
        $to = EncodingAliases::get($to, $this->fallback_encoding);

        if ($from === $to) {
            return $str;
        }

        // We don't need to do convertEncoding() if charset is ASCII (us-ascii):
        //     ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded
        //     https://stackoverflow.com/a/11303410
        //
        // us-ascii is the same as ASCII:
        //     ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA)
        //     prefers the updated name US-ASCII, which clarifies that this system was developed in the US and
        //     based on the typographical symbols predominantly in use there.
        //     https://en.wikipedia.org/wiki/ASCII
        //
        // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken.
        if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') {
            return $str;
        }

        try {
            if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
                return iconv($from, $to, $str);
            } else {
                if (!$from) {
                    return mb_convert_encoding($str, $to);
                }
                return mb_convert_encoding($str, $to, $from);
            }
        } catch (\Exception $e) {
            if (strstr($from, '-')) {
                $from = str_replace('-', '', $from);
                return $this->convertEncoding($str, $from, $to);
            } else {
                return $str;
            }
        }
    }

    /**
     * Get the encoding of a given abject
     * @param object|string $structure
     *
     * @return string
     */
    public function getEncoding($structure): string
    {
        if (property_exists($structure, 'parameters')) {
            foreach ($structure->parameters as $parameter) {
                if (strtolower($parameter->attribute) == "charset") {
                    return EncodingAliases::get($parameter->value, $this->fallback_encoding);
                }
            }
        } elseif (property_exists($structure, 'charset')) {
            return EncodingAliases::get($structure->charset, $this->fallback_encoding);
        } elseif (is_string($structure) === true) {
            $result = mb_detect_encoding($structure);
            return $result === false ? $this->fallback_encoding : $result;
        }

        return $this->fallback_encoding;
    }

    /**
     * Test if a given value is utf-8 encoded
     * @param $value
     *
     * @return bool
     */
    private function is_uft8($value): bool
    {
        return strpos(strtolower($value), '=?utf-8?') === 0;
    }

    /**
     * Try to decode a specific header
     * @param mixed $value
     *
     * @return mixed
     */
    public function decode($value)
    {
        if (is_array($value)) {
            return $this->decodeArray($value);
        }
        $original_value = $value;
        $decoder = 'utf-8';

        if ($value !== null) {
            $is_utf8_base = $this->is_uft8($value);

            if ($decoder === 'utf-8' && extension_loaded('imap')) {
                $value = \imap_utf8($value);
                $is_utf8_base = $this->is_uft8($value);
                if ($is_utf8_base) {
                    $value = mb_decode_mimeheader($value);
                }
                if ($this->notDecoded($original_value, $value)) {
                    $decoded_value = $this->mime_header_decode($value);
                    if (count($decoded_value) > 0) {
                        if (property_exists($decoded_value[0], "text")) {
                            $value = $decoded_value[0]->text;
                        }
                    }
                }
            } elseif ($decoder === 'iconv' && $is_utf8_base) {
                $value = iconv_mime_decode($value);
            } elseif ($is_utf8_base) {
                $value = mb_decode_mimeheader($value);
            }

            if ($this->is_uft8($value)) {
                $value = mb_decode_mimeheader($value);
            }

            if ($this->notDecoded($original_value, $value)) {
                $value = $this->convertEncoding($original_value, $this->getEncoding($original_value));
            }
        }

        return $value;
    }

    /**
     * Decode a given array
     * @param array $values
     *
     * @return array
     */
    private function decodeArray(array $values): array
    {
        foreach ($values as $key => $value) {
            $values[$key] = $this->decode($value);
        }
        return $values;
    }



    /**
     * Exception handling for invalid dates
     *
     * Currently known invalid formats:
     * ^ Datetime                                   ^ Problem                           ^ Cause
     * | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification     | A Windows feature
     * | Thu, 8 Nov 2018 08:54:58 -0200 (-02)       |
     * |                                            | and invalid timezone (max 6 char) |
     * | 04 Jan 2018 10:12:47 UT                    | Missing letter "C"                | Unknown
     * | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
     * |                                            | mail server                       |
     * | Sat, 31 Aug 2013 20:08:23 +0580            | Invalid timezone                  | PHPMailer bug https://sourceforge.net/p/phpmailer/mailman/message/6132703/
     *
     * Please report any new invalid timestamps to [#45](https://github.com/Webklex/php-imap/issues)
     *
     * @param object $header
     *
     * @throws InvalidMessageDateException
     */
    private function parseDate($header)
    {

        if (property_exists($header, 'date')) {
            $date = $header->date;

            if (preg_match('/\+0580/', $date)) {
                $date = str_replace('+0580', '+0530', $date);
            }

            $date = trim(rtrim($date));
            try {
                if (strpos($date, '&nbsp;') !== false) {
                    $date = str_replace('&nbsp;', ' ', $date);
                }
                $parsed_date = Carbon::parse($date);
            } catch (\Exception $e) {
                switch (true) {
                    case preg_match('/([0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}\-[0-9]{1,2}\.[0-9]{1,2}.[0-9]{1,2})+$/i', $date) > 0:
                        $date = Carbon::createFromFormat("Y.m.d-H.i.s", $date);
                        break;
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
                        $date .= 'C';
                        break;
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}[\,]\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4})+$/i', $date) > 0:
                        $date = str_replace(',', '', $date);
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ \+[0-9]{2,4}\ \(\+[0-9]{1,2}\))+$/i', $date) > 0:
                    case preg_match('/([A-Z]{2,3}[\,|\ \,]\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}.*)+$/i', $date) > 0:
                    case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
                    case preg_match('/([A-Z]{2,3}\, \ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
                    case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
                        $array = explode('(', $date);
                        $array = array_reverse($array);
                        $date = trim(array_pop($array));
                        break;
                }

                $parsed_date = Carbon::parse($date);

            }


        }
    }



}

class EncodingAliases {

    /**
     * Contains email encoding mappings
     *
     * @var array
     */
    private static $aliases = [
        /*
        |--------------------------------------------------------------------------
        | Email encoding aliases
        |--------------------------------------------------------------------------
        |
        | Email encoding aliases used to convert to iconv supported charsets
        |
        |
        | This Source Code Form is subject to the terms of the Mozilla Public
        | License, v. 2.0. If a copy of the MPL was not distributed with this
        | file, You can obtain one at http://mozilla.org/MPL/2.0/.
        |
        | This Original Code has been modified by IBM Corporation.
        | Modifications made by IBM described herein are
        | Copyright (c) International Business Machines
        | Corporation, 1999
        |
        | Modifications to Mozilla code or documentation
        | identified per MPL Section 3.3
        |
        | Date         Modified by     Description of modification
        | 12/09/1999   IBM Corp.       Support for IBM codepages - 850,852,855,857,862,864
        |
        | Rule of this file:
        | 1. key should always be in lower case ascii so we can do case insensitive
        |    comparison in the code faster.
        | 2. value should be the one used in unicode converter
        |
        | 3. If the charset is not used for document charset, but font charset
        |    (e.g. XLFD charset- such as JIS x0201, JIS x0208), don't put here
        |
        */
        "ascii"                    => "us-ascii",
        "us-ascii"                 => "us-ascii",
        "ansi_x3.4-1968"           => "us-ascii",
        "646"                      => "us-ascii",
        "iso-8859-1"               => "ISO-8859-1",
        "iso-8859-2"               => "ISO-8859-2",
        "iso-8859-3"               => "ISO-8859-3",
        "iso-8859-4"               => "ISO-8859-4",
        "iso-8859-5"               => "ISO-8859-5",
        "iso-8859-6"               => "ISO-8859-6",
        "iso-8859-6-i"             => "ISO-8859-6-I",
        "iso-8859-6-e"             => "ISO-8859-6-E",
        "iso-8859-7"               => "ISO-8859-7",
        "iso-8859-8"               => "ISO-8859-8",
        "iso-8859-8-i"             => "ISO-8859-8-I",
        "iso-8859-8-e"             => "ISO-8859-8-E",
        "iso-8859-9"               => "ISO-8859-9",
        "iso-8859-10"              => "ISO-8859-10",
        "iso-8859-11"              => "ISO-8859-11",
        "iso-8859-13"              => "ISO-8859-13",
        "iso-8859-14"              => "ISO-8859-14",
        "iso-8859-15"              => "ISO-8859-15",
        "iso-8859-16"              => "ISO-8859-16",
        "iso-ir-111"               => "ISO-IR-111",
        "iso-2022-cn"              => "ISO-2022-CN",
        "iso-2022-cn-ext"          => "ISO-2022-CN",
        "iso-2022-kr"              => "ISO-2022-KR",
        "iso-2022-jp"              => "ISO-2022-JP",
        "utf-16be"                 => "UTF-16BE",
        "utf-16le"                 => "UTF-16LE",
        "utf-16"                   => "UTF-16",
        "windows-1250"             => "windows-1250",
        "windows-1251"             => "windows-1251",
        "windows-1252"             => "windows-1252",
        "windows-1253"             => "windows-1253",
        "windows-1254"             => "windows-1254",
        "windows-1255"             => "windows-1255",
        "windows-1256"             => "windows-1256",
        "windows-1257"             => "windows-1257",
        "windows-1258"             => "windows-1258",
        "ibm866"                   => "IBM866",
        "ibm850"                   => "IBM850",
        "ibm852"                   => "IBM852",
        "ibm855"                   => "IBM855",
        "ibm857"                   => "IBM857",
        "ibm862"                   => "IBM862",
        "ibm864"                   => "IBM864",
        "utf-8"                    => "UTF-8",
        "utf-7"                    => "UTF-7",
        "shift_jis"                => "Shift_JIS",
        "big5"                     => "Big5",
        "euc-jp"                   => "EUC-JP",
        "euc-kr"                   => "EUC-KR",
        "gb2312"                   => "GB2312",
        "gb18030"                  => "gb18030",
        "viscii"                   => "VISCII",
        "koi8-r"                   => "KOI8-R",
        "koi8_r"                   => "KOI8-R",
        "cskoi8r"                  => "KOI8-R",
        "koi"                      => "KOI8-R",
        "koi8"                     => "KOI8-R",
        "koi8-u"                   => "KOI8-U",
        "tis-620"                  => "TIS-620",
        "t.61-8bit"                => "T.61-8bit",
        "hz-gb-2312"               => "HZ-GB-2312",
        "big5-hkscs"               => "Big5-HKSCS",
        "gbk"                      => "gbk",
        "cns11643"                 => "x-euc-tw",
        //
        // Aliases for ISO-8859-1
        //
        "latin1"                   => "ISO-8859-1",
        "iso_8859-1"               => "ISO-8859-1",
        "iso8859-1"                => "ISO-8859-1",
        "iso8859-2"                => "ISO-8859-2",
        "iso8859-3"                => "ISO-8859-3",
        "iso8859-4"                => "ISO-8859-4",
        "iso8859-5"                => "ISO-8859-5",
        "iso8859-6"                => "ISO-8859-6",
        "iso8859-7"                => "ISO-8859-7",
        "iso8859-8"                => "ISO-8859-8",
        "iso8859-9"                => "ISO-8859-9",
        "iso8859-10"               => "ISO-8859-10",
        "iso8859-11"               => "ISO-8859-11",
        "iso8859-13"               => "ISO-8859-13",
        "iso8859-14"               => "ISO-8859-14",
        "iso8859-15"               => "ISO-8859-15",
        "iso_8859-1:1987"          => "ISO-8859-1",
        "iso-ir-100"               => "ISO-8859-1",
        "l1"                       => "ISO-8859-1",
        "ibm819"                   => "ISO-8859-1",
        "cp819"                    => "ISO-8859-1",
        "csisolatin1"              => "ISO-8859-1",
        //
        // Aliases for ISO-8859-2
        //
        "latin2"                   => "ISO-8859-2",
        "iso_8859-2"               => "ISO-8859-2",
        "iso_8859-2:1987"          => "ISO-8859-2",
        "iso-ir-101"               => "ISO-8859-2",
        "l2"                       => "ISO-8859-2",
        "csisolatin2"              => "ISO-8859-2",
        //
        // Aliases for ISO-8859-3
        //
        "latin3"                   => "ISO-8859-3",
        "iso_8859-3"               => "ISO-8859-3",
        "iso_8859-3:1988"          => "ISO-8859-3",
        "iso-ir-109"               => "ISO-8859-3",
        "l3"                       => "ISO-8859-3",
        "csisolatin3"              => "ISO-8859-3",
        //
        // Aliases for ISO-8859-4
        //
        "latin4"                   => "ISO-8859-4",
        "iso_8859-4"               => "ISO-8859-4",
        "iso_8859-4:1988"          => "ISO-8859-4",
        "iso-ir-110"               => "ISO-8859-4",
        "l4"                       => "ISO-8859-4",
        "csisolatin4"              => "ISO-8859-4",
        //
        // Aliases for ISO-8859-5
        //
        "cyrillic"                 => "ISO-8859-5",
        "iso_8859-5"               => "ISO-8859-5",
        "iso_8859-5:1988"          => "ISO-8859-5",
        "iso-ir-144"               => "ISO-8859-5",
        "csisolatincyrillic"       => "ISO-8859-5",
        //
        // Aliases for ISO-8859-6
        //
        "arabic"                   => "ISO-8859-6",
        "iso_8859-6"               => "ISO-8859-6",
        "iso_8859-6:1987"          => "ISO-8859-6",
        "iso-ir-127"               => "ISO-8859-6",
        "ecma-114"                 => "ISO-8859-6",
        "asmo-708"                 => "ISO-8859-6",
        "csisolatinarabic"         => "ISO-8859-6",
        //
        // Aliases for ISO-8859-6-I
        //
        "csiso88596i"              => "ISO-8859-6-I",
        //
        // Aliases for ISO-8859-6-E",
        //
        "csiso88596e"              => "ISO-8859-6-E",
        //
        // Aliases for ISO-8859-7",
        //
        "greek"                    => "ISO-8859-7",
        "greek8"                   => "ISO-8859-7",
        "sun_eu_greek"             => "ISO-8859-7",
        "iso_8859-7"               => "ISO-8859-7",
        "iso_8859-7:1987"          => "ISO-8859-7",
        "iso-ir-126"               => "ISO-8859-7",
        "elot_928"                 => "ISO-8859-7",
        "ecma-118"                 => "ISO-8859-7",
        "csisolatingreek"          => "ISO-8859-7",
        //
        // Aliases for ISO-8859-8",
        //
        "hebrew"                   => "ISO-8859-8",
        "iso_8859-8"               => "ISO-8859-8",
        "visual"                   => "ISO-8859-8",
        "iso_8859-8:1988"          => "ISO-8859-8",
        "iso-ir-138"               => "ISO-8859-8",
        "csisolatinhebrew"         => "ISO-8859-8",
        //
        // Aliases for ISO-8859-8-I",
        //
        "csiso88598i"              => "ISO-8859-8-I",
        "iso-8859-8i"              => "ISO-8859-8-I",
        "logical"                  => "ISO-8859-8-I",
        //
        // Aliases for ISO-8859-8-E",
        //
        "csiso88598e"              => "ISO-8859-8-E",
        //
        // Aliases for ISO-8859-9",
        //
        "latin5"                   => "ISO-8859-9",
        "iso_8859-9"               => "ISO-8859-9",
        "iso_8859-9:1989"          => "ISO-8859-9",
        "iso-ir-148"               => "ISO-8859-9",
        "l5"                       => "ISO-8859-9",
        "csisolatin5"              => "ISO-8859-9",
        //
        // Aliases for UTF-8",
        //
        "unicode-1-1-utf-8"        => "UTF-8",
        // nl_langinfo(CODESET) in HP/UX returns 'utf8' under UTF-8 locales",
        "utf8"                     => "UTF-8",
        //
        // Aliases for Shift_JIS",
        //
        "x-sjis"                   => "Shift_JIS",
        "shift-jis"                => "Shift_JIS",
        "ms_kanji"                 => "Shift_JIS",
        "csshiftjis"               => "Shift_JIS",
        "windows-31j"              => "Shift_JIS",
        "cp932"                    => "Shift_JIS",
        "sjis"                     => "Shift_JIS",
        //
        // Aliases for EUC_JP",
        //
        "cseucpkdfmtjapanese"      => "EUC-JP",
        "x-euc-jp"                 => "EUC-JP",
        //
        // Aliases for ISO-2022-JP",
        //
        "csiso2022jp"              => "ISO-2022-JP",
        // The following are really not aliases ISO-2022-JP, but sharing the same decoder",
        "iso-2022-jp-2"            => "ISO-2022-JP",
        "csiso2022jp2"             => "ISO-2022-JP",
        //
        // Aliases for Big5",
        //
        "csbig5"                   => "Big5",
        "cn-big5"                  => "Big5",
        // x-x-big5 is not really a alias for Big5, add it only for MS FrontPage",
        "x-x-big5"                 => "Big5",
        // Sun Solaris",
        "zh_tw-big5"               => "Big5",
        //
        // Aliases for EUC-KR",
        //
        "cseuckr"                  => "EUC-KR",
        "ks_c_5601-1987"           => "EUC-KR",
        "iso-ir-149"               => "EUC-KR",
        "ks_c_5601-1989"           => "EUC-KR",
        "ksc_5601"                 => "EUC-KR",
        "ksc5601"                  => "EUC-KR",
        "korean"                   => "EUC-KR",
        "csksc56011987"            => "EUC-KR",
        "5601"                     => "EUC-KR",
        "windows-949"              => "EUC-KR",
        //
        // Aliases for GB2312",
        //
        // The following are really not aliases GB2312, add them only for MS FrontPage",
        "gb_2312-80"               => "GB2312",
        "iso-ir-58"                => "GB2312",
        "chinese"                  => "GB2312",
        "csiso58gb231280"          => "GB2312",
        "csgb2312"                 => "GB2312",
        "zh_cn.euc"                => "GB2312",
        // Sun Solaris",
        "gb_2312"                  => "GB2312",
        //
        // Aliases for windows-125x ",
        //
        "x-cp1250"                 => "windows-1250",
        "x-cp1251"                 => "windows-1251",
        "x-cp1252"                 => "windows-1252",
        "x-cp1253"                 => "windows-1253",
        "x-cp1254"                 => "windows-1254",
        "x-cp1255"                 => "windows-1255",
        "x-cp1256"                 => "windows-1256",
        "x-cp1257"                 => "windows-1257",
        "x-cp1258"                 => "windows-1258",
        //
        // Aliases for windows-874 ",
        //
        "windows-874"              => "windows-874",
        "ibm874"                   => "windows-874",
        "dos-874"                  => "windows-874",
        //
        // Aliases for macintosh",
        //
        "macintosh"                => "macintosh",
        "x-mac-roman"              => "macintosh",
        "mac"                      => "macintosh",
        "csmacintosh"              => "macintosh",
        //
        // Aliases for IBM866",
        //
        "cp866"                    => "IBM866",
        "cp-866"                   => "IBM866",
        "866"                      => "IBM866",
        "csibm866"                 => "IBM866",
        //
        // Aliases for IBM850",
        //
        "cp850"                    => "IBM850",
        "850"                      => "IBM850",
        "csibm850"                 => "IBM850",
        //
        // Aliases for IBM852",
        //
        "cp852"                    => "IBM852",
        "852"                      => "IBM852",
        "csibm852"                 => "IBM852",
        //
        // Aliases for IBM855",
        //
        "cp855"                    => "IBM855",
        "855"                      => "IBM855",
        "csibm855"                 => "IBM855",
        //
        // Aliases for IBM857",
        //
        "cp857"                    => "IBM857",
        "857"                      => "IBM857",
        "csibm857"                 => "IBM857",
        //
        // Aliases for IBM862",
        //
        "cp862"                    => "IBM862",
        "862"                      => "IBM862",
        "csibm862"                 => "IBM862",
        //
        // Aliases for IBM864",
        //
        "cp864"                    => "IBM864",
        "864"                      => "IBM864",
        "csibm864"                 => "IBM864",
        "ibm-864"                  => "IBM864",
        //
        // Aliases for T.61-8bit",
        //
        "t.61"                     => "T.61-8bit",
        "iso-ir-103"               => "T.61-8bit",
        "csiso103t618bit"          => "T.61-8bit",
        //
        // Aliases for UTF-7",
        //
        "x-unicode-2-0-utf-7"      => "UTF-7",
        "unicode-2-0-utf-7"        => "UTF-7",
        "unicode-1-1-utf-7"        => "UTF-7",
        "csunicode11utf7"          => "UTF-7",
        //
        // Aliases for ISO-10646-UCS-2",
        //
        "csunicode"                => "UTF-16BE",
        "csunicode11"              => "UTF-16BE",
        "iso-10646-ucs-basic"      => "UTF-16BE",
        "csunicodeascii"           => "UTF-16BE",
        "iso-10646-unicode-latin1" => "UTF-16BE",
        "csunicodelatin1"          => "UTF-16BE",
        "iso-10646"                => "UTF-16BE",
        "iso-10646-j-1"            => "UTF-16BE",
        //
        // Aliases for ISO-8859-10",
        //
        "latin6"                   => "ISO-8859-10",
        "iso-ir-157"               => "ISO-8859-10",
        "l6"                       => "ISO-8859-10",
        // Currently .properties cannot handle : in key",
        //iso_8859-10:1992" => "ISO-8859-10",
        "csisolatin6"              => "ISO-8859-10",
        //
        // Aliases for ISO-8859-15",
        //
        "iso_8859-15"              => "ISO-8859-15",
        "csisolatin9"              => "ISO-8859-15",
        "l9"                       => "ISO-8859-15",
        //
        // Aliases for ISO-IR-111",
        //
        "ecma-cyrillic"            => "ISO-IR-111",
        "csiso111ecmacyrillic"     => "ISO-IR-111",
        //
        // Aliases for ISO-2022-KR",
        //
        "csiso2022kr"              => "ISO-2022-KR",
        //
        // Aliases for VISCII",
        //
        "csviscii"                 => "VISCII",
        //
        // Aliases for x-euc-tw",
        //
        "zh_tw-euc"                => "x-euc-tw",
        //
        // Following names appears in unix nl_langinfo(CODESET)",
        // They can be compiled as platform specific if necessary",
        // DONT put things here if it does not look generic enough (like hp15CN)",
        //
        "iso88591"                 => "ISO-8859-1",
        "iso88592"                 => "ISO-8859-2",
        "iso88593"                 => "ISO-8859-3",
        "iso88594"                 => "ISO-8859-4",
        "iso88595"                 => "ISO-8859-5",
        "iso88596"                 => "ISO-8859-6",
        "iso88597"                 => "ISO-8859-7",
        "iso88598"                 => "ISO-8859-8",
        "iso88599"                 => "ISO-8859-9",
        "iso885910"                => "ISO-8859-10",
        "iso885911"                => "ISO-8859-11",
        "iso885912"                => "ISO-8859-12",
        "iso885913"                => "ISO-8859-13",
        "iso885914"                => "ISO-8859-14",
        "iso885915"                => "ISO-8859-15",
        "cp1250"                   => "windows-1250",
        "cp1251"                   => "windows-1251",
        "cp1252"                   => "windows-1252",
        "cp1253"                   => "windows-1253",
        "cp1254"                   => "windows-1254",
        "cp1255"                   => "windows-1255",
        "cp1256"                   => "windows-1256",
        "cp1257"                   => "windows-1257",
        "cp1258"                   => "windows-1258",
        "x-gbk"                    => "gbk",
        "windows-936"              => "gbk",
        "ansi-1251"                => "windows-1251",
    ];

    /**
     * Returns proper encoding mapping, if exsists. If it doesn't, return unchanged $encoding
     * @param string|null $encoding
     * @param string|null $fallback
     *
     * @return string
     */
    public static function get($encoding, string $fallback = null): string {
        if (isset(self::$aliases[strtolower($encoding ?? '')])) {
            return self::$aliases[strtolower($encoding ?? '')];
        }
        return $fallback !== null ? $fallback : $encoding;
    }

}