<?php

if (!function_exists('handleCors')) {
    function handleCors(): void
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';

        $allowedOrigins = [
            'http://localhost:5173',
            'http://127.0.0.1:5173',
            'https://email.edafaty.com',
        ];

        if (in_array($origin, $allowedOrigins, true)) {
            header("Access-Control-Allow-Origin: {$origin}");
        }

        header('Vary: Origin');
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, Accept, Origin, X-Guest-Inbox-Token');
        header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');

        if (strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'OPTIONS') {
            http_response_code(204);
            exit;
        }
    }
}

if (!function_exists('jsonResponse')) {
    function jsonResponse(bool $ok, string $message, $data = null, int $status = 200): void
    {
        http_response_code($status);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'ok' => $ok,
            'message' => $message,
            'data' => $data,
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }
}

if (!function_exists('getJsonInput')) {
    function getJsonInput(): array
    {
        $raw = file_get_contents('php://input');
        if (!$raw) {
            return [];
        }

        $decoded = json_decode($raw, true);
        return is_array($decoded) ? $decoded : [];
    }
}

if (!function_exists('getRequestHeadersSafe')) {
    function getRequestHeadersSafe(): array
    {
        $headers = function_exists('getallheaders') ? getallheaders() : [];
        return is_array($headers) ? $headers : [];
    }
}

if (!function_exists('getHeaderValue')) {
    function getHeaderValue(string $name): ?string
    {
        $target = strtolower($name);
        foreach (getRequestHeadersSafe() as $key => $value) {
            if (strtolower((string) $key) === $target) {
                $value = is_array($value) ? implode(',', $value) : (string) $value;
                $value = trim($value);
                return $value !== '' ? $value : null;
            }
        }

        $serverKey = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
        $serverValue = $_SERVER[$serverKey] ?? null;
        if (is_string($serverValue)) {
            $serverValue = trim($serverValue);
            return $serverValue !== '' ? $serverValue : null;
        }

        return null;
    }
}

if (!function_exists('getBearerToken')) {
    function getBearerToken(): ?string
    {
        $authHeader = getHeaderValue('Authorization') ?? '';

        if (preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
            return trim($matches[1]);
        }

        return null;
    }
}

if (!function_exists('getRequestToken')) {
    function getRequestToken(array $config): ?string
    {
        $bearer = getBearerToken();
        if ($bearer) {
            return $bearer;
        }

        $cookieName = $config['auth']['session_cookie'] ?? 'user_token';
        $cookieToken = $_COOKIE[$cookieName] ?? null;
        return is_string($cookieToken) && $cookieToken !== '' ? $cookieToken : null;
    }
}

if (!function_exists('hashToken')) {
    function hashToken(string $token): string
    {
        return hash('sha256', $token);
    }
}

if (!function_exists('generateToken')) {
    function generateToken(int $length = 32): string
    {
        return bin2hex(random_bytes($length));
    }
}

if (!function_exists('sanitizeUser')) {
    function sanitizeUser(array $user): array
    {
        unset($user['password']);
        return $user;
    }
}

if (!function_exists('getMethod')) {
    function getMethod(): string
    {
        return strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
    }
}

if (!function_exists('getPathSegments')) {
    function getPathSegments(): array
    {
        $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
        return array_values(array_filter(explode('/', trim($uri, '/'))));
    }
}

if (!function_exists('getApiRouteSegments')) {
    function getApiRouteSegments(): array
    {
        $segments = getPathSegments();
        $apiIndex = array_search('api', $segments, true);

        if ($apiIndex === false) {
            return $segments;
        }

        return array_values(array_slice($segments, $apiIndex + 1));
    }
}

if (!function_exists('getClientIp')) {
    function getClientIp(): ?string
    {
        $keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
        foreach ($keys as $key) {
            if (!empty($_SERVER[$key])) {
                $value = explode(',', $_SERVER[$key])[0];
                return trim($value);
            }
        }
        return null;
    }
}

