import { buildUrl, toServiceException } from "../_util.js";
import { safeStringify } from "../../json.js";
import { z } from 'zod';
/**
 * An email address registered with the Agentuity email service.
 *
 * Email addresses are created under the `@agentuity.email` domain and can receive
 * inbound emails (forwarded to configured destinations) and send outbound emails.
 */
export const EmailAddressSchema = z.object({
    /**
     * Unique identifier for the email address.
     *
     * @remarks Prefixed with `eaddr_`.
     */
    id: z.string().describe('Unique identifier for the email address.'),
    /**
     * The full email address (e.g., `support@agentuity.email`).
     */
    email: z.string().describe('The full email address (e.g., `support@agentuity.email`).'),
    /**
     * Provider-specific configuration (e.g., inbound routing config).
     *
     * @remarks Opaque to callers — the structure is managed by the platform.
     */
    config: z
        .record(z.string(), z.unknown())
        .optional()
        .describe('Provider-specific configuration (e.g., inbound routing config).'),
    /**
     * ID of the user who registered this address.
     */
    created_by: z.string().optional().describe('ID of the user who registered this address.'),
    /**
     * ISO 8601 timestamp when the address was created.
     */
    created_at: z.string().describe('ISO 8601 timestamp when the address was created.'),
    /**
     * ISO 8601 timestamp when the address was last updated.
     */
    updated_at: z
        .string()
        .optional()
        .describe('ISO 8601 timestamp when the address was last updated.'),
    /**
     * Total number of inbound emails received at this address.
     */
    inbound_count: z
        .number()
        .optional()
        .describe('Total number of inbound emails received at this address.'),
    /**
     * Total number of outbound emails sent from this address.
     */
    outbound_count: z
        .number()
        .optional()
        .describe('Total number of outbound emails sent from this address.'),
    /**
     * ISO 8601 timestamp of the most recent inbound or outbound email activity.
     */
    last_activity: z
        .string()
        .optional()
        .describe('ISO 8601 timestamp of the most recent inbound or outbound email activity.'),
});
/**
 * A destination configuration for an email address.
 *
 * When an inbound email is received at the parent address, the platform forwards
 * it to each configured destination via an HTTP request.
 */
export const EmailDestinationSchema = z.object({
    /**
     * Unique identifier for the destination.
     *
     * @remarks Prefixed with `edst_`.
     */
    id: z.string().describe('Unique identifier for the destination.'),
    /**
     * The destination type. Currently only `'url'` is supported.
     */
    type: z.string().describe("The destination type. Currently only `'url'` is supported."),
    /**
     * Destination-specific configuration.
     *
     * @remarks
     * For `'url'` type the shape is:
     * ```typescript
     * {
     *   url: string;             // Must use http or https; must not point to private/loopback addresses
     *   headers?: Record<string, string>;
     *   method?: 'POST' | 'PUT' | 'PATCH';
     * }
     * ```
     */
    config: z
        .record(z.string(), z.unknown())
        .optional()
        .describe('Destination-specific configuration.'),
    /**
     * ISO 8601 timestamp when the destination was created.
     */
    created_at: z.string().describe('ISO 8601 timestamp when the destination was created.'),
    /**
     * ISO 8601 timestamp when the destination was last updated.
     */
    updated_at: z
        .string()
        .optional()
        .describe('ISO 8601 timestamp when the destination was last updated.'),
});
/**
 * Connection settings for an email protocol (IMAP or POP3).
 *
 * Used to configure a mail client for accessing an Agentuity email address
 * via standard mail protocols.
 */
export const EmailProtocolConfigSchema = z.object({
    /**
     * The mail server hostname.
     */
    host: z.string().describe('The mail server hostname.'),
    /**
     * The mail server port number.
     */
    port: z.number().describe('The mail server port number.'),
    /**
     * TLS mode (e.g., `'starttls'`, `'ssl'`, `'none'`).
     */
    tls: z.string().describe("TLS mode (e.g., `'starttls'`, `'ssl'`, `'none'`)."),
    /**
     * The authentication username (typically the address ID).
     */
    username: z.string().describe('The authentication username (typically the address ID).'),
    /**
     * The authentication password.
     */
    password: z.string().describe('The authentication password.'),
});
/**
 * Full connection configuration for accessing an email address via IMAP and POP3 protocols.
 *
 * Returned by {@link EmailService.getConnectionConfig} to allow external mail clients
 * to connect to an Agentuity email address.
 */
