<?php

namespace Lib\Mail;

use Lib\Mail\MailFun;

/**
 * 解析邮件body内容
 * 通过 fetch msgno RFC822.text 获取到的内容
 * 此内容包含html 文本 附件
 * @author:dc
 * @time 2022/8/12 9:15
 * Class Body
 * @package App\Mail\lib
 */
class Body {

    /**
     * @var string
     */
    private $body;

    /**
     *
     * @var array
     */
    private $item = [];

    /**
     * 保存的目录
     * @var string
     */
    private $fileSavePath;


    /**
     * Body constructor.
     * @param string $body
     * @param string $fileSavePath
     */
    public function __construct(string $body, string $fileSavePath='/')
    {
        $this->body = $body = trim($body);

        $this->fileSavePath = $fileSavePath;

        // 这个是描述特殊文本
        if(strpos($body,'This is a multi-part message in MIME format.')===0){
            $body = trim($body,'This is a multi-part message in MIME format.');
            $body = trim($body);
        }

        // 163 有
        if(strpos($body,'------=_Part')!==false){
            $this->parse($body,'------=_Part');
        }
        elseif (mb_strpos($body,'------=_NextPart')!==false){
            $this->parse($body,'------=_NextPart');
        }
        elseif (mb_strpos($body,'----_NmP')!==false){
            $this->parse($body,'----_NmP');
        }
        elseif (mb_strpos($body,'--_=_swift')!==false){
            $this->parse($body,'--_=_swift');
        }
        elseif (mb_strpos($body,'----==_mimepart')!==false){
            $this->parse($body,'----==_mimepart');
        }
        elseif (mb_strpos($body,'--------------Boundary')!==false){
            $this->parse($body,'--------------Boundary');
        }
        elseif (mb_strpos($body,'--=-')!==false){
            $this->parse($body,'--=-');
        }
        // 很多--开始的,且不规则
        elseif(strpos($body,'--')===0){
            // 获取第一行
            $tag = $this->body_get_tag($body,'--');
            // 以第一行为标准
            $this->parse($body,trim($tag));
        }
        // 直接html
        elseif (mb_strpos($body,'<')===0){
            $body = quoted_printable_decode($body);
//            preg_match("/<meta(?!\s*(?:name|value)\s*=)(?:[^>]*?content\s*=[\s\"']*)?([^>]*?)[\s\"';]*charset\s*=[\s\"']*([^\s\"'\/>]*)/",$body,$icon);
//            if(!empty($icon[2])){
//                // 解码
//                $body = mb_convert_encoding($body,'utf-8',$icon[2]);
//            }
            $this->setItem(['type'=>'text/html','body'=>$body]);
        }
        else{
            // qq的是base64
            if(rtrim($body,'=') == rtrim(base64_encode(base64_decode($body)),'=')){
                $this->setItem(['type'=>'text/plain','body'=>base64_decode($body)]);
            }else{
                $this->setItem(['type'=>'text/plain','body'=>$body]);
            }

        }


    }

    /**
     * 获取标签
     * @param $body
     * @param $tag
     * @return mixed|string
     * @author:dc
     * @time 2022/8/12 10:49
     */
    private function body_get_tag($body,$tag){
        preg_match("/{$tag}[\w\W].*/i",$body,$result);
        if(!empty($result[0])) {
            return $result[0];
        }
        return '';
    }

    /**
     * @param $item
     */
    private function setItem($item): void
    {
        $this->item[] = $item;
    }


    /**
     * @return []
     */
    public function getItem(): array
    {
        return $this->item;
    }


