<?php
class ImapDriver {
    private $fp;                      
    public $error;                    
    private $commandCounter = "00000001";
    public $lastResponse = array();
    public $lastEndline = "";

    public function init(string $host, int $port): bool {
        $errno = 0;
        $errstr = '';
        if (!($this->fp = fsockopen($host, $port, $errno, $errstr, 15))) {
            $this->error = "Could not connect to host ($errno) $errstr";
            return false;
        }
        if (!stream_set_timeout($this->fp, 15)) {
            $this->error = "Could not set timeout";
            return false;
        }
        fgets($this->fp);     
        return true;
    }
    
    private function close(): void {
        fclose($this->fp);
    }

    private function command(string $command): void {
        $this->lastResponse = array();
        $this->lastEndline  = "";
        fwrite($this->fp, "$this->commandCounter $command\r\n");
        while ($line = trim(fgets($this->fp))) {
            $encodedLine = mb_convert_encoding($line, 'UTF-8');
            $line_arr = preg_split('/\s+/', $encodedLine, 0, PREG_SPLIT_NO_EMPTY);  
            if (count($line_arr) > 0) {
                $code = array_shift($line_arr);                              
                if (strtoupper($code) == $this->commandCounter) {
                    $this->lastEndline = join(' ', $line_arr);              
                    break;
                }
                $this->lastResponse[] = $line;                          
            } else {
                $this->lastResponse[] = $line;
            }
        }
        $this->incrementCounter();
    }

    private function incrementCounter(): void {
        $this->commandCounter = sprintf('%08d', intval($this->commandCounter) + 1);
    }

    public function login(string $login, string $pwd): bool {
        $this->command("LOGIN $login $pwd");
        if (preg_match('~^OK~', $this->lastEndline)) {
            return true;
        }
        $this->error = join(', ', $this->lastResponse);
        $this->close();
        return false;
    }

    public function selectFolder(string $folder): bool{
        $this->command("SELECT $folder");
        if (preg_match('~^OK~', $this->lastEndline)) {
            return true;
        }
        $this->error = join(', ', $this->lastResponse);
        $this->close();
        return false;
    }

    public function getUidsBySearch(string $criteria): array|false {
        $this->command("SEARCH $criteria");
        if (preg_match('~^OK~', $this->lastEndline)
                && is_array($this->lastResponse)
                && count($this->lastResponse) == 1) {
            $splitted_response = explode(' ', $this->lastResponse[0]);
            $uids              = array();
            foreach ($splitted_response as $item) {
                if (preg_match('~^\d+$~', $item)) {
                    $uids[] = $item;              
                }
            }
            return $uids;
        }
        $this->error = join(', ', $this->lastResponse);
        $this->close();
        return false;
    }

    public function getHeadersFromUid(int $uid): array|false {
        $this->command("FETCH $uid BODY.PEEK[HEADER]");
        if (!preg_match('~^OK~', $this->lastEndline)) {
            array_shift($this->lastResponse);
            $headers    = array();
            $prev_match = '';
            foreach ($this->lastResponse as $item) {
                $match = [];
                if (preg_match('~^([a-z][a-z0-9-_]+):~is', $item, $match)) {
                    $header_name           = strtolower($match[1]);
                    $prev_match            = $header_name;
                    $headers[$header_name] = trim(substr($item, strlen($header_name) + 1));
                    
                } else {
                    $headers[] .= " " . $item;
                }
            }
            return $headers;
        }
        $this->error = join(', ', $this->lastResponse);
        $this->close();
        return false;
    }
    
    public function getEmail(string|int $uid): array|false {
        $this->command('FETCH ' . $uid . ' BODY');
        if (preg_match('~^OK~', $this->lastEndline)) {
            return $this->parseEmailBody($this->lastResponse[0]);
        }
        $this->error = join(', ', $this->lastResponse);
        $this->close();
        return false;
    }
    
    protected function parseEmailBody(string $body): array {
        $contentStart = stripos($body, 'body (');
        $content = substr($body, $contentStart + 6, -1);
        print_r($content);die;
        return [];
    }
}