export const EmailConnectionConfigSchema = z.object({
    /**
     * The full email address these settings are for.
     */
    email: z.string().describe('The full email address these settings are for.'),
    /**
     * IMAP protocol connection settings.
     */
    imap: EmailProtocolConfigSchema.describe('IMAP protocol connection settings.'),
    /**
     * POP3 protocol connection settings.
     */
    pop3: EmailProtocolConfigSchema.describe('POP3 protocol connection settings.'),
});
/**
 * An inbound email message received at an Agentuity email address.
 */
export const EmailInboundSchema = z.object({
    /**
     * Unique identifier for the inbound email.
     *
     * @remarks Prefixed with `einb_`.
     */
    id: z.string().describe('Unique identifier for the inbound email.'),
    /**
     * The sender's email address.
     */
    from: z.string().describe("The sender's email address."),
    /**
     * The recipient email address (comma-separated if multiple).
     */
    to: z.string().describe('The recipient email address (comma-separated if multiple).'),
    /**
     * The email subject line.
     */
    subject: z.string().optional().describe('The email subject line.'),
    /**
     * Plain text body of the email.
     */
    text: z.string().optional().describe('Plain text body of the email.'),
    /**
     * HTML body of the email.
     */
    html: z.string().optional().describe('HTML body of the email.'),
    /**
     * ISO 8601 timestamp when the email was received.
     */
    received_at: z.string().optional().describe('ISO 8601 timestamp when the email was received.'),
    /**
     * Raw email headers as key-value pairs.
     */
    headers: z
        .record(z.string(), z.unknown())
        .optional()
        .describe('Raw email headers as key-value pairs.'),
    /**
     * Array of stored attachment metadata with S3 locations.
     */
    attachments: z
        .array(z.lazy(() => EmailStoredAttachmentSchema))
        .optional()
        .describe('Array of stored attachment metadata with S3 locations.'),
});
/**
 * An outbound email message sent from an Agentuity email address.
 */
export const EmailOutboundSchema = z.object({
    /**
     * Unique identifier for the outbound email.
     *
     * @remarks Prefixed with `eout_`.
     */
    id: z.string().describe('Unique identifier for the outbound email.'),
    /**
     * The sender's email address (must be owned by the organization).
     */
    from: z.string().describe("The sender's email address (must be owned by the organization)."),
    /**
     * The recipient email addresses (comma-separated).
     */
    to: z.string().describe('The recipient email addresses (comma-separated).'),
    /**
     * The email subject line.
     */
    subject: z.string().optional().describe('The email subject line.'),
    /**
     * Plain text body of the email.
     */
    text: z.string().optional().describe('Plain text body of the email.'),
    /**
     * HTML body of the email.
     */
    html: z.string().optional().describe('HTML body of the email.'),
    /**
     * Delivery status: `'pending'`, `'success'`, or `'failed'`.
     *
     * @remarks Emails are sent asynchronously, so the initial status is always `'pending'`.
     */
    status: z
        .string()
        .optional()
        .describe("Delivery status: `'pending'`, `'success'`, or `'failed'`."),
    /**
     * Error message if the delivery failed.
     */
    error: z.string().optional().describe('Error message if the delivery failed.'),
    /**
     * ISO 8601 timestamp when the send was initiated.
     */
    created_at: z.string().optional().describe('ISO 8601 timestamp when the send was initiated.'),
    /**
     * Custom email headers that were included.
     */
    headers: z
        .record(z.string(), z.unknown())
        .optional()
        .describe('Custom email headers that were included.'),
    /**
     * Array of stored attachment metadata with S3 locations.
     */
    attachments: z
        .array(z.lazy(() => EmailStoredAttachmentSchema))
        .optional()
        .describe('Array of stored attachment metadata with S3 locations.'),
});
/**
 * An email attachment to include when sending an outbound email.
 */
export const EmailAttachmentSchema = z.object({
    /**
     * The filename for the attachment
     */
    filename: z.string().describe('The filename for the attachment'),
    /**
     * The base64-encoded content of the attachment
     */
    content: z.string().describe('The base64-encoded content of the attachment'),
    /**
     * The MIME content type of the attachment
     */
    contentType: z.string().optional().describe('The MIME content type of the attachment'),
});
/**
 * A stored email attachment with S3 location metadata.
 * Returned by inbound/outbound email queries — different from EmailAttachment used for sending.
 */