if (!function_exists('getUserAgent')) {
    function getUserAgent(): ?string
    {
        return $_SERVER['HTTP_USER_AGENT'] ?? null;
    }
}

if (!function_exists('normalizeBasePath')) {
    function normalizeBasePath(string $path): string
    {
        $path = trim($path);
        if ($path === '') {
            return '';
        }

        $path = '/' . trim($path, '/');
        return $path === '/' ? '' : $path;
    }
}

if (!function_exists('getAppBasePath')) {
    function getAppBasePath(): string
    {
        $scriptName = str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '');
        $scriptDir = trim(dirname($scriptName), '/.');

        if ($scriptDir === '') {
            return '';
        }

        $segments = explode('/', $scriptDir);
        if (end($segments) === 'api') {
            array_pop($segments);
        }

        return normalizeBasePath(implode('/', $segments));
    }
}

if (!function_exists('buildAppUrl')) {
    function buildAppUrl(string $path, array $config = []): string
    {
        $path = '/' . ltrim($path, '/');
        $configuredBaseUrl = trim((string)($config['app']['base_url'] ?? ''));

        if ($configuredBaseUrl !== '') {
            return rtrim($configuredBaseUrl, '/') . $path;
        }

        $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'] ?? '';
        $basePath = getAppBasePath();

        if ($host === '') {
            return $basePath . $path;
        }

        return $scheme . '://' . $host . $basePath . $path;
    }
}

if (!function_exists('ensureDirectory')) {
    function ensureDirectory(string $directory): void
    {
        if (is_dir($directory)) {
            return;
        }

        if (!mkdir($directory, 0755, true) && !is_dir($directory)) {
            throw new RuntimeException('Failed to create upload directory');
        }
    }
}

if (!function_exists('parseUserAgentDetails')) {
    function parseUserAgentDetails(string $userAgent): array
    {
        $ua = strtolower($userAgent);

        $browser = 'Unknown Browser';
        if (str_contains($ua, 'edg/')) {
            $browser = 'Edge';
        } elseif (str_contains($ua, 'opr/') || str_contains($ua, 'opera')) {
            $browser = 'Opera';
        } elseif (str_contains($ua, 'chrome/') && !str_contains($ua, 'edg/')) {
            $browser = 'Chrome';
        } elseif (str_contains($ua, 'safari/') && !str_contains($ua, 'chrome/')) {
            $browser = 'Safari';
        } elseif (str_contains($ua, 'firefox/')) {
            $browser = 'Firefox';
        } elseif (str_contains($ua, 'msie') || str_contains($ua, 'trident/')) {
            $browser = 'Internet Explorer';
        }

        $platform = 'Unknown Platform';
        if (str_contains($ua, 'windows')) {
            $platform = 'Windows';
        } elseif (str_contains($ua, 'macintosh') || str_contains($ua, 'mac os x')) {
            $platform = 'macOS';
        } elseif (str_contains($ua, 'iphone') || str_contains($ua, 'ipad') || str_contains($ua, 'ios')) {
            $platform = 'iOS';
        } elseif (str_contains($ua, 'android')) {
            $platform = 'Android';
        } elseif (str_contains($ua, 'linux')) {
            $platform = 'Linux';
        }

        $device = 'Desktop';
        if (str_contains($ua, 'ipad') || str_contains($ua, 'tablet')) {
            $device = 'Tablet';
        } elseif (str_contains($ua, 'mobile') || str_contains($ua, 'iphone') || str_contains($ua, 'android')) {
            $device = 'Mobile';
        }

        return [
            'device' => $device,
            'browser' => $browser,
            'platform' => $platform,
        ];
    }
}

if (!function_exists('normalizeBool')) {
    function normalizeBool($value): int
    {
        if (is_bool($value)) {
            return $value ? 1 : 0;
        }

        if (is_numeric($value)) {
            return (int)$value === 1 ? 1 : 0;
        }

        $value = strtolower(trim((string)$value));
        return in_array($value, ['1', 'true', 'yes', 'on'], true) ? 1 : 0;
    }
}

