<?php

declare(strict_types=1);

namespace App\Services\Tickets;

use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Message;

class ImapEmailService implements EmailServiceInterface
{
    private ClientManager $clientManager;

    public function __construct()
    {
        $this->clientManager = new ClientManager;
    }

    /** @return array<int, ParsedEmail> */
    public function fetchNewEmails(): array
    {
        $client = $this->clientManager->make([
            'host' => config('tickets.imap.host'),
            'port' => config('tickets.imap.port'),
            'encryption' => config('tickets.imap.encryption'),
            'validate_cert' => config('tickets.imap.validate_cert'),
            'username' => config('tickets.imap.username'),
            'password' => config('tickets.imap.password'),
            'protocol' => config('tickets.imap.protocol'),
        ]);

        $client->connect();

        $folder = $client->getFolder((string) config('tickets.imap_folder'));

        if ($folder === null) {
            return [];
        }

        $messages = $folder->messages()->unseen()->get();

        $emails = [];
        foreach ($messages as $message) {
            /** @var Message $message */
            $from = $this->extractFromAddress($message);
            $subject = (string) $message->getSubject();
            $body = $this->cleanBody($message);
            $messageId = (string) $message->getMessageId();
            $inReplyTo = $this->extractHeader($message, 'in_reply_to');
            $references = $this->extractHeader($message, 'references');

            $emails[] = new ParsedEmail(
                messageId: $messageId,
                from: $from,
                subject: $subject,
                body: $body,
                inReplyTo: $inReplyTo,
                references: $references,
            );

            $this->markAsProcessed($message);
        }

        return $emails;
    }

    public function markAsProcessed(mixed $message): void
    {
        if ($message instanceof Message) {
            $message->setFlag('Seen');
        }
    }

    private function extractFromAddress(Message $message): string
    {
        $from = $message->getFrom();
        if ($from && count($from) > 0) {
            return (string) $from->first()->mail;
        }

        return '';
    }

    private function extractHeader(Message $message, string $header): ?string
    {
        $value = $message->get($header);
        if ($value !== null && (string) $value !== '') {
            return (string) $value;
        }

        return null;
    }

    private function cleanBody(Message $message): string
    {
        $body = $message->hasTextBody()
            ? (string) $message->getTextBody()
            : strip_tags((string) $message->getHTMLBody());

        return $this->stripSignaturesAndQuotes($body);
    }

    public static function stripSignaturesAndQuotes(string $body): string
    {
        // Strip email signatures
        $signaturePatterns = [
            '/^--\s*$/m',
            '/^_{3,}$/m',
            '/^Sent from my .+$/mi',
            '/^Get Outlook for .+$/mi',
            '/^Sent from .+$/mi',
        ];

        foreach ($signaturePatterns as $pattern) {
            if (preg_match($pattern, $body, $matches, \PREG_OFFSET_CAPTURE)) {
                $body = substr($body, 0, (int) $matches[0][1]);
            }
        }

        // Strip quoted text (lines starting with >)
        $lines = explode("\n", $body);
        $cleanLines = [];
        $hitQuote = false;

        foreach ($lines as $line) {
            // Check for "On <date> <person> wrote:" pattern
            if (preg_match('/^On .+ wrote:\s*$/i', $line)) {
                $hitQuote = true;

                continue;
            }

            // Check for lines starting with >
            if (str_starts_with(trim($line), '>')) {
                $hitQuote = true;

                continue;
            }

            // Once we hit quoted content, skip remaining lines
            if ($hitQuote) {
                continue;
            }

            $cleanLines[] = $line;
        }

        return trim(implode("\n", $cleanLines));
    }
}
