import { Effect, Layer } from "effect";
import { type HttpClient } from "effect/unstable/http";
import type { Connection } from "./connection";
import type { IFumaClient, StorageFailure } from "./fuma-runtime";
import { AuthTemplateSlug, ConnectionName, IntegrationSlug, OAuthClientSlug, Owner } from "./ids";
import { type OAuthService } from "./oauth-client";
import type { OwnerBinding } from "./plugin";
import type { CredentialProvider } from "./provider";
import { type OAuthEndpointUrlPolicy } from "./oauth-helpers";
/** Connection-minting input for the OAuth flow — extends a connection create
 *  with the OAuth lifecycle fields (client slug, refresh material, expiry,
 *  granted scope). The executor's `mintOAuthConnection` writes these onto the
 *  `connection` row and produces the connection's tools. */
export interface MintOAuthConnectionInput {
    readonly owner: Owner;
    readonly name: ConnectionName;
    readonly integration: IntegrationSlug;
    readonly template: AuthTemplateSlug;
    readonly identityLabel?: string | null;
    /** Credential provider key + item id the access token is stored under. */
    readonly provider: string;
    readonly itemId: string;
    readonly oauthClient: OAuthClientSlug;
    /** The owner of `oauthClient` (persisted so refresh loads it by explicit owner). */
    readonly oauthClientOwner: Owner;
    readonly refreshItemId: string | null;
    readonly expiresAt: number | null;
    readonly oauthScope: string | null;
    /** Per-connection override for the token endpoint, persisted only when the
     *  code was redeemed at a region other than the client's configured token
     *  host (Datadog multi-site). Null means refresh uses the client's token URL. */
    readonly oauthTokenUrl?: string | null;
}
/** The OAuth scope policy for a `(integration, template)`. Either the
 *  integration declares the scopes to request (`scopes`, possibly empty — an
 *  empty set requests no scopes), or it declares none and the request scopes
 *  are discovered from the server's metadata at connect (`discover`, used by
 *  MCP). The two are mutually exclusive by construction. */
export type OAuthScopePolicy = {
    readonly kind: "scopes";
    readonly scopes: readonly string[];
} | {
    readonly kind: "discover";
};
/** Everything the OAuth service needs from the executor: fuma access for the
 *  owned `oauth_client` / `oauth_session` tables, the default credential
 *  provider for minted tokens, a `mintOAuthConnection` callback (writes the
 *  connection row + produces tools), the owner binding, and the redirect base. */
export interface OAuthServiceDeps {
    readonly fuma: IFumaClient;
    readonly owner: OwnerBinding;
    readonly tenant: string;
    readonly subject: string | null;
    readonly ownedKeys: (owner: Owner) => {
        readonly tenant: string;
        readonly owner: Owner;
        readonly subject: string;
    };
    readonly defaultWritableProvider: () => CredentialProvider | null;
    /** Write the connection row with OAuth lifecycle fields + produce its tools. */
    readonly mintOAuthConnection: (input: MintOAuthConnectionInput) => Effect.Effect<Connection, StorageFailure>;
    /**
     * Resolve the OAuth scope policy for a `(integration, template)`:
     *  - `{ kind: "scopes", scopes }`: the scopes the integration's auth template
     *    DECLARES (e.g. an OpenAPI bundle's authentication-template scope union),
     *    NOT the scopes frozen on a specific `oauth_client` row. These are
     *    requested verbatim at connect (`start`); an empty set requests none.
     *  - `{ kind: "discover" }`: the integration declares no scopes, so `start`
     *    discovers the request scopes from the server's RFC 9728 / RFC 8414
     *    metadata. Used by server-targeting integrations (MCP) whose scopes live
     *    on the server rather than in a template.
     */
    readonly resolveOAuthScopePolicy: (integration: IntegrationSlug, template: AuthTemplateSlug) => Effect.Effect<OAuthScopePolicy, StorageFailure>;
    readonly httpClientLayer?: Layer.Layer<HttpClient.HttpClient>;
    readonly fetch?: typeof globalThis.fetch;
    readonly endpointUrlPolicy?: OAuthEndpointUrlPolicy;
    /**
     * The OAuth callback URL (`${webBaseUrl}${mountPrefix}/oauth/callback`) the host
     * serves and sends to providers on every authorization request + DCR registration.
     * The path carries the host's API mount prefix (cloud: `/api`; root-mounted
     * hosts like local: none), so it matches the route that serves the callback.
     *
     * REQUIRED and EXPLICIT — there is no localhost default. Pass `null` only when
     * the host genuinely has no redirect callback (e.g. a pure client-credentials
     * or non-HTTP context); the redirect-requiring flows (`start` for
     * `authorization_code`, `registerDynamicClient`) then fail loudly instead of
     * silently handing the provider a wrong `http://127.0.0.1/callback`. Hosts
     * that serve OAuth MUST derive this from the request origin / web base URL.
     */
    readonly redirectUri: string | null;
    /** URL selected organization slug to round-trip through OAuth `state`. */
    readonly callbackStateOrgSlug?: string | null;
}
export declare const makeOAuthService: (deps: OAuthServiceDeps) => OAuthService;
//# sourceMappingURL=oauth-service.d.ts.map