<?php

namespace App\Http\Controllers\Api\Mobile;

use App\Exceptions\PlanLimitReachedException;
use App\Http\Controllers\Controller;
use App\Models\EmailAccount;
use App\Models\Workspace;
use App\Services\PlanLimitService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Webklex\PHPIMAP\ClientManager;

/**
 * Mobile Email Accounts endpoints — powers the "+ Add another account" flow
 * from the MailTrixy accounts bottom sheet, plus full CRUD on connected
 * mailboxes.
 *
 * Two ways to connect a new mailbox (same as the web):
 *
 *   1. OAuth (Gmail / Outlook) — POST /email-accounts/oauth/{provider}
 *      Server creates a placeholder EmailAccount row and returns a one-time
 *      OAuth start URL. Mobile opens the URL in a browser tab; the callback
 *      finishes account setup and the next /email-accounts call shows it.
 *
 *   2. IMAP / SMTP — POST /email-accounts with the preset key + credentials.
 *      The controller validates, runs a live IMAP connection test, then
 *      creates the account.
 *
 * Presets (Gmail App Password, Outlook IMAP, Yahoo, Zoho, iCloud, AOL,
 * ProtonMail Bridge, Fastmail, GMX, Yandex, Mail.ru, custom) come from
 * config('email_providers.presets').
 */
class EmailAccountsController extends Controller
{
    /**
     * GET /api/v1/mobile/email-accounts
     */
    public function index(Request $request): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;
        if (!$workspaceId) {
            return response()->json(['data' => []]);
        }

        $rows = EmailAccount::where('workspace_id', $workspaceId)
            ->orderByDesc('is_default')
            ->orderBy('id')
            ->get(['id', 'email', 'display_name', 'provider', 'status', 'is_default', 'ai_auto_reply', 'last_synced_at', 'error_message']);