if (!function_exists('encryptMailboxSecret')) {
    function encryptMailboxSecret(string $plainText, array $config): string
    {
        $secret = (string)($config['mailboxes']['secret_key'] ?? '');
        if ($secret === '') {
            throw new RuntimeException('mailboxes.secret_key_missing');
        }

        $key = hash('sha256', $secret, true);
        $cipher = 'AES-256-CBC';
        $ivLength = openssl_cipher_iv_length($cipher);
        $iv = random_bytes($ivLength);

        $encrypted = openssl_encrypt($plainText, $cipher, $key, OPENSSL_RAW_DATA, $iv);
        if ($encrypted === false) {
            throw new RuntimeException('mailboxes.encryption_failed');
        }

        return base64_encode($iv . $encrypted);
    }
}

if (!function_exists('decryptMailboxSecret')) {
    function decryptMailboxSecret(string $encodedCipherText, array $config): string
    {
        $secret = (string)($config['mailboxes']['secret_key'] ?? '');
        if ($secret === '') {
            throw new RuntimeException('mailboxes.secret_key_missing');
        }

        $raw = base64_decode($encodedCipherText, true);
        if ($raw === false) {
            throw new RuntimeException('mailboxes.decryption_failed');
        }

        $key = hash('sha256', $secret, true);
        $cipher = 'AES-256-CBC';
        $ivLength = openssl_cipher_iv_length($cipher);

        if (strlen($raw) <= $ivLength) {
            throw new RuntimeException('mailboxes.decryption_failed');
        }

        $iv = substr($raw, 0, $ivLength);
        $cipherText = substr($raw, $ivLength);

        $decrypted = openssl_decrypt($cipherText, $cipher, $key, OPENSSL_RAW_DATA, $iv);
        if ($decrypted === false) {
            throw new RuntimeException('mailboxes.decryption_failed');
        }

        return $decrypted;
    }
}

if (!function_exists('decodeMimeHeaderValue')) {
    function decodeMimeHeaderValue(?string $value): ?string
    {
        if ($value === null || $value === '') {
            return $value;
        }

        if (function_exists('mb_decode_mimeheader')) {
            $decoded = @mb_decode_mimeheader($value);
            if (is_string($decoded) && $decoded !== '') {
                return $decoded;
            }
        }

        return $value;
    }
}

if (!function_exists('extractAddressEmail')) {
    function extractAddressEmail(?string $value): ?string
    {
        $value = trim((string) $value);
        if ($value === '') {
            return null;
        }

        if (preg_match('/<([^>]+)>/', $value, $matches)) {
            return trim($matches[1]);
        }

        return $value;
    }
}

if (!function_exists('extractAddressName')) {
    function extractAddressName(?string $value): ?string
    {
        $value = trim((string) $value);
        if ($value === '') {
            return null;
        }

        $name = trim((string) preg_replace('/<[^>]+>/', '', $value));
        return $name !== '' ? decodeMimeHeaderValue($name) : null;
    }
}

if (!function_exists('decodeImapPartBody')) {
    function decodeImapPartBody(string $body, int $encoding): string
    {
        return match ($encoding) {
            3 => (string) base64_decode($body, true),
            4 => quoted_printable_decode($body),
            default => $body,
        };
    }
}

if (!function_exists('getPartParameters')) {
    function getPartParameters(object $part): array
    {
        $params = [];

        if (!empty($part->parameters) && is_array($part->parameters)) {
            foreach ($part->parameters as $parameter) {
                $attribute = strtolower((string) ($parameter->attribute ?? ''));
                if ($attribute !== '') {
                    $params[$attribute] = (string) ($parameter->value ?? '');
                }
            }
        }

        if (!empty($part->dparameters) && is_array($part->dparameters)) {
            foreach ($part->dparameters as $parameter) {
                $attribute = strtolower((string) ($parameter->attribute ?? ''));
                if ($attribute !== '') {
                    $params[$attribute] = (string) ($parameter->value ?? '');
                }
            }
        }

        return $params;
    }
}

