<?php

namespace Lib\Mail;

use Lib\DbPool;
use Model\bodySql;
use Model\folderSql;
use Model\listsSql;

/**
 * 操作邮件
 * @author:dc
 * @time 2023/2/5 10:10
 * Class MailFun
 * @package Helper\Mail
 */
class Mail {

    /**
     * imap服务器连接实例
     * @var Imap
     */
    public Imap $client;

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

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

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

    /**
     * Mail constructor.
     * @param string $email
     * @param string $password
     * @param string $imap
     */
    public function __construct(string $email,string $password,string $imap)
    {
        $this->username = $email;
        $this->password = $password;
        $this->server = $imap;
    }

    /**
     * 登录imap服务器
     * @param bool $pass_err
     * @return int
     * @author:dc
     * @time 2023/3/14 10:03
     */
    public function login($pass_err=true):int {

        $this->client = new Imap();

        try {
            // 是否初始成功
            $this->client->login("ssl://{$this->server}:993",$this->username,$this->password);
        }catch (\Throwable $e){
            if($pass_err && $e->getCode() == 403){
                // 一天中超过 3次失败说明密码错误了
                if(redis()->incr('email_login_error:'.md5($this->username),86400) > 3){
                    // 登录失败了 ,
                    db()->update(\Model\emailSql::$table,['pwd_error'=>1],dbWhere(['email'=>$this->username]));
                }
                return -1;
            }else{
                logs($e->getMessage());
            }

            return $e->getCode() == 403 ? 0 : -1;

        }
        redis()->delete('email_login_error:'.md5($this->username));

        return 1;
    }


    /**
     * 同步文件夹
     * @param int $email_id
     * @param DbPool|null $db
     * @return mixed
     * @author:dc
     * @time 2023/2/5 10:58
     */
    public function syncFolder($email_id,$db=null){
        $db = $db ? $db : db();
        // 读取所有文件夹,未解密
        $folders    =   $this->client->getFolder();

//        $db->transaction();
        foreach ($folders as $folder){
            $pid = 0;
            $uuid = md5($email_id.$folder['folder']);

            // 处理子父文件夹
            if(str_contains($folder['folder'], '/')){
                // 子目录
                $folder['name'] = explode('/',$folder['parseFolder']);
                // 查找pid
                $pid = $db->value(folderSql::has(['uuid'=>md5($email_id.explode('/',$folder['folder'])[0])]));
                // 去掉父目录名称
                $folder['parseFolder'] = explode('/',$folder['parseFolder'])[1];
            }

            if(!$db->count(folderSql::has(['uuid'=>$uuid]))){
                try {
                    $db->insert(folderSql::$table,[
                        'email_id' => $email_id,
                        'folder' => folderAlias($folder['parseFolder']),
                        'origin_folder' => $folder['folder'],
                        'uuid'  =>  $uuid,
                        'pid'   =>  $pid
                    ],false);
                }catch (\Throwable $e){
                    // 这里就不处理失败了
                }
            }


        }
//        $db->commit();

    }