        return response()->json([
            'data' => $rows->map(fn (EmailAccount $a) => $this->shape($a))->values()->all(),
        ]);
    }

    /**
     * GET /api/v1/mobile/email-accounts/{id}
     */
    public function show(Request $request, int $id): JsonResponse
    {
        $user = $request->user();
        $account = EmailAccount::where('workspace_id', $user->active_workspace_id)->find($id);
        if (!$account) {
            return response()->json(['message' => 'Email account not found.'], 404);
        }
        return response()->json(['email_account' => $this->shape($account)]);
    }

    /**
     * GET /api/v1/mobile/email-accounts/providers
     * Returns the IMAP/SMTP preset list for the "Add another account" picker.
     */
    public function providers(Request $request): JsonResponse
    {
        $presets = config('email_providers.presets', []);

        $items = [];
        foreach ($presets as $key => $preset) {
            $items[] = [
                'key'             => $key,
                'label'           => $preset['label'] ?? ucfirst($key),
                'description'     => $preset['description'] ?? null,
                'icon'            => $preset['icon'] ?? 'mail',
                'imap_host'       => $preset['imap_host'] ?? '',
                'imap_port'       => (int) ($preset['imap_port'] ?? 993),
                'imap_encryption' => $preset['imap_encryption'] ?? 'ssl',
                'smtp_host'       => $preset['smtp_host'] ?? '',
                'smtp_port'       => (int) ($preset['smtp_port'] ?? 587),
                'smtp_encryption' => $preset['smtp_encryption'] ?? 'tls',
                'help_text'       => $preset['help_text'] ?? null,
                'help_url'        => $preset['help_url'] ?? null,
            ];
        }

        return response()->json([
            'oauth' => [
                ['key' => 'gmail',   'label' => 'Gmail (OAuth)',   'available' => (bool) config('services.google.client_id')],
                ['key' => 'outlook', 'label' => 'Outlook (OAuth)', 'available' => (bool) config('services.microsoft.client_id')],
            ],
            'imap_smtp_presets' => $items,
        ]);
    }

    /**
     * POST /api/v1/mobile/email-accounts
     * Connect a new IMAP/SMTP mailbox. Validates the connection live before
     * persisting so the user doesn't end up with a broken account row.
     */
    public function store(Request $request): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;
        if (!$workspaceId) {
            return response()->json(['message' => 'No active workspace.'], 422);
        }

        $role = $user->workspaceRole($workspaceId);
        if (!in_array($role, ['owner', 'admin'], true)) {
            return response()->json(['message' => 'Only owners and admins can connect mailboxes.'], 403);
        }

        $validator = Validator::make($request->all(), [
            'email'           => ['required', 'email', 'max:255'],
            'display_name'    => ['nullable', 'string', 'max:255'],
            'preset'          => ['sometimes', 'string', 'max:50'],
            'imap_host'       => ['required', 'string', 'max:255'],
            'imap_port'       => ['required', 'integer', 'min:1', 'max:65535'],
            'imap_username'   => ['required', 'string', 'max:255'],
            'imap_password'   => ['required', 'string', 'max:255'],
            'imap_encryption' => ['required', 'string', 'in:ssl,tls,starttls,none'],
            'smtp_host'       => ['nullable', 'string', 'max:255'],
            'smtp_port'       => ['nullable', 'integer', 'min:1', 'max:65535'],
            'smtp_username'   => ['nullable', 'string', 'max:255'],
            'smtp_password'   => ['nullable', 'string', 'max:255'],
            'smtp_encryption' => ['nullable', 'string', 'in:ssl,tls,starttls,none'],
            'skip_test'       => ['sometimes', 'boolean'],
        ]);
        if ($validator->fails()) {
            return response()->json(['errors' => $validator->errors()], 422);
        }

        // Plan limit
        try {
            $workspace = Workspace::findOrFail($workspaceId);
            app(PlanLimitService::class)->assertCanCreate($workspace, 'email_accounts');
        } catch (PlanLimitReachedException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        // Duplicate email per workspace
        $duplicate = EmailAccount::where('workspace_id', $workspaceId)
            ->where('email', $request->input('email'))
            ->exists();
        if ($duplicate) {
            return response()->json([
                'message' => 'This email is already connected to the workspace.',
                'errors'  => ['email' => ['Already connected.']],
            ], 422);
        }

        // Live IMAP connection test (skippable for advanced cases / proxies)
        if (!$request->boolean('skip_test')) {
            $err = $this->testImapConnection(
                (string) $request->input('imap_host'),
                (int) $request->input('imap_port'),
                (string) $request->input('imap_username'),
                (string) $request->input('imap_password'),
                (string) $request->input('imap_encryption'),
            );
            if ($err !== null) {
                $hint = $this->hintForImapFailure(
                    $err,
                    (string) $request->input('preset', ''),
                    (string) $request->input('imap_username'),
                    (string) $request->input('email'),
                );

                return response()->json([
                    'message' => 'IMAP connection failed: ' . $err,
                    'reason'  => $this->classifyImapError($err),
                    'hint'    => $hint,
                    'errors'  => ['imap_password' => [$hint ?: 'Could not authenticate against IMAP server.']],
                ], 422);
            }
        }

        $isFirst = !EmailAccount::where('workspace_id', $workspaceId)->exists();

        $account = EmailAccount::create([
            'workspace_id'    => $workspaceId,
            'user_id'         => $user->id,
            'email'           => $request->input('email'),
            'display_name'    => $request->input('display_name') ?: null,
            'provider'        => 'imap',
            'imap_host'       => $request->input('imap_host'),
            'imap_port'       => (int) $request->input('imap_port'),
            'imap_username'   => $request->input('imap_username'),
            'imap_password'   => $request->input('imap_password'),
            'imap_encryption' => $request->input('imap_encryption'),
            'smtp_host'       => $request->input('smtp_host') ?: null,
            'smtp_port'       => $request->input('smtp_port') ?: null,
            'smtp_username'   => $request->input('smtp_username') ?: null,
            'smtp_password'   => $request->input('smtp_password') ?: null,
            'smtp_encryption' => $request->input('smtp_encryption') ?: null,
            'status'          => 'connected',
            'is_default'      => $isFirst,
            'ai_auto_reply'   => false,
        ]);

        return response()->json([
            'message'       => 'Email account connected.',
            'email_account' => $this->shape($account),
        ], 201);
    }

    /**
     * POST /api/v1/mobile/email-accounts/oauth/{provider}
     * Returns the OAuth start URL the mobile app should open in a browser
     * tab. Server creates the placeholder account first so the callback can
     * link back to it.
     */
    public function startOAuth(Request $request, string $provider): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;
        if (!$workspaceId) {
            return response()->json(['message' => 'No active workspace.'], 422);
        }

        $role = $user->workspaceRole($workspaceId);
        if (!in_array($role, ['owner', 'admin'], true)) {
            return response()->json(['message' => 'Only owners and admins can connect mailboxes.'], 403);
        }

        if (!in_array($provider, ['gmail', 'outlook'], true)) {
            return response()->json([
                'message' => 'Unsupported OAuth provider.',
                'errors'  => ['provider' => ['Allowed: gmail, outlook.']],
            ], 422);
        }

        if ($provider === 'gmail' && !config('services.google.client_id')) {
            return response()->json([
                'message' => 'Google OAuth is not configured. Use the IMAP option with an App Password instead.',
            ], 422);
        }
        if ($provider === 'outlook' && !config('services.microsoft.client_id')) {
            return response()->json([
                'message' => 'Microsoft OAuth is not configured. Use the IMAP option instead.',
            ], 422);
        }

        try {
            $workspace = Workspace::findOrFail($workspaceId);
            app(PlanLimitService::class)->assertCanCreate($workspace, 'email_accounts');
        } catch (PlanLimitReachedException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $account = EmailAccount::create([
            'workspace_id' => $workspaceId,
            'user_id'      => $user->id,
            'email'        => $user->email,
            'display_name' => $user->name,
            'provider'     => $provider,
            'status'       => 'disconnected',
            'is_default'   => !EmailAccount::where('workspace_id', $workspaceId)->where('is_default', true)->exists(),
        ]);

        $routeName = $provider === 'gmail' ? 'email-oauth.google' : 'email-oauth.microsoft';
        $url = route($routeName, ['account_id' => $account->id]);

        return response()->json([
            'message'       => "Open the OAuth URL in a browser to finish connecting your {$provider} mailbox.",
            'oauth_url'     => $url,
            'account_id'    => $account->id,
            'expires_in'    => 600,
            'note'          => 'After completing OAuth in the browser, refresh GET /email-accounts to see the connected mailbox.',
        ]);
    }

    /**
     * POST /api/v1/mobile/email-accounts/oauth/{provider}/exchange
     *
     * Native mobile OAuth — the Flutter app drives the Google / Microsoft
     * consent flow itself (via google_sign_in / msal_flutter SDKs) and
     * hands us the resulting tokens. Skips the clunky browser-bounce that
     * startOAuth() requires.
     *
     * Body:
     *   - access_token   (required) — the Bearer token from the device SDK
     *   - refresh_token  (optional) — only Gmail returns one on first consent;
     *                                  without it the mailbox will disconnect
     *                                  when the 1-hour access_token expires
     *   - expires_in     (optional) — seconds until access_token expiry;
     *                                  defaults to 3600 if absent
     *   - id_token       (optional) — ignored on server, app may pass it
     *
     * Returns the connected EmailAccount JSON ready for the next /index call.
     */
    public function exchangeOAuth(Request $request, string $provider): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;
        if (!$workspaceId) {
            return response()->json(['message' => 'No active workspace.'], 422);
        }

        $role = $user->workspaceRole($workspaceId);
        if (!in_array($role, ['owner', 'admin'], true)) {
            return response()->json(['message' => 'Only owners and admins can connect mailboxes.'], 403);
        }

        if (!in_array($provider, ['gmail', 'outlook'], true)) {
            return response()->json([
                'message' => 'Unsupported OAuth provider.',
                'errors'  => ['provider' => ['Allowed: gmail, outlook.']],
            ], 422);
        }

        $data = Validator::make($request->all(), [
            'access_token'  => ['required', 'string'],
            'refresh_token' => ['nullable', 'string'],
            'expires_in'    => ['nullable', 'integer', 'min:0', 'max:86400'],
        ])->validate();

        // Verify the access_token AND fetch the connected email address by
        // calling the provider's userinfo endpoint. If the token is bad we
        // get an HTTP error from Google/Microsoft instead of silently
        // saving a non-working mailbox.
        try {
            [$email, $displayName] = $this->fetchOAuthProfile($provider, $data['access_token']);
        } catch (\Throwable $e) {
            Log::warning('Mobile OAuth exchange: token verify failed', [
                'provider' => $provider,
                'error'    => $e->getMessage(),
            ]);
            return response()->json([
                'message' => "Could not verify {$provider} access token. Please sign in again on the device.",
            ], 401);
        }

        if (!$email) {
            return response()->json([
                'message' => "{$provider} did not return an email address. Make sure the SDK requested the `email` scope.",
            ], 422);
        }

        try {
            $workspace = Workspace::findOrFail($workspaceId);
            app(PlanLimitService::class)->assertCanCreate($workspace, 'email_accounts');
        } catch (PlanLimitReachedException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $expiresAt = now()->addSeconds((int) ($data['expires_in'] ?? 3600));

        // Re-connect an existing row when the same email + workspace already
        // exists, otherwise create. Avoids "duplicate mailbox" pile-up when
        // a user re-runs the connect flow to refresh tokens.
        $account = EmailAccount::firstOrNew([
            'workspace_id' => $workspaceId,
            'email'        => strtolower($email),
        ]);

        $account->fill([
            'user_id'                => $user->id,
            'display_name'           => $account->display_name ?: ($displayName ?: $user->name),
            'provider'               => $provider,
            'oauth_token'            => $data['access_token'],
            'oauth_token_expires_at' => $expiresAt,
            'status'                 => 'connected',
        ]);

        // Only overwrite refresh_token when the device sent one — Google
        // omits it on subsequent consents and we don't want to clobber the
        // working one that's already saved.
        if (!empty($data['refresh_token'])) {
            $account->oauth_refresh_token = $data['refresh_token'];
        }

        if (!$account->exists) {
            $account->is_default = !EmailAccount::where('workspace_id', $workspaceId)
                ->where('is_default', true)
                ->exists();
        }

        $account->save();

        // Kick off the initial mailbox backfill on the email-sync queue.
        try {
            \App\Jobs\SyncEmailAccountJob::dispatch($account);
        } catch (\Throwable $e) {
            Log::warning('Mobile OAuth exchange: initial sync dispatch failed', [
                'account_id' => $account->id,
                'error'      => $e->getMessage(),
            ]);
        }

        return response()->json([
            'message' => 'Mailbox connected.',
            'account' => [
                'id'           => $account->id,
                'email'        => $account->email,
                'display_name' => $account->display_name,
                'provider'     => $account->provider,
                'status'       => $account->status,
                'is_default'   => (bool) $account->is_default,
            ],
        ]);
    }

    /**
     * Call the provider's userinfo endpoint to (a) prove the access_token
     * is valid and (b) discover which mailbox we're about to connect.
     *
     * @return array{0: string|null, 1: string|null} [email, displayName]
     */
    private function fetchOAuthProfile(string $provider, string $accessToken): array
    {
        if ($provider === 'gmail') {
            $resp = Http::withToken($accessToken)
                ->timeout(10)
                ->get('https://www.googleapis.com/oauth2/v2/userinfo');
            if (!$resp->successful()) {
                throw new \RuntimeException("Google userinfo HTTP {$resp->status()}: " . $resp->body());
            }
            return [$resp->json('email'), $resp->json('name')];
        }

        // Microsoft Graph for Outlook / Microsoft 365
        $resp = Http::withToken($accessToken)
            ->timeout(10)
            ->get('https://graph.microsoft.com/v1.0/me');
        if (!$resp->successful()) {
            throw new \RuntimeException("Microsoft Graph HTTP {$resp->status()}: " . $resp->body());
        }
        $email = $resp->json('mail') ?: $resp->json('userPrincipalName');
        return [$email, $resp->json('displayName')];
    }

    /**
     * POST /api/v1/mobile/email-accounts/test-connection
     * Live-validate IMAP credentials without creating an account row.
     */
    public function testConnection(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'imap_host'       => ['required', 'string'],
            'imap_port'       => ['required', 'integer'],
            'imap_username'   => ['required', 'string'],
            'imap_password'   => ['required', 'string'],
            'imap_encryption' => ['required', 'string', 'in:ssl,tls,starttls,none'],
        ]);
        if ($validator->fails()) {
            return response()->json(['errors' => $validator->errors()], 422);
        }

        $err = $this->testImapConnection(
            (string) $request->input('imap_host'),
            (int) $request->input('imap_port'),
            (string) $request->input('imap_username'),
            (string) $request->input('imap_password'),
            (string) $request->input('imap_encryption'),
        );

        return $err === null
            ? response()->json(['status' => 'success', 'message' => 'IMAP connection successful.'])
            : response()->json(['status' => 'error', 'message' => $err], 422);
    }

    /**
     * PUT /api/v1/mobile/email-accounts/{id}
     */
    public function update(Request $request, int $id): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;

        $account = EmailAccount::where('workspace_id', $workspaceId)->find($id);
        if (!$account) {
            return response()->json(['message' => 'Email account not found.'], 404);
        }

        $validator = Validator::make($request->all(), [
            'display_name'    => ['nullable', 'string', 'max:255'],
            'imap_host'       => ['sometimes', 'string', 'max:255'],
            'imap_port'       => ['sometimes', 'integer', 'min:1', 'max:65535'],
            'imap_username'   => ['sometimes', 'string', 'max:255'],
            'imap_password'   => ['sometimes', 'string', 'max:255'],
            'imap_encryption' => ['sometimes', 'string', 'in:ssl,tls,starttls,none'],
            'smtp_host'       => ['nullable', 'string', 'max:255'],
            'smtp_port'       => ['nullable', 'integer', 'min:1', 'max:65535'],
            'smtp_username'   => ['nullable', 'string', 'max:255'],
            'smtp_password'   => ['nullable', 'string', 'max:255'],
            'smtp_encryption' => ['nullable', 'string', 'in:ssl,tls,starttls,none'],
            'ai_auto_reply'   => ['sometimes', 'boolean'],
            'is_default'      => ['sometimes', 'boolean'],
        ]);
        if ($validator->fails()) {
            return response()->json(['errors' => $validator->errors()], 422);
        }

        $data = $validator->validated();
        // Don't clobber the password with an empty value on edits.
        foreach (['imap_password', 'smtp_password'] as $secret) {
            if (array_key_exists($secret, $data) && ($data[$secret] === null || $data[$secret] === '')) {
                unset($data[$secret]);
            }
        }

        if ($request->boolean('is_default')) {
            // Only one default per workspace.
            EmailAccount::where('workspace_id', $workspaceId)->update(['is_default' => false]);
            $data['is_default'] = true;
        }

        $account->fill($data);
        $account->save();

        return response()->json([
            'message'       => 'Email account updated.',
            'email_account' => $this->shape($account->fresh()),
        ]);
    }

    /**
     * DELETE /api/v1/mobile/email-accounts/{id}
     */
    public function destroy(Request $request, int $id): JsonResponse
    {
        $user = $request->user();
        $workspaceId = $user->active_workspace_id;

        $account = EmailAccount::where('workspace_id', $workspaceId)->find($id);
        if (!$account) {
            return response()->json(['message' => 'Email account not found.'], 404);
        }

        $role = $user->workspaceRole($workspaceId);
        if (!in_array($role, ['owner', 'admin'], true)) {
            return response()->json(['message' => 'Only owners and admins can disconnect mailboxes.'], 403);
        }

        $account->delete();
        return response()->json(['message' => 'Email account disconnected.']);
    }

    // ── Helpers ──────────────────────────────────────────────────────

    /**
     * Returns null on success, an error string on failure.
     */
    private function testImapConnection(string $host, int $port, string $user, string $password, string $encryption): ?string
    {
        try {
            $cm = new ClientManager();
            $client = $cm->make([
                'host'          => $host,
                'port'          => $port,
                'encryption'    => $encryption ?: 'ssl',
                'validate_cert' => false,
                'timeout'       => 30,
                'username'      => $user,
                'password'      => $password,
                'protocol'      => 'imap',
            ]);
            $client->connect();
            $client->disconnect();
            return null;
        } catch (\Throwable $e) {
            return $e->getMessage();
        }
    }

    /**
     * Classify a raw IMAP failure string into a stable machine-readable code
     * the mobile app can switch on for UI ("show retry", "show app-password
     * help", "show network error", etc.).
     */
    private function classifyImapError(string $err): string
    {
        $lower = strtolower($err);
        return match (true) {
            str_contains($lower, 'authenticationfailed') ||
            str_contains($lower, 'invalid credentials') ||
            str_contains($lower, 'authentication failed')      => 'invalid_credentials',
            str_contains($lower, 'application-specific') ||
            str_contains($lower, 'application specific')       => 'needs_app_password',
            str_contains($lower, 'connection refused') ||
            str_contains($lower, 'could not connect') ||
            str_contains($lower, 'connection timed out')       => 'connection_refused',
            str_contains($lower, 'name or service not known') ||
            str_contains($lower, 'could not resolve')          => 'dns_failure',
            str_contains($lower, 'certificate') ||
            str_contains($lower, 'ssl') ||
            str_contains($lower, 'tls')                        => 'tls_error',
            default                                            => 'imap_error',
        };
    }

    /**
     * Build a preset-aware, human-readable hint string for the most common
     * IMAP auth failures. Returns null if no specific hint applies.
     */
    private function hintForImapFailure(string $err, string $preset, string $imapUsername, string $email): ?string
    {
        $reason = $this->classifyImapError($err);

        // Username/email mismatch is a frequent cause across all providers:
        // users type just "k" or their first name instead of the full address.
        $usernameLooksWrong = $imapUsername !== ''
            && strcasecmp($imapUsername, $email) !== 0
            && !filter_var($imapUsername, FILTER_VALIDATE_EMAIL);

        if ($reason === 'invalid_credentials') {
            $hints = [];
            if ($usernameLooksWrong) {
                $hints[] = "Username must be the full email address (e.g. '{$email}'), not '{$imapUsername}'.";
            }

            switch ($preset) {
                case 'gmail_imap':
                    $hints[] = 'Gmail requires an App Password — your regular Google password will not work. Generate one at https://myaccount.google.com/apppasswords (you must have 2-Step Verification enabled first).';
                    break;
                case 'outlook_imap':
                    $hints[] = 'Outlook / Microsoft 365 IMAP requires basic-auth to be enabled by the tenant admin, or an App Password from account.microsoft.com.';
                    break;
                case 'yahoo':
                case 'aol':
                case 'fastmail':
                case 'gmx':
                case 'yandex':
                case 'mailru':
                    $hints[] = ucfirst($preset) . ' requires an App Password generated in the account security settings — your regular password will not work.';
                    break;
                case 'icloud':
                    $hints[] = 'iCloud requires an app-specific password from appleid.apple.com — your Apple ID password will not work.';
                    break;
                case 'zoho':
                    $hints[] = 'Zoho requires IMAP to be enabled in Zoho Mail settings, and an App Password if 2FA is on.';
                    break;
                case 'protonmail':
                    $hints[] = 'ProtonMail requires the Bridge app running locally and the username/password it shows — not your ProtonMail login.';
                    break;
            }

            return $hints ? implode(' ', $hints) : 'Could not authenticate against IMAP server.';
        }

        if ($reason === 'needs_app_password') {
            return 'This account requires an App Password (not the regular login password). Generate one in the provider\'s account security settings.';
        }
        if ($reason === 'connection_refused') {
            return 'Could not reach the IMAP server. Check the host / port and that your network allows outbound connections.';
        }
        if ($reason === 'dns_failure') {
            return 'The IMAP host could not be resolved. Double-check the host name.';
        }
        if ($reason === 'tls_error') {
            return 'TLS / SSL handshake with the IMAP server failed. Verify the encryption setting (ssl vs tls) matches the port (993 = ssl, 143 = starttls).';
        }

        return null;
    }

    private function shape(EmailAccount $a): array
    {
        return [
            'id'             => $a->id,
            'email'          => $a->email,
            'display_name'   => $a->display_name,
            'name'           => $a->display_name ?: $a->email,
            'provider'       => $a->provider,
            'status'         => $a->status,
            'is_connected'   => $a->status === 'connected',
            'is_default'     => (bool) $a->is_default,
            'ai_auto_reply'  => (bool) ($a->ai_auto_reply ?? false),
            'last_synced_at' => $a->last_synced_at?->toIso8601String(),
            'error_message'  => $a->error_message,
        ];
    }
}
