<?php

require_once __DIR__ . '/../../../config/database.php';
require_once __DIR__ . '/../../../config/helpers.php';
require_once __DIR__ . '/../../../middleware/admin.php';
require_once __DIR__ . '/MailboxRepository.php';

requireAdmin();

if (getMethod() !== 'POST') {
    jsonResponse(false, 'method.not_allowed', null, 405);
}

if (!function_exists('imap_open')) {
    jsonResponse(false, 'mailboxes.imap_not_available', null, 500);
}

$route = getApiRouteSegments();
$mailboxId = isset($route[2]) && ctype_digit((string) $route[2]) ? (int) $route[2] : 0;

global $pdo;
$config = require __DIR__ . '/../../../config/app.php';
$repo = new MailboxRepository($pdo);

if ($mailboxId <= 0) {
    jsonResponse(false, 'mailboxes.not_found', ['route' => $route], 404);
}

$mailbox = $repo->findById($mailboxId, true);
if (!$mailbox) {
    jsonResponse(false, 'mailboxes.not_found', ['mailbox_id' => $mailboxId], 404);
}

$password = decryptMailboxSecret((string) $mailbox['password_encrypted'], $config);
$logId = $repo->createSyncLog($mailboxId);
$imported = 0;

try {
    $mailboxString = sprintf(
        '{%s:%d/%s%s}INBOX',
        $mailbox['host'],
        (int) $mailbox['port'],
        strtolower((string) $mailbox['protocol']),
        strtolower((string) $mailbox['encryption']) !== 'none' ? '/' . strtolower((string) $mailbox['encryption']) : ''
    );

    $imap = @imap_open(
        $mailboxString,
        (string) $mailbox['username'],
        $password,
        (int) ($config['mailboxes']['imap_open_options'] ?? 0),
        1
    );

    if ($imap === false) {
        $repo->finishSyncLog($logId, 'failed', 'mailboxes.connection_failed', 0);
        jsonResponse(false, 'mailboxes.connection_failed', ['errors' => imap_errors() ?: []], 422);
    }

    $messageNumbers = imap_search($imap, 'ALL') ?: [];
    rsort($messageNumbers);
    $messageNumbers = array_slice($messageNumbers, 0, (int) ($config['mailboxes']['sync_limit'] ?? 50));

    foreach ($messageNumbers as $msgNo) {
        $overviewList = imap_fetch_overview($imap, (string) $msgNo, 0);
        $overview = $overviewList[0] ?? null;

        if (!$overview) {
            continue;
        }

        $uid = (int) imap_uid($imap, (int) $msgNo);
        if ($uid <= 0) {
            $uid = (int) $msgNo;
        }

        $headers = imap_fetchheader($imap, (int) $msgNo, FT_PREFETCHTEXT) ?: '';
        $rawBody = imap_body($imap, (int) $msgNo, FT_PEEK) ?: '';
        $receivedAt = null;

        if (!empty($overview->date)) {
            $ts = strtotime((string) $overview->date);
            if ($ts !== false) {
                $receivedAt = date('Y-m-d H:i:s', $ts);
            }
        }

        $from = decodeMimeHeaderValue((string) ($overview->from ?? ''));
        $to = decodeMimeHeaderValue((string) ($overview->to ?? ''));
        $subject = isset($overview->subject) ? decodeMimeHeaderValue((string) $overview->subject) : null;

        $structure = imap_fetchstructure($imap, (int) $msgNo);
        $textBody = null;
        $htmlBody = null;
        $attachments = [];

if ($structure) {
    if (!empty($structure->parts) && is_array($structure->parts)) {
        $flatParts = flattenImapParts($structure);

        foreach ($flatParts as $item) {
            $partNumber = $item['part_number'];
            $part = $item['part'];
            $params = getPartParameters($part);

            $type = (int) ($part->type ?? 0);
            $subtype = strtoupper((string) ($part->subtype ?? ''));
            $encoding = (int) ($part->encoding ?? 0);
            $disposition = strtolower((string) ($part->disposition ?? ''));

            $filenameParam = $params['filename'] ?? $params['name'] ?? '';
            if (is_array($filenameParam)) {
                $filenameParam = reset($filenameParam) ?: '';
            }
            $filename = trim((string) $filenameParam);

            $contentIdParam = $params['content-id'] ?? ($part->id ?? '');
            if (is_array($contentIdParam)) {
                $contentIdParam = reset($contentIdParam) ?: '';
            }
            $contentId = trim((string) $contentIdParam, '<>');

            $rawPartBody = imap_fetchbody($imap, (int) $msgNo, $partNumber, FT_PEEK);
            if ($rawPartBody === false) {
                $rawPartBody = '';
            }

            $decodedBody = decodeImapPartBody((string) $rawPartBody, $encoding);

            if ($type === 0 && $subtype === 'PLAIN' && $textBody === null && $filename === '') {
                $textBody = $decodedBody;
                continue;
            }

            if ($type === 0 && $subtype === 'HTML' && $htmlBody === null && $filename === '') {
                $htmlBody = $decodedBody;
                continue;
            }

            $isAttachment = $filename !== '' || in_array($disposition, ['attachment', 'inline'], true);

            if ($isAttachment && (bool) ($config['mailboxes']['store_attachments'] ?? true)) {
                $originalName = $filename !== '' ? $filename : 'attachment';
                $originalName = decodeMimeHeaderValue((string) $originalName);
                $originalName = trim($originalName);

                if ($originalName === '' || strtolower($originalName) === 'array') {
                    $fallbackExt = '';
                    if ($type === 0 && $subtype !== '') {
                        $fallbackExt = strtolower($subtype) === 'PLAIN' ? 'txt' : strtolower($subtype);
                    } elseif ($type === 5 && $subtype !== '') {
                        $fallbackExt = strtolower($subtype);
                    }

                    $originalName = 'attachment';
                    if ($fallbackExt !== '') {
                        $originalName .= '.' . $fallbackExt;
                    }
                }

                $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
                $mimeType = 'application/octet-stream';

                if ($type === 0) {
                    $mimeType = 'text/' . strtolower($subtype ?: 'plain');
                } elseif ($type === 5) {
                    $mimeType = 'application/' . strtolower($subtype ?: 'octet-stream');
                } elseif ($type === 2) {
                    $mimeType = 'message/rfc822';
                }

                if ($disposition === 'inline' && !(bool) ($config['mailboxes']['sync_inline_attachments'] ?? true)) {
                    continue;
                }

                if (strlen($decodedBody) > (int) ($config['mailboxes']['max_attachment_size_bytes'] ?? (25 * 1024 * 1024))) {
                    continue;
                }

                $relativeDir = trim((string) ($config['mailboxes']['attachments_directory'] ?? 'uploads/mailboxes'), '/');
                $absoluteDir = dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativeDir) . DIRECTORY_SEPARATOR . $mailboxId;

                $savedFilename = saveMailboxBinaryFile($absoluteDir, $originalName, $decodedBody);

                if (is_array($savedFilename)) {
                    $savedFilename =
                        $savedFilename['filename']
                        ?? $savedFilename['basename']
                        ?? $savedFilename['name']
                        ?? reset($savedFilename)
                        ?? '';
                }

                $savedFilename = basename(trim((string) $savedFilename));

                if ($savedFilename === '' || strtolower($savedFilename) === 'array') {
                    $savedFilename = basename($originalName);
                }

                if ($savedFilename === '' || strtolower($savedFilename) === 'array') {
                    $savedFilename = 'attachment.bin';
                }

                $relativePath = '/' . $relativeDir . '/' . $mailboxId . '/' . $savedFilename;

                $attachments[] = [
                    'mailbox_id' => $mailboxId,
                    'imap_part' => $partNumber,
                    'filename' => $savedFilename,
                    'original_name' => $originalName,
                    'mime_type' => $mimeType,
                    'extension' => $extension !== '' ? $extension : null,
                    'size' => strlen($decodedBody),
                    'storage_path' => $relativePath,
                    'public_url' => $relativePath,
                    'content_id' => $contentId !== '' ? $contentId : null,
                    'disposition' => $disposition !== '' ? $disposition : 'attachment',
                    'is_inline' => $disposition === 'inline' ? 1 : 0,
                ];
            }
        }
    } else {
        $type = (int) ($structure->type ?? 0);
        $subtype = strtoupper((string) ($structure->subtype ?? ''));
        $encoding = (int) ($structure->encoding ?? 0);
        $decodedBody = decodeImapPartBody($rawBody, $encoding);

        if ($type === 0 && $subtype === 'HTML') {
            $htmlBody = $decodedBody;
        } else {
            $textBody = $decodedBody;
        }
    }
}
        if ($textBody === null && $htmlBody !== null) {
            $textBody = trim(html_entity_decode(strip_tags($htmlBody)));
        }

        $dbRead = isset($overview->seen) ? (int) $overview->seen : 0;
        if ((bool) ($config['mailboxes']['mark_unread_on_insert'] ?? false)) {
            $dbRead = 0;
        }

        $resolvedToEmail = normalizeEmailAddress(extractOriginalRecipientEmail($headers, $to));
        $matchedInbox = $resolvedToEmail ? $repo->findInboxByEmailAddress($resolvedToEmail) : null;

        $messageRowId = $repo->upsertMessageByUid([
            'mailbox_id' => $mailboxId,
            'inbox_id' => $matchedInbox['id'] ?? null,
            'uid' => $uid,
            'message_id' => $uid > 0 ? (string) $uid : null,
            'message_id_header' => isset($overview->message_id) ? (string) $overview->message_id : null,
            'from_email' => extractAddressEmail($from),
            'from_name' => extractAddressName($from),
            'to_email' => $resolvedToEmail,
            'subject' => $subject,
            'text_body' => $textBody,
            'html_body' => $htmlBody,
            'raw_headers' => $headers,
            'raw_body' => $rawBody,
            'received_at' => $receivedAt,
            'is_read' => $dbRead,
            'is_starred' => isset($overview->flagged) ? (int) $overview->flagged : 0,
            'is_deleted' => isset($overview->deleted) ? (int) $overview->deleted : 0,
        ]);

        if ((bool) ($config['mailboxes']['store_attachments'] ?? true)) {
            $repo->replaceAttachmentsForMessage(
                $messageRowId,
                array_map(
                    static function (array $attachment) use ($messageRowId) {
                        $attachment['message_id'] = $messageRowId;
                        return $attachment;
                    },
                    $attachments
                )
            );
        }

        if ((bool) ($config['mailboxes']['mark_real_inbox_read_on_sync'] ?? false)) {
            @imap_setflag_full($imap, (string) $msgNo, "\\Seen");
        }

        if ((bool) ($config['mailboxes']['mark_real_inbox_unread_on_sync'] ?? false)) {
            @imap_clearflag_full($imap, (string) $msgNo, "\\Seen");
        }

        if (!empty($config['mailboxes']['move_to_folder_after_sync'])) {
            @imap_mail_move($imap, (string) $msgNo, (string) $config['mailboxes']['move_to_folder_after_sync']);
        } elseif ((bool) ($config['mailboxes']['delete_after_sync'] ?? false)) {
            @imap_delete($imap, (string) $msgNo);
        }

        $imported++;
    }

    if (!empty($config['mailboxes']['move_to_folder_after_sync']) || (bool) ($config['mailboxes']['delete_after_sync'] ?? false)) {
        @imap_expunge($imap);
    }

    imap_close($imap);

    $repo->touchLastSyncAt($mailboxId);
    $repo->finishSyncLog($logId, 'success', 'mailboxes.sync_success', $imported);

    jsonResponse(true, 'mailboxes.sync_success', [
        'mailbox_id' => $mailboxId,
        'imported_count' => $imported,
    ]);
} catch (Throwable $e) {
    $repo->finishSyncLog($logId, 'failed', 'mailboxes.sync_failed', $imported);
    jsonResponse(false, 'mailboxes.sync_failed', ['error' => $e->getMessage()], 500);
}