export const EmailStoredAttachmentSchema = z.object({
    /** The original filename */
    filename: z.string().describe('The original filename'),
    /** The MIME content type */
    content_type: z.string().optional().describe('The MIME content type'),
    /** File size in bytes */
    size: z.number().describe('File size in bytes'),
    /** The S3 bucket name where the attachment is stored */
    bucket: z.string().describe('The S3 bucket name where the attachment is stored'),
    /** The S3 object key */
    key: z.string().describe('The S3 object key'),
    /** Optional pre-signed download URL */
    url: z.string().optional().describe('Optional pre-signed download URL'),
});
/**
 * Parameters for sending an email
 */
export const EmailSendParamsSchema = z.object({
    /**
     * The sender email address (must be owned by the organization)
     */
    from: z.string().describe('The sender email address (must be owned by the organization)'),
    /**
     * The recipient email addresses
     */
    to: z.array(z.string()).describe('The recipient email addresses'),
    /**
     * The email subject
     */
    subject: z.string().describe('The email subject'),
    /**
     * Plain text email body
     */
    text: z.string().optional().describe('Plain text email body'),
    /**
     * HTML email body
     */
    html: z.string().optional().describe('HTML email body'),
    /**
     * File attachments
     */
    attachments: z.array(EmailAttachmentSchema).optional().describe('File attachments'),
    /**
     * Custom email headers (e.g., In-Reply-To, References for threading)
     */
    headers: z
        .record(z.string(), z.string())
        .optional()
        .describe('Custom email headers (e.g., In-Reply-To, References for threading)'),
});
/**
 * Parameters for querying email activity time-series data.
 */
export const EmailActivityParamsSchema = z.object({
    /**
     * Number of days of activity to retrieve.
     *
     * @remarks Values below 7 are clamped to 7; values above 365 are clamped to 365.
     *
     * @default 7
     */
    days: z.number().min(7).max(365).default(7).describe('Number of days of activity to retrieve.'), // min 7, max 365, default 7
});
/**
 * A single data point in the email activity time-series.
 */
export const EmailActivityDataPointSchema = z.object({
    /**
     * The date in `YYYY-MM-DD` format.
     */
    date: z.string().describe('The date in `YYYY-MM-DD` format.'), // "2026-02-28"
    /**
     * Number of inbound emails received on this date.
     */
    inbound: z.number().describe('Number of inbound emails received on this date.'),
    /**
     * Number of outbound emails sent on this date.
     */
    outbound: z.number().describe('Number of outbound emails sent on this date.'),
});
/**
 * Result of an email activity query containing daily time-series data.
 */
export const EmailActivityResultSchema = z.object({
    /**
     * Array of daily activity data points, ordered chronologically.
     */
    activity: z
        .array(EmailActivityDataPointSchema)
        .describe('Array of daily activity data points, ordered chronologically.'),
    /**
     * The number of days of data returned.
     */
    days: z.number().describe('The number of days of data returned.'),
});
/**
 * Unwrap a Catalyst API response payload.
 *
 * The Catalyst API may return data in one of two envelope formats:
 * - `{ key: data }` — the key maps directly to the data
 * - `{ data: { key: data } }` — the data is nested inside a `data` wrapper
 *
 * This helper normalises both shapes so callers always receive the inner value.
 *
 * @param payload - The raw JSON-parsed response body from the API
 * @param key - The property name to extract from the payload (e.g., `'address'`, `'destinations'`)
 * @returns The extracted value cast to type `T`
 *
 * @remarks
 * If neither envelope format matches, the raw payload is returned as-is.
 * This function does not throw — it always returns a value.
 */