if (!function_exists('flattenImapParts')) {
    function flattenImapParts(object $structure, string $prefix = ''): array
    {
        $parts = [];
        $currentPartNumber = $prefix === '' ? '1' : $prefix;
        $parts[] = ['part_number' => $currentPartNumber, 'part' => $structure];

        if (!empty($structure->parts) && is_array($structure->parts)) {
            foreach ($structure->parts as $index => $child) {
                $childNumber = $prefix === '' ? (string) ($index + 1) : $prefix . '.' . ($index + 1);
                $parts = array_merge($parts, flattenImapParts($child, $childNumber));
            }
        }

        return $parts;
    }
}

if (!function_exists('saveMailboxBinaryFile')) {
    function saveMailboxBinaryFile(string $directory, string $filename, string $content): array
    {
        ensureDirectory($directory);

        $extension = pathinfo($filename, PATHINFO_EXTENSION);
        $safeBase = preg_replace('/[^a-zA-Z0-9-_]+/', '-', pathinfo($filename, PATHINFO_FILENAME));
        $safeBase = trim((string) $safeBase, '-');
        $safeBase = $safeBase !== '' ? $safeBase : 'file';

        $uniqueName = $safeBase . '-' . bin2hex(random_bytes(6));
        if ($extension !== '') {
            $uniqueName .= '.' . strtolower($extension);
        }

        $filePath = rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $uniqueName;
        file_put_contents($filePath, $content);

        return [
            'storage_path' => $filePath,
            'filename' => $uniqueName,
            'size' => strlen($content),
        ];
    }
}

if (!function_exists('replaceInlineCidSources')) {
    function replaceInlineCidSources(?string $html, array $attachments, int $mailboxId, int $messageId): ?string
    {
        if ($html === null || $html === '' || empty($attachments)) {
            return $html;
        }

        $config = require __DIR__ . '/app.php';

        foreach ($attachments as $attachment) {
            $contentId = trim((string) ($attachment['content_id'] ?? ''));
            if ($contentId === '') {
                continue;
            }

            $contentId = trim($contentId, '<>');
            $publicUrl = $attachment['public_url'] ?? buildAppUrl('/api/Admin/mailboxes/' . $mailboxId . '/messages/' . $messageId . '/attachments/' . ($attachment['id'] ?? 0) . '/download', $config);
            $html = str_replace('cid:' . $contentId, $publicUrl, $html);
        }

        return $html;
    }
}

if (!function_exists('getGuestInboxCookieName')) {
    function getGuestInboxCookieName(array $config): string
    {
        return (string) ($config['guest_inbox']['cookie_name'] ?? 'guest_inbox_token');
    }
}

if (!function_exists('getGuestInboxAccessToken')) {
    function getGuestInboxAccessToken(array $config): ?string
    {
        $headerToken = getHeaderValue('X-Guest-Inbox-Token');
        if (is_string($headerToken) && $headerToken !== '') {
            return $headerToken;
        }

        $input = getJsonInput();
        $bodyToken = trim((string) ($input['guest_access_token'] ?? ''));
        if ($bodyToken !== '') {
            return $bodyToken;
        }

        $queryToken = trim((string) ($_GET['guest_access_token'] ?? ''));
        if ($queryToken !== '') {
            return $queryToken;
        }

        $cookieName = getGuestInboxCookieName($config);
        $token = $_COOKIE[$cookieName] ?? null;
        return is_string($token) && $token !== '' ? $token : null;
    }
}