    /**
     * 开始解析
     * @param string $body
     * @param string $tag
     * @return array
     * @author:dc
     * @time 2022/8/12 9:50
     */
    private function parse(string $body, string $tag){

        // 删除第一个标签前面的数据,一般情况无用
        $body = mb_substr($this->body,strpos($this->body,$tag),99999999999);

        // 有附件的情况
        preg_match('/boundary="([-_A-Za-z0-9=\.]{1,})"/i',$body,$boundary);
        if($boundary[0]??''){
            $body = str_replace($boundary[0],'',$body);
//            $body = mb_substr($body,mb_strpos($body,$boundary[0])+strlen($boundary[0]),99999999999);
        }
        // 附件情况
        if(!empty($boundary[1])){
            preg_match_all('/.*'.$boundary[1].'.*/i',$body,$boundary_tag);
            $body = str_replace($boundary_tag[0],'{--tag--}',$body);
        }
        // 查找tag块
        preg_match_all("/(".$tag.".*+\n)/i",$body."\r\n\r\n",$he);
        // 把每个tag块分开成数组
        if(!empty($he[0])){
            foreach ($he[0] as $hk=>$h){
                $he[0][$hk] = trim($h);
            }
            arsort($he[0]);
            $body = str_replace($he[0],'{--tag--}',$body);
        }
        $body = explode('{--tag--}',$body);
        // 处理
        foreach ($body as $key=>$item){
            $data = [];

            $item = trim($item);
            // 附件的头
            if(!$item) { continue; }

            // 邮件体包含邮件体
            if(preg_match("/boundary=\"([-_a-z0-9]{5,})\"/Ui",$item,$bm)){
                if (strpos($item,$bm[1].'--')!==false){
                    $data = (new self('--'.$bm[1]."\r\n".$item,$this->fileSavePath))->getItem();
//                    $this->setItem($data);
                    // 合并邮件体
                    $this->item = array_merge($this->item,$data);
                }
                continue;
            }

            // 先解码解码
            $encode = $this->body_match_tag('Content-Transfer-Encoding:',$item);
            if($encode){
                $data['encode'] = strtolower($encode['text']);
                $item = str_replace($encode['origin'],'',$item);
            }

            // 内容类型
            $type = $this->preg_match_type($item);
            if($type){
                $data['type'] = strtolower($type['type']);
                // 编码
                if(isset($type['charset'])){
                    $data['charset'] = strtolower($type['charset']);
                }
                // nama。附件
                if(isset($type['name'])){
                    $data['name'] = $type['name'];
                }
                // 删除
                $item = str_replace($type['origin'],'',$item);
            }

            //
            if(empty($data['charset'])){
                // 编码
                $code = $this->preg_match_charset($item);
                if($code){
                    $data['charset'] = strtolower($code['charset']);

                    $item = str_replace($code['origin'],'',$item);
                }
            }

            // 先匹配留存文件名称
            preg_match('/filename="(\w?.*)"/',$item,$filename);
            if(!empty($filename[1])){
                $filename = MailFun::decodeMimeStr($filename[1]);
            }


            // 删除不需要的tag属性,如果需要进进行解析
            $item = $this->body_remove_tag($item,'Content-Description:');
            $item = $this->body_remove_tag($item,'Content-Disposition:');
            $item = $this->body_remove_tag($item,'Mime-Version:');

            $data['body'] =   trim($item);

            if(!empty($data['type'])){
                // 邮件头
                if($data['type'] == 'multipart/alternative'){

                }
                // 是文本还是附件
                else if(strpos($data['type'],'text/') === 0 ){
                    // body解密
                    switch($data['encode']??''){
                        case 'base64': {
                            $data['body'] = base64_decode($data['body']);
                            break;
                        }
                        case 'quoted-printable': {
                            $data['body'] = quoted_printable_decode($data['body']);
                            break;
                        }
                        case '8bit': {
                            try {
                                $data['body'] = DeCoding::de8bit($data['body']);
                                $data['body'] = quoted_printable_decode($data['body']);
                            }catch (\Throwable $e){

                            }

                            break;
                        }
                    }

                    // 转码
//                    if(isset($data['charset']) && $data['charset']){
//                        $debody = @mb_convert_encoding($data['body'],'utf-8',$data['charset']);
//                        if($debody){
//                            $data['body'] = $debody;
//                            $debody = null;
//                        }
//                    }


                }
                // 系统退信//里面包含了发送邮件所有内容,这里不记录
                elseif (strpos($data['type'],'message') === 0){
                    $data['body'] = '';// 一般不需要这些内容,如有需要就要重新解析
                }
                elseif (!empty($data['type']) && $data['body']){
                    // 解析附件
                    $data = $this->parseFile($data,$filename);
                }
            }

            $this->setItem($data);

        }

    }