function unwrap(payload, key) {
    if (typeof payload === 'object' && payload !== null) {
        const obj = payload;
        if (key in obj) {
            return obj[key];
        }
        if ('data' in obj && typeof obj.data === 'object' && obj.data !== null) {
            const data = obj.data;
            if (key in data) {
                return data[key];
            }
            return data;
        }
    }
    return payload;
}
/**
 * Client for the Agentuity Email service.
 *
 * Provides methods for managing email addresses, configuring inbound email
 * destinations, sending outbound emails, and querying email history.
 *
 * Email addresses are created under the `@agentuity.email` domain. Inbound emails
 * can be forwarded to URL destinations. Outbound emails are sent asynchronously
 * and support attachments up to 25 MB total.
 *
 * All methods are instrumented with OpenTelemetry spans for observability.
 *
 * @example
 * ```typescript
 * const email = new EmailStorageService(baseUrl, adapter);
 *
 * // Create an address
 * const addr = await email.createAddress('notifications');
 *
 * // Send an email
 * await email.send({
 *   from: addr.email,
 *   to: ['user@example.com'],
 *   subject: 'Hello',
 *   text: 'Hello from Agentuity!',
 * });
 * ```
 */
export class EmailStorageService {
    #adapter;
    #baseUrl;
    /**
     * Create a new EmailStorageService instance.
     *
     * @param baseUrl - The base URL for the Agentuity Email API (e.g., `https://api.agentuity.com`)
     * @param adapter - The HTTP fetch adapter used for making API requests
     */
    constructor(baseUrl, adapter) {
        this.#adapter = adapter;
        this.#baseUrl = baseUrl;
    }
    /**
     * Create a new email address under the `@agentuity.email` domain.
     *
     * @param localPart - The local part of the email address (the part before the `@`).
     *   For example, passing `'support'` creates `support@agentuity.email`.
     * @returns The newly created email address record
     * @throws ServiceException on API errors (e.g., duplicate address, invalid local part)
     *
     * @example
     * ```typescript
     * const addr = await email.createAddress('support');
     * console.log('Created:', addr.email); // support@agentuity.email
     * ```
     */
    async createAddress(localPart) {
        const url = buildUrl(this.#baseUrl, '/email/addresses');
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'POST',
            body: safeStringify({ local_part: localPart }),
            contentType: 'application/json',
            signal,
            telemetry: {
                name: 'agentuity.email.createAddress',
                attributes: {
                    localPart,
                },
            },
        });
        if (res.ok) {
            return unwrap(res.data, 'address');
        }
        throw await toServiceException('POST', url, res.response);
    }
    /**
     * List all email addresses owned by the current organization.
     *
     * @returns An array of email address records. Returns an empty array if none exist.
     * @throws ServiceException on API errors
     *
     * @example
     * ```typescript
     * const addresses = await email.listAddresses();
     * for (const addr of addresses) {
     *   console.log(`${addr.email} — ${addr.inbound_count ?? 0} received`);
     * }
     * ```
     */
    async listAddresses() {
        const url = buildUrl(this.#baseUrl, '/email/addresses');
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.listAddresses',
                attributes: {},
            },
        });
        if (res.response.status === 404) {
            return [];
        }
        if (res.ok) {
            const items = unwrap(res.data, 'addresses');
            return Array.isArray(items) ? items : [];
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Get an email address by its ID.
     *
     * @param id - The email address ID (prefixed with `eaddr_`)
     * @returns The email address record, or `null` if no address with the given ID exists
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * const addr = await email.getAddress('eaddr_abc123');
     * if (addr) {
     *   console.log('Found:', addr.email);
     * }
     * ```
     */
    async getAddress(id) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.getAddress',
                attributes: {
                    id,
                },
            },
        });
        if (res.response.status === 404) {
            return null;
        }
        if (res.ok) {
            return unwrap(res.data, 'address');
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Get IMAP and POP3 connection settings for an email address.
     *
     * These settings can be used to configure an external mail client (e.g., Thunderbird, Outlook)
     * to access the mailbox associated with the given address.
     *
     * @param id - The email address ID (prefixed with `eaddr_`)
     * @returns The connection configuration with IMAP and POP3 settings, or `null` if the address is not found
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * const config = await email.getConnectionConfig('eaddr_abc123');
     * if (config) {
     *   console.log('IMAP host:', config.imap.host);
     *   console.log('POP3 host:', config.pop3.host);
     * }
     * ```
     */
    async getConnectionConfig(id) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(id)}/connection`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.getConnectionConfig',
                attributes: {
                    id,
                },
            },
        });
        if (res.response.status === 404) {
            return null;
        }
        if (res.ok) {
            return unwrap(res.data, 'connection');
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Delete an email address and all associated destinations.
     *
     * @remarks This operation is idempotent — deleting a non-existent address does not throw.
     *
     * @param id - The email address ID (prefixed with `eaddr_`)
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * await email.deleteAddress('eaddr_abc123');
     * ```
     */
    async deleteAddress(id) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'DELETE',
            signal,
            telemetry: {
                name: 'agentuity.email.deleteAddress',
                attributes: {
                    id,
                },
            },
        });
        if (res.ok || res.response.status === 404) {
            return;
        }
        throw await toServiceException('DELETE', url, res.response);
    }
    /**
     * Create a new destination for an email address.
     *
     * Destinations determine where inbound emails are forwarded when they arrive
     * at the parent address.
     *
     * @param addressId - The email address ID (prefixed with `eaddr_`)
     * @param type - The destination type (currently only `'url'` is supported)
     * @param config - Type-specific destination configuration. For `'url'`:
     *   `{ url: string, headers?: Record<string, string>, method?: 'POST' | 'PUT' | 'PATCH' }`
     * @returns The newly created destination record
     * @throws ServiceException on API errors (e.g., invalid URL, address not found)
     *
     * @example
     * ```typescript
     * const dest = await email.createDestination('eaddr_abc123', 'url', {
     *   url: 'https://example.com/webhook',
     *   headers: { 'X-Secret': 'my-token' },
     * });
     * console.log('Destination created:', dest.id);
     * ```
     */
    async createDestination(addressId, type, config) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(addressId)}/destinations`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'POST',
            body: safeStringify({ type, config }),
            contentType: 'application/json',
            signal,
            telemetry: {
                name: 'agentuity.email.createDestination',
                attributes: {
                    addressId,
                    type,
                },
            },
        });
        if (res.ok) {
            return unwrap(res.data, 'destination');
        }
        throw await toServiceException('POST', url, res.response);
    }
    /**
     * List all destinations configured for an email address.
     *
     * @param addressId - The email address ID (prefixed with `eaddr_`)
     * @returns An array of destination records. Returns an empty array if none exist.
     * @throws ServiceException on API errors
     *
     * @example
     * ```typescript
     * const destinations = await email.listDestinations('eaddr_abc123');
     * for (const dest of destinations) {
     *   console.log(`${dest.type}: ${dest.id}`);
     * }
     * ```
     */
    async listDestinations(addressId) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(addressId)}/destinations`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.listDestinations',
                attributes: {
                    addressId,
                },
            },
        });
        if (res.response.status === 404) {
            return [];
        }
        if (res.ok) {
            const items = unwrap(res.data, 'destinations');
            return Array.isArray(items) ? items : [];
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Delete a destination from an email address.
     *
     * @remarks This operation is idempotent — deleting a non-existent destination does not throw.
     *
     * @param addressId - The email address ID (prefixed with `eaddr_`)
     * @param destinationId - The destination ID (prefixed with `edst_`)
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * await email.deleteDestination('eaddr_abc123', 'edst_xyz789');
     * ```
     */
    async deleteDestination(addressId, destinationId) {
        const url = buildUrl(this.#baseUrl, `/email/addresses/${encodeURIComponent(addressId)}/destinations/${encodeURIComponent(destinationId)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'DELETE',
            signal,
            telemetry: {
                name: 'agentuity.email.deleteDestination',
                attributes: {
                    addressId,
                    destinationId,
                },
            },
        });
        if (res.ok || res.response.status === 404) {
            return;
        }
        throw await toServiceException('DELETE', url, res.response);
    }
    /**
     * Send an outbound email from an Agentuity email address.
     *
     * Emails are sent asynchronously — this method returns immediately with an outbound
     * record whose status is `'pending'`. Use {@link getOutbound} to poll for delivery status.
     *
     * @remarks
     * - The `from` address must be owned by the current organization.
     * - Maximum 50 recipients per send.
     * - Maximum 25 MB for the full RFC 822 body (including attachments).
     *
     * @param params - The email send parameters including from, to, subject, and body
     * @returns The outbound email record with initial status `'pending'`
     * @throws ServiceException on API errors (e.g., invalid sender, too many recipients)
     *
     * @example
     * ```typescript
     * const result = await email.send({
     *   from: 'notifications@agentuity.email',
     *   to: ['user@example.com'],
     *   subject: 'Welcome!',
     *   text: 'Welcome to our platform.',
     *   html: '<h1>Welcome!</h1>',
     *   attachments: [{
     *     filename: 'guide.pdf',
     *     content: base64EncodedPdf,
     *     contentType: 'application/pdf',
     *   }],
     * });
     * console.log('Email queued:', result.id);
     * ```
     */
    async send(params) {
        const url = buildUrl(this.#baseUrl, '/email/outbound/send');
        const signal = AbortSignal.timeout(30_000);
        // Transform attachments to API format (snake_case)
        const body = {
            from: params.from,
            to: params.to,
            subject: params.subject,
        };
        if (params.text !== undefined) {
            body.text = params.text;
        }
        if (params.html !== undefined) {
            body.html = params.html;
        }
        if (params.attachments && params.attachments.length > 0) {
            body.attachments = params.attachments.map((a) => ({
                filename: a.filename,
                content: a.content,
                ...(a.contentType && { content_type: a.contentType }),
            }));
        }
        if (params.headers && Object.keys(params.headers).length > 0) {
            body.headers = params.headers;
        }
        const res = await this.#adapter.invoke(url, {
            method: 'POST',
            body: safeStringify(body),
            contentType: 'application/json',
            signal,
            telemetry: {
                name: 'agentuity.email.send',
                attributes: {
                    from: params.from,
                    toCount: String(params.to.length),
                },
            },
        });
        if (res.ok) {
            return unwrap(res.data, 'outbound');
        }
        throw await toServiceException('POST', url, res.response);
    }
    /**
     * List inbound emails, optionally filtered by email address.
     *
     * @param addressId - Optional email address ID (prefixed with `eaddr_`) to filter results.
     *   When omitted, returns inbound emails across all addresses in the organization.
     * @returns An array of inbound email records. Returns an empty array if none exist.
     * @throws ServiceException on API errors
     *
     * @example
     * ```typescript
     * // List all inbound emails
     * const all = await email.listInbound();
     *
     * // List inbound for a specific address
     * const filtered = await email.listInbound('eaddr_abc123');
     * for (const msg of filtered) {
     *   console.log(`From: ${msg.from}, Subject: ${msg.subject}`);
     * }
     * ```
     */
    async listInbound(addressId) {
        const queryParams = new URLSearchParams();
        if (addressId) {
            queryParams.set('address_id', addressId);
        }
        const queryString = queryParams.toString();
        const url = buildUrl(this.#baseUrl, `/email/inbound${queryString ? `?${queryString}` : ''}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.listInbound',
                attributes: {
                    ...(addressId && { addressId }),
                },
            },
        });
        if (res.response.status === 404) {
            return [];
        }
        if (res.ok) {
            const items = unwrap(res.data, 'inbound');
            return Array.isArray(items) ? items : [];
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Get an inbound email by its ID.
     *
     * @param id - The inbound email ID (prefixed with `einb_`)
     * @returns The inbound email record, or `null` if not found
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * const msg = await email.getInbound('einb_abc123');
     * if (msg) {
     *   console.log('Subject:', msg.subject);
     *   console.log('Attachments:', msg.attachments?.length ?? 0);
     * }
     * ```
     */
    async getInbound(id) {
        const url = buildUrl(this.#baseUrl, `/email/inbound/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.getInbound',
                attributes: {
                    id,
                },
            },
        });
        if (res.response.status === 404) {
            return null;
        }
        if (res.ok) {
            return unwrap(res.data, 'inbound');
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Delete an inbound email by its ID.
     *
     * @remarks This operation is idempotent — deleting a non-existent email does not throw.
     *
     * @param id - The inbound email ID (prefixed with `einb_`)
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * await email.deleteInbound('einb_abc123');
     * ```
     */
    async deleteInbound(id) {
        const url = buildUrl(this.#baseUrl, `/email/inbound/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'DELETE',
            signal,
            telemetry: {
                name: 'agentuity.email.deleteInbound',
                attributes: {
                    id,
                },
            },
        });
        if (res.ok || res.response.status === 404) {
            return;
        }
        throw await toServiceException('DELETE', url, res.response);
    }
    /**
     * List outbound emails, optionally filtered by email address.
     *
     * @param addressId - Optional email address ID (prefixed with `eaddr_`) to filter results.
     *   When omitted, returns outbound emails across all addresses in the organization.
     * @returns An array of outbound email records. Returns an empty array if none exist.
     * @throws ServiceException on API errors
     *
     * @example
     * ```typescript
     * // List all outbound emails
     * const all = await email.listOutbound();
     *
     * // List outbound for a specific address
     * const filtered = await email.listOutbound('eaddr_abc123');
     * for (const msg of filtered) {
     *   console.log(`To: ${msg.to}, Status: ${msg.status}`);
     * }
     * ```
     */
    async listOutbound(addressId) {
        const queryParams = new URLSearchParams();
        if (addressId) {
            queryParams.set('address_id', addressId);
        }
        const queryString = queryParams.toString();
        const url = buildUrl(this.#baseUrl, `/email/outbound${queryString ? `?${queryString}` : ''}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.listOutbound',
                attributes: {
                    ...(addressId && { addressId }),
                },
            },
        });
        if (res.response.status === 404) {
            return [];
        }
        if (res.ok) {
            const items = unwrap(res.data, 'outbound');
            return Array.isArray(items) ? items : [];
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Get an outbound email by its ID.
     *
     * @param id - The outbound email ID (prefixed with `eout_`)
     * @returns The outbound email record, or `null` if not found
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * const msg = await email.getOutbound('eout_abc123');
     * if (msg) {
     *   console.log('Status:', msg.status);
     *   if (msg.error) {
     *     console.error('Delivery failed:', msg.error);
     *   }
     * }
     * ```
     */
    async getOutbound(id) {
        const url = buildUrl(this.#baseUrl, `/email/outbound/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.getOutbound',
                attributes: {
                    id,
                },
            },
        });
        if (res.response.status === 404) {
            return null;
        }
        if (res.ok) {
            return unwrap(res.data, 'outbound');
        }
        throw await toServiceException('GET', url, res.response);
    }
    /**
     * Delete an outbound email by its ID.
     *
     * @remarks This operation is idempotent — deleting a non-existent email does not throw.
     *
     * @param id - The outbound email ID (prefixed with `eout_`)
     * @throws ServiceException on API errors (other than 404)
     *
     * @example
     * ```typescript
     * await email.deleteOutbound('eout_abc123');
     * ```
     */
    async deleteOutbound(id) {
        const url = buildUrl(this.#baseUrl, `/email/outbound/${encodeURIComponent(id)}`);
        const signal = AbortSignal.timeout(30_000);
        const res = await this.#adapter.invoke(url, {
            method: 'DELETE',
            signal,
            telemetry: {
                name: 'agentuity.email.deleteOutbound',
                attributes: {
                    id,
                },
            },
        });
        if (res.ok || res.response.status === 404) {
            return;
        }
        throw await toServiceException('DELETE', url, res.response);
    }
    /**
     * Get email activity time-series data showing inbound and outbound counts per day.
     *
     * @param params - Optional query parameters. `days` controls the lookback window
     *   (minimum 7, maximum 365, server default 7).
     * @returns An {@link EmailActivityResult} with daily data points ordered chronologically
     *   and the total number of days returned
     * @throws ServiceException on API errors
     *
     * @example
     * ```typescript
     * // Get last 30 days of activity
     * const result = await email.getActivity({ days: 30 });
     * console.log(`Activity over ${result.days} days:`);
     * for (const point of result.activity) {
     *   console.log(`  ${point.date}: ${point.inbound} in, ${point.outbound} out`);
     * }
     * ```
     */
    async getActivity(params) {
        const queryParams = new URLSearchParams();
        if (params?.days !== undefined) {
            const raw = Number(params.days);
            if (Number.isFinite(raw)) {
                const clamped = Math.max(7, Math.min(365, Math.trunc(raw)));
                queryParams.set('days', String(clamped));
            }
        }
        const queryString = queryParams.toString();
        const url = buildUrl(this.#baseUrl, `/email/activity${queryString ? `?${queryString}` : ''}`);
        const signal = AbortSignal.timeout(30_000);
        const days = queryParams.get('days');
        const res = await this.#adapter.invoke(url, {
            method: 'GET',
            signal,
            telemetry: {
                name: 'agentuity.email.activity',
                attributes: {
                    ...(days ? { days } : {}),
                },
            },
        });
        if (res.ok) {
            // Email endpoints return data directly (no success wrapper)
            return res.data;
        }
        throw await toServiceException('GET', url, res.response);
    }
}
//# sourceMappingURL=service.js.map