if (!function_exists('setGuestInboxCookie')) {
    function setGuestInboxCookie(array $config, string $token, ?int $expiresAtTimestamp = null): void
    {
        setcookie(getGuestInboxCookieName($config), $token, [
            'expires' => $expiresAtTimestamp ?? (time() + 7200),
            'path' => '/',
            'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }
}

if (!function_exists('clearGuestInboxCookie')) {
    function clearGuestInboxCookie(array $config): void
    {
        setcookie(getGuestInboxCookieName($config), '', [
            'expires' => time() - 3600,
            'path' => '/',
            'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }
}

if (!function_exists('generateGuestEmailAddress')) {
    function generateGuestEmailAddress(array $config): string
    {
        $prefix = preg_replace('/[^a-z0-9]/i', '', (string) ($config['guest_inbox']['email_prefix'] ?? ''));
        $domain = strtolower(trim((string) ($config['guest_inbox']['email_domain'] ?? 'example.com')));
        return strtolower($prefix . bin2hex(random_bytes(4)) . '@' . $domain);
    }
}



if (!function_exists('getUserInboxDomain')) {
    function getUserInboxDomain(array $config): string
    {
        $domain = strtolower(trim((string) ($config['user_inbox']['email_domain'] ?? $config['guest_inbox']['email_domain'] ?? 'example.com')));
        $domain = preg_replace('/[^a-z0-9.-]/i', '', $domain) ?: 'example.com';
        return $domain;
    }
}

if (!function_exists('normalizeUsername')) {
    function normalizeUsername(?string $username): string
    {
        $username = strtolower(trim((string) $username));
        if (str_contains($username, '@')) {
            $username = explode('@', $username, 2)[0];
        }
        $username = preg_replace('/[^a-z0-9._-]/', '', $username) ?? '';
        return trim($username, '._-');
    }
}

if (!function_exists('isValidInboxUsername')) {
    function isValidInboxUsername(string $username): bool
    {
        $length = strlen($username);
        if ($length < 3 || $length > 32) {
            return false;
        }

        return (bool) preg_match('/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])$/', $username);
    }
}

if (!function_exists('buildUserInboxEmail')) {
    function buildUserInboxEmail(string $username, array $config): string
    {
        return normalizeUsername($username) . '@' . getUserInboxDomain($config);
    }
}

if (!function_exists('extractFirstEmailAddress')) {
    function extractFirstEmailAddress(?string $value): ?string
    {
        $value = decodeMimeHeaderValue((string) $value) ?? '';
        $value = trim($value);
        if ($value === '') {
            return null;
        }

        if (preg_match('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i', $value, $matches)) {
            return strtolower(trim($matches[0]));
        }

        return null;
    }
}

if (!function_exists('extractHeaderValue')) {
    function extractHeaderValue(string $headers, string $headerName): ?string
    {
        $pattern = '/^' . preg_quote($headerName, '/') . '\s*:\s*(.+(?:\r?\n[ \t].+)*)/im';
        if (!preg_match($pattern, $headers, $matches)) {
            return null;
        }

        $value = preg_replace('/\r?\n[ \t]+/', ' ', $matches[1]);
        $value = trim((string) $value);
        return $value !== '' ? $value : null;
    }
}

if (!function_exists('extractOriginalRecipientEmail')) {
    function extractOriginalRecipientEmail(string $headers, ?string $overviewTo = null): ?string
    {
        // For catch-all mailboxes, Delivered-To is usually the real IMAP/cPanel
        // mailbox address (example: all@domain.com), not the original recipient.
        // Prefer original/envelope recipient headers first so guest/user inbox matching
        // uses the address the sender actually typed.
        $priorityHeaders = [
            'X-Original-To',
            'Envelope-To',
            'X-Envelope-To',
            'Original-Recipient',
            'Final-Recipient',
            'Apparently-To',
            'To',
            'Delivered-To',
        ];

        foreach ($priorityHeaders as $headerName) {
            $value = extractHeaderValue($headers, $headerName);
            $email = extractFirstEmailAddress($value);
            if ($email) {
                return $email;
            }
        }

        return extractFirstEmailAddress($overviewTo);
    }
}

if (!function_exists('normalizeEmailAddress')) {
    function normalizeEmailAddress(?string $email): ?string
    {
        if ($email === null) {
            return null;
        }

        $email = strtolower(trim($email));
        return $email !== '' ? $email : null;
    }
}