    /**
     * 解析文件
     * @param $item
     * @return array|mixed
     * @author:dc
     * @time 2022/8/12 10:40
     */
    private function parseFile($item,$filename=''){
        $data = [];
        // 查找文件名

        $data['filename'] = $this->file_save_name($item['body'],'filename');
        $data['name'] = $this->file_save_name($item['body'],'name');
        $data['name']   =   $data['name'] ? : ($item['name']??$filename);
        $data['filename'] = $data['filename'] ? : $data['name'];

        // 是否有文件名
        if(empty($data['filename']) || strpos($data['filename'],'.')===false){
            return $item;
        }

        $ext = explode('.',$data['filename']);
        $ext = end($ext);

//        if(!empty($item['type'])){
//            // 文件类型来判断后缀
//            // // download it from http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
//            if(is_readable(__DIR__.'/mime.types')){
//                $f = fopen(__DIR__.'/mime.types','r');
//                while(!feof($f)){
//                    $fext = fgets($f);
//                    if($fext){
//                        $fext = strtolower($fext);
//                        $item['type'] = strtolower($item['type']);
//                        // 找到了类型后缀
//                        if(strpos($fext,$item['type']) === 0){
//                            $ext = trim(str_replace($item['type'],'',$fext));
//                            break;//找到了要跳出循环
//                        }
//                    }
//                }
//                // 关闭文件
//                fclose($f);
//            }
//        }

        // 找不到后缀,说明不是文件
//        if(empty($ext)){
            // 文件后缀
//            $ext = explode('.',$data['filename']);
//            $ext = count($ext) > 1 ? ($ext[count($ext)-1]??'') : '';
            // 直接返回
//            return $item['body'];
//        }


        // content id
        preg_match("/Content-ID:[\s].*<[\w\W]{1,}>/i",$item['body'],$result);
        if (!empty($result[0])){
            $data['content-id'] = explode('<',$result[0]);
            $data['content-id'] = $data['content-id'][1];
            $data['content-id'] = trim($data['content-id']);
            $data['content-id'] = trim($data['content-id'],'>');

            $item['body'] = str_replace($result[0],'',$item['body']);
        }

        $item['body'] = str_replace($result,'',$item['body']);


        $content = base64_decode(trim($item['body']));

        if($content){
            // 目录
            $data['path']   =   $this->fileSavePath;
            if(!is_dir($data['path'])){
                mkdir($data['path'],0775,true);
            }
            $data['signName'] = md5($content).($ext ? '.'.$ext : '');
            $data['path']   =   $data['path'].'/'.$data['signName'];
            // 保存文件
            @file_put_contents($data['path'],$content);
        }


        return $data;
    }

    // 获取文件名称
    private function file_save_name(&$body,$tag){
        preg_match('/'.$tag.'="[(\S\W.*\s.*)]{1,}"/i',$body,$result);
        if($result[0]??''){
            $body = str_replace($result[0],'',$body);
        }
        $val = trim(str_replace([$tag.'=','"',"'"],'',$result[0]??''));
        if ($val && strpos($val,'=?')===0){

            $val = iconv_mime_decode($val,ICONV_MIME_DECODE_CONTINUE_ON_ERROR,'utf-8');
        }

        return $val;
    }

    /**
     * 删除tag
     * @param $body
     * @param $tag
     * @return mixed|string|string[]
     * @author:dc
     * @time 2022/8/12 10:34
     */
    private function body_remove_tag($body,$tag){
        preg_match("/{$tag}[\w\W].*/i",$body,$result);
        if(!empty($result[0])) {
            $body = str_replace($result, '', $body);
        }
        return $body;
    }

    /**
     * 读取编码
     * @param $item
     * @return array
     * @author:dc
     * @time 2022/8/12 10:28
     */
    private static function preg_match_charset($item){
        // 匹配内容 type
        preg_match('/charset[ \t]{0,}=[ \t]{0,}"?[ \t0-9a-zA-Z-]{1,}"?/i',$item,$result);

        if(!empty($result[0])){
            $ret['origin'] = trim($result[0]);
            // charset
            $ret['charset'] = trim(str_replace(['charset','=','"',"'"],'',$ret['origin']));

            return $ret;
        }
        return [];
    }

    /**
     * 解析type
     * @param $item
     * @return array
     * @author:dc
     * @time 2022/8/12 10:26
     */
    private function preg_match_type($item){
        // 匹配内容 type
        preg_match("/Content-Type:[\w\W].*/i",$item,$result);

        if(!empty($result[0])){
            $ret['origin'] = trim($result[0]);
            // type
            $type = str_replace(['Content-Type:','"',"'"],'',$ret['origin']);
            $type = explode(';',$type);
            // 类型
            $ret['type'] = trim($type[0]);
            if(isset($type[1]) && $type[1]){
                // 编码
                $r = explode('=',$type[1]);
                $ret[strtolower(trim($r[0]))] = trim($r[1]??'');
            }
            return $ret;
        }
        return [];
    }

    /**
     * 匹配tag
     * @param $tag
     * @param $item
     * @return array
     * @author:dc
     * @time 2022/8/12 10:05
     */
    private function body_match_tag($tag,$item){
        // tag Content-Transfer-Encoding:
        preg_match("/".$tag."[\w\W].*/i",$item,$result);

        if(!empty($result[0])){
            $ret['origin'] = trim($result[0]);
            // charset
            $ret['text'] = trim(str_replace([$tag,'"',"'"],'',$ret['origin']));

            return $ret;
        }
        return [];
    }


}