    /**
     * 同步邮件
     * @param $email_id
     * @param $folder_id
     * @param string $folder
     * @param null|DbPool $db
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/2/18 9:54
     */
    public function syncMail($email_id,$folder_id,$folder='INBOX',$db = null):bool {
        if(empty($folder)){
            return 0;
        }
//        _echo('正在同步文件夹:'.$folder);
        $db = $db ? $db : db();
        // 选择文件夹
        $status =   $this->client->selectFolder($folder);

        // 是否有邮件
        if (!is_array($status) || !isset($status['EXISTS']) || !$status['EXISTS']){
            return true;
        }

        // 更新数量
        $db->update(
            folderSql::$table,
            ['exsts'=>$status['EXISTS'],'unseen'=>$status['UNSEEN']??0],
            dbWhere(['id'=>$folder_id]),
            false
        );


        // 最后拉取的msgno
        $lastMsgno   =   $db->value(listsSql::lastMsgno($email_id,$folder_id));

        $nu = 20;

        if(!$lastMsgno){
            $msgno = range(1,$nu);
        }else{
            $msgno = range($lastMsgno,$lastMsgno+$nu);

            if($lastMsgno > $status['EXISTS']){
                $msgno = range($status['EXISTS'] > $nu ? $status['EXISTS'] - $nu : 1,$status['EXISTS']);
            }
            // 一样就不拉新的
            if($lastMsgno == $status['EXISTS']){
                return true;
            }
        }


        // 循环
        $results = $this->client->fetchHeader($msgno);
        if($results && is_array($results)){
            // 表示已存在新邮件
            if($folder == 'INBOX') redis()->incr('have_new_mail_'.$email_id,120);

            // 批量插入
            foreach ($results as $key=>$result){
                $header = $result['HEADER.FIELDS'];

                foreach ($result['FLAGS'] as $k=>$FLAG){
                    $result['FLAGS'][$k] = strtolower(str_replace('\\','',$FLAG));
                }
                try {

                    // 没有收件人
                    if(!empty($header['To'])){
                        $header['To'] = MailFun::toOrFrom($header['To']);
                    }else{
                        $header['To'] = [];
                    }

                    $header['From'] = MailFun::toOrFrom($header['From']);
                    // 抄送 ,密送
                    $cc = [];
                    $bcc = [];
                    if($header['Cc']??''){
                        $cc = MailFun::toOrFrom($header['Cc']);
                    }
                    if($header['Bcc']??''){
                        $bcc = MailFun::toOrFrom($header['Bcc']);
                    }


                    $data   =   [
                        'msgno'   =>  $key,
                        'uid'   =>  $result['UID'],
                        'subject'   =>  $header['Subject'],
                        'cc'    =>  $cc,
                        'bcc'    =>  $bcc,
                        'from'   =>  $header['From'][0]['email']??'',
                        'from_name'   =>  $header['From'][0]['name']??'',
                        'to'   =>  $header['To']?implode(',',array_column($header['To'],'email')):'',
                        'to_name'   =>  json_encode($header['To']),
                        'date'   =>  strtotime(is_array($header['Date']??'') ? $header['Date'][0] : $header['Date']??''),
                        'message_id'   =>  $header['Message-ID']??'',
                        'udate'   =>  strtotime($result['INTERNALDATE']),
                        'size'   =>  $result['RFC822.SIZE']??0,
                        'recent'   =>  in_array('recent',$result['FLAGS']) ? 1 : 0,
                        'seen'   =>  in_array('seen',$result['FLAGS']) ? 1 : 0,
                        'draft'   =>  in_array('draft',$result['FLAGS']) ? 1 : 0,
                        'flagged'   =>  in_array('flagged',$result['FLAGS']) ? 1 : 0,
                        'answered'   =>  in_array('answered',$result['FLAGS']) ? 1 : 0,
                        'folder_id'   =>  $folder_id,
                        'email_id'    =>  $email_id,
                        'uuid'  =>  md5($email_id.$folder_id.$result['UID']),
                        'is_file'  =>  MailFun::isFile($result['BODYSTRUCTURE']??'') ? 1: 0 //是否附件
                    ];
                    $data['date'] = $data['date'] ? : 0;
                }catch (\Throwable $e){
                    logs(
                        '邮件解析失败:'.PHP_EOL.$e->getMessage().PHP_EOL.print_r($result,true),
                        LOG_PATH.'/imap/mail/'.$email_id.'/'.$result['UID'].'.log'
                    );
                    unset($results[$key]);
                    continue;
                }

                $results[$key]  =   $data;
            }

            // 保存数据,这里其实不用再次写循环的。我想写一个
            $uuids = $db->all(listsSql::hasUuid(array_column($results,'uuid')));
            $uuids = $uuids ? array_column($uuids,null,'uuid') : [];

//            $db->transaction();
            foreach ($results as $insert){
                if(empty($uuids[$insert['uuid']])){
                    // 新增
                    try {
                        $id = $db->insert(listsSql::$table,$insert);
                        // 同步body内容
                        redis()->rPush('sync_email_body', [
                            'lists_id'  =>  $id,
                            'email_id'  =>  $email_id,
                            'folder_id'  =>  $folder_id,
                            'folder'  =>  $folder,
                            'uid'  =>  $insert['uid'],
                        ]);
                    }catch (\Throwable $e){

                    }


                }else{
                    // 修改
                    $db->update(
                        listsSql::$table,
                        $insert,
                        dbWhere(['id'=>$uuids[$insert['uuid']]['id']])
                    );
                }
            }
//            $db->commit();

            // 更新数量
            $db->update(
                folderSql::$table,
                ['last_sync_time' => time()],
                dbWhere(['id'=>$folder_id]),
                false
            );
            // 结束操作了

            // 再次调用
            $this->syncMail($email_id,$folder_id,$folder,$db);
        }

        return true;

    }


