<?php

declare(strict_types=1);

namespace FecaMailshots\Infrastructure;

use FecaMailshots\Application\ImapAppender;

final class PhpImapAppender implements ImapAppender
{
    /** @var array<string, bool> */
    private array $attemptCache = [];

    public function appendSent(array $credentials, string $rawMime, string $attemptId): void
    {
        if (isset($this->attemptCache[$attemptId])) {
            return;
        }

        $host = (string) ($credentials['imap_host'] ?? '');
        $port = (int) ($credentials['imap_port'] ?? 0);
        $user = (string) ($credentials['imap_user'] ?? '');
        $pass = (string) ($credentials['imap_password'] ?? '');
        $folder = (string) ($credentials['imap_sent_folder'] ?? '');
        $flags = (string) ($credentials['imap_mailbox_flags'] ?? '');

        if ($host === '' || $port <= 0 || $user === '' || $pass === '' || $folder === '' || $flags === '') {
            throw new \RuntimeException('IMAP credentials are missing.');
        }

        if (!function_exists('imap_open') || !function_exists('imap_append')) {
            throw new \RuntimeException('IMAP extension is not available in PHP runtime.');
        }

        $mailboxRoot = sprintf('{%s:%d%s}', $host, $port, $flags);
        $imap = @imap_open($mailboxRoot . 'INBOX', $user, $pass);
        if ($imap === false) {
            throw new \RuntimeException('Failed to open IMAP mailbox: ' . (imap_last_error() ?: 'unknown error'));
        }

        try {
            $appendMailbox = $this->resolveAppendMailbox($imap, $mailboxRoot, $folder);
            if (!@imap_append($imap, $appendMailbox, $rawMime . "\r\n", "\\Seen")) {
                throw new \RuntimeException('Failed to append to IMAP Sent folder: ' . (imap_last_error() ?: 'unknown error'));
            }
            $this->attemptCache[$attemptId] = true;
        } finally {
            imap_close($imap);
        }
    }

    /**
     * @param resource $imap
     */
    private function resolveAppendMailbox($imap, string $mailboxRoot, string $configuredFolder): string
    {
        $configuredFolder = trim($configuredFolder);
        if ($configuredFolder === '') {
            throw new \RuntimeException('IMAP sent folder is required.');
        }

        $exact = $mailboxRoot . $configuredFolder;
        $boxes = function_exists('imap_getmailboxes') ? @imap_getmailboxes($imap, $mailboxRoot, '*') : false;
        if (!is_array($boxes) || $boxes === []) {
            return $exact;
        }

        $names = [];
        foreach ($boxes as $box) {
            $name = (string) ($box->name ?? '');
            if ($name !== '') {
                $names[] = $name;
            }
        }
        if ($names === []) {
            return $exact;
        }

        foreach ($names as $name) {
            if (strcasecmp($name, $exact) === 0) {
                return $name;
            }
        }

        $matches = [];
        $configuredLower = strtolower($configuredFolder);
        foreach ($names as $name) {
            if (str_starts_with($name, $mailboxRoot)) {
                $short = substr($name, strlen($mailboxRoot));
            } else {
                $short = $name;
            }
            if (strtolower($short) === $configuredLower) {
                $matches[] = $name;
                continue;
            }

            $terminal = $this->terminalMailboxName($short);
            if ($terminal !== '' && strtolower($terminal) === $configuredLower) {
                $matches[] = $name;
            }
        }

        if (count($matches) === 1) {
            return $matches[0];
        }
        if (count($matches) > 1) {
            throw new \RuntimeException(
                'Configured IMAP sent folder is ambiguous. Matches: ' . implode(', ', $matches)
            );
        }

        $inboxCandidates = [
            $mailboxRoot . 'INBOX.' . $configuredFolder,
            $mailboxRoot . 'INBOX/' . $configuredFolder,
        ];
        foreach ($inboxCandidates as $candidate) {
            foreach ($names as $name) {
                if (strcasecmp($name, $candidate) === 0) {
                    return $name;
                }
            }
        }

        throw new \RuntimeException(
            'IMAP sent folder "' . $configuredFolder . '" does not exist on server. Available mailboxes include: '
            . implode(', ', array_slice($names, 0, 20))
        );
    }

    private function terminalMailboxName(string $name): string
    {
        $name = trim($name);
        if ($name === '') {
            return '';
        }

        $dotPos = strrpos($name, '.');
        $slashPos = strrpos($name, '/');
        $pos = max($dotPos === false ? -1 : $dotPos, $slashPos === false ? -1 : $slashPos);
        if ($pos < 0) {
            return $name;
        }
        return substr($name, $pos + 1);
    }
}
