<?php
namespace EasyChat\Library\Email;

 
final class ImapConnector {
    public function testConnection(array $account): bool {
        $this->fetchSince($account, 4294967294, 1);
        return true;
    }
    public function fetchSince(array $account, int $lastUid, int $limit = 10): array {
        $host=(string)$account['imap_host']; $port=(int)$account['imap_port'];$ip=EmailEndpoint::resolveAddress($host,!empty($account['allow_private_server']));if($ip==='')throw new \RuntimeException('imap_dns_failed');
        $context=stream_context_create(['ssl'=>['verify_peer'=>true,'verify_peer_name'=>true,'allow_self_signed'=>false,'peer_name'=>$host,'SNI_enabled'=>true]]);
        $errno=0;$error='';$socket=@stream_socket_client('tls://'.EmailEndpoint::socketHost($ip).':'.$port,$errno,$error,10,STREAM_CLIENT_CONNECT,$context);
        if(!$socket)throw new \RuntimeException('imap_connect_failed');
        stream_set_timeout($socket,12);
        try {
            $this->line($socket);
            $this->command($socket,'A1','LOGIN '.$this->quote((string)$account['username']).' '.$this->quote((string)$account['password']));
            $select=$this->command($socket,'A2','SELECT "INBOX"');
            $uidValidity=0;
            if(preg_match('/\[UIDVALIDITY\s+(\d+)\]/i',$select,$m))$uidValidity=(int)$m[1];
            if($lastUid<0){$uidNext=1;if(preg_match('/\[UIDNEXT\s+(\d+)\]/i',$select,$m))$uidNext=max(1,(int)$m[1]);@fwrite($socket,"AZ LOGOUT\r\n");return ['uidvalidity'=>$uidValidity,'last_uid'=>$uidNext-1,'messages'=>[]];}
            $search=$this->command($socket,'A3','UID SEARCH UID '.max(1,$lastUid+1).':*');
            preg_match('/\* SEARCH([^\r\n]*)/i',$search,$m);
            $uids=array_values(array_filter(array_map('intval',preg_split('/\s+/',trim((string)($m[1]??''))))));
             
             
             
             
            $uids=array_values(array_filter($uids,static function($uid)use($lastUid){return $uid>$lastUid;}));
            $uids=array_slice($uids,0,max(1,min(25,$limit)));
            $messages=[];$tag=4;
            foreach($uids as $uid){
                $raw=$this->command($socket,'A'.$tag++,'UID FETCH '.$uid.' (BODY.PEEK[])');
                $messages[]=['uid'=>$uid,'raw'=>$this->extractLiteral($raw)];
            }
            @fwrite($socket,"AZ LOGOUT\r\n");
            return ['uidvalidity'=>$uidValidity,'messages'=>$messages];
        } finally { fclose($socket); }
    }
    private function command($socket,string $tag,string $command): string { fwrite($socket,$tag.' '.$command."\r\n");$out='';while(true){$line=$this->line($socket);$out.=$line;if(preg_match('/\{(\d+)\}\r?\n$/',$line,$m)){$length=(int)$m[1];$literal='';while(strlen($literal)<$length){$part=fread($socket,$length-strlen($literal));if($part===false||$part==='')throw new \RuntimeException('imap_literal_failed');$literal.=$part;}$out.=$literal;continue;}if(strpos($line,$tag.' ')===0){if(stripos($line,$tag.' OK')!==0)throw new \RuntimeException('imap_command_failed');break;}}return $out; }
    private function line($socket): string { $line=fgets($socket,8192);if($line===false)throw new \RuntimeException('imap_read_failed');return $line; }
    private function quote(string $value): string { if(preg_match('/[\r\n]/',$value))throw new \RuntimeException('imap_credential_invalid');return '"'.addcslashes($value,"\\\"").'"'; }
    private function extractLiteral(string $response): string { if(!preg_match('/\{(\d+)\}\r?\n/s',$response,$m,PREG_OFFSET_CAPTURE))return '';$start=$m[0][1]+strlen($m[0][0]);return substr($response,$start,(int)$m[1][0]); }
}