    /**
     * 同步 邮件 内容 body
     * @param $id
     * @param $msgno
     * @param $email_id
     * @param $folder_name
     * @param $email
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/2/9 10:29
     */
    public function syncBody($folder_name, $uid , $id, $db=null):bool {
        if(empty($folder_name)){
            return 0;
        }
        $db = $db ? $db : db();
        // 选择文件夹
        $this->client->selectFolder($folder_name);

        $body = $this->client->fetchBody([$uid],MAIL_ATTACHMENT_PATH,true);

        $body = array_values($body);
        $body = $body[0]['RFC822.TEXT']??'';

        if(!empty($body)){
            $description = '';
            foreach ($body as $key=>$item){
                if(!$description && in_array($item['type']??'',['text/html','text/plain'])){

                    if(!empty($item['charset'])){
                        $value = iconv($item['charset'],'utf-8',$item['body']);
                    }else{
                        $value = $item['body'];
                    }
                    $value=preg_replace("/<(script.*?)>(.*?)<(\/script.*?)>/si","",$value); //过滤script标签
                    $value=preg_replace("/<(\/?script.*?)>/si","",$value); //过滤script标签
                    $value=preg_replace("/javascript/si","Javascript",$value); //过滤script标签
                    $value=preg_replace("/<(style.*?)>(.*?)<(\/style.*?)>/si","",$value); //过滤style标签
                    $value=preg_replace("/<(\/?style.*?)>/si","",$value); //过滤style标签

                    $value = strip_tags($value);
                    $value = str_replace(["\n","\\n","&nbsp;"],'',$value);
                    $description = mb_substr(trim($value),0,190);

                }

                if(!empty($body[$key]['body'])){
                    $body[$key]['body'] = base64_encode($body[$key]['body']);
                }

                if(!empty($body[$key]['filename'])){
                    $body[$key]['filename'] = base64_encode($body[$key]['filename']);
                }

                if(!empty($body[$key]['name'])){
                    $body[$key]['name'] = base64_encode($body[$key]['name']);
                }

            }

            bodySql::insertOrUpdate($db,[
                'lists_id'    =>  $id,
                'text_html'  =>  $body // todo::因为邮件会出现多编码问题,会导致数据库写不进去
            ]);


            // 更新描述
            $db->update(listsSql::$table,[
                'description'  =>  $description
            ],dbWhere([
                'id'    =>  $id
            ]));
        }

        return true;

    }


    /**
     * 设置为未读
     * @param $uids
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2022/10/26 17:11
     */
    public function seen($uids,$folder,$seen):bool{
        // 选择目录
        $status =   $this->client->selectFolder($folder);

        return $this->client->flags($uids,[Imap::FLAGS_SEEN],$seen ? '+' : '-',true);
    }

    /**
     * 回复标记
     * @param $uids
     * @param $folder
     * @param $seen
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/4/6 17:10
     */
    public function answered($uids,$folder,$seen):bool{
        // 选择目录
        $status =   $this->client->selectFolder($folder);

        return $this->client->flags($uids,[Imap::FLAGS_ANSWERED],$seen ? '+' : '-',true);
    }


    /**
     * 复制
     * @param $uids
     * @param $folder
     * @param $to_folder
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/3/22 16:38
     */
    public function copy($uids,$folder,$to_folder){
        // 选择目录
        $status =   $this->client->selectFolder($folder);

        return $this->client->copy($uids,$to_folder);

    }

    /**
     * 移动邮件
     * @param $uids
     * @param $folder
     * @param $to_folder
     * @return bool
     * @throws \Exception
     * @author:dc
     * @time 2023/3/22 18:06
     */
    public function move($uids,$folder,$to_folder){
        // 选择目录
        $status =   $this->client->selectFolder($folder);

        return $this->client->move($uids,$to_folder);

    }


//    /**
//     * 删除
//     * @param $uids
//     * @param $folder
//     * @return bool
//     * @throws \Exception
//     * @author:dc
//     * @time 2023/3/22 17:52
//     */
//    public function delete($uids,$folder){
//        // 选择目录
//        $status =   $this->client->selectFolder($folder);
//
//        return $this->client->delete($uids);
//    }




}