import type { DnsRecordGroupState } from "./cloudflare.js";
export declare const PROVISIONING_STATUSES: readonly ["requested", "quoted", "registration_submitting", "registration_submitted", "registered", "zone_ready", "nameservers_submitted", "delegated", "worker_bound", "ready", "manual_review", "failed"];
export type ProvisioningStatus = (typeof PROVISIONING_STATUSES)[number];
export type OriginTlsMode = "strict" | "full";
export type ProvisioningRegistrar = "route53" | "brandsight";
export interface DomainProvisioningRequest {
    name: string;
    idempotency_key: string;
    max_price_usd: number;
    years: number;
    auto_renew: boolean;
    acquisition_mode: "purchase" | "adopt";
    registrar: ProvisioningRegistrar;
    dns_provider: "cloudflare";
    target: "shortlinks" | "website_origin";
    worker_name: string | null;
    origin_hostname: string | null;
    origin_tls_mode: OriginTlsMode | null;
}
export interface ProvisionedWebRecord {
    type: "CNAME";
    name: string;
    value: string;
    proxied: true;
    ttl: number;
}
export interface DomainProvisioningResult {
    zone_ref: string;
    nameservers: string[];
    web_records: ProvisionedWebRecord[];
    origin_tls_mode: OriginTlsMode | null;
    checked_at: string;
}
export declare const HOSTED_DNS_RECORD_TYPES: readonly ["TXT", "CNAME", "MX"];
export type HostedDnsRecordType = (typeof HOSTED_DNS_RECORD_TYPES)[number];
export interface HostedDnsRecord {
    type: HostedDnsRecordType;
    name: string;
    value: string;
    ttl: number;
    priority: number | null;
}
export interface EffectiveHostedDnsRecord extends HostedDnsRecord {
    /** Server-derived TXT presentation decoding; raw value remains authoritative for preservation. */
    semantic_value?: string;
}
export type AppliedHostedDnsRecords = HostedDnsRecord[] | {
    records: HostedDnsRecord[];
    verifiedGroup: DnsRecordGroupState;
};
export interface DomainDnsReconciliation {
    id: string;
    provisioning_job_id: string;
    domain_name: string;
    idempotency_key: string;
    request_hash: string;
    status: "requested" | "applying" | "ready" | "manual_review";
    records: HostedDnsRecord[];
    result: {
        records: EffectiveHostedDnsRecord[];
        checked_at: string;
    } | null;
    error: string | null;
    lease_token: string | null;
    lease_until: string | null;
    created_at: string;
    updated_at: string;
}
export type PublicDomainDnsReconciliation = Omit<DomainDnsReconciliation, "lease_token">;
export declare function publicDnsReconciliation(reconciliation: DomainDnsReconciliation): PublicDomainDnsReconciliation;
export interface DomainProvisioningProviderState {
    route53_account_ref?: string;
    route53_account_binding_sha256?: string;
    quoted_price_usd?: number;
    currency?: string;
    registration_operation_id?: string;
    nameserver_operation_id?: string;
    cloudflare_zone_id?: string;
    cloudflare_nameservers?: string[];
    route53_zone_ids_before_registration?: string[];
    route53_hosted_zone_id?: string;
    route53_hosted_zone_cleaned?: boolean;
    worker_domain_bound?: boolean;
    website_origin_configured?: boolean;
    origin_tls_mode_configured?: OriginTlsMode | "origin_pull";
    origin_tls_mode_checked_at?: string;
    web_records?: ProvisionedWebRecord[];
    target_checked_at?: string;
    last_provider_status?: string;
    registration_submitted_at?: string;
    nameservers_submitted_at?: string;
    delegation_dns_preserved_count?: number;
    delegation_dns_preserved_sha256?: string;
    delegation_dns_preserved_at?: string;
}
export interface PortfolioDomainRegistration extends RegisteredDomainDetail {
    id: string;
    status: string;
    registrar: string;
}
export interface DomainProvisioningJob extends DomainProvisioningRequest {
    id: string;
    domain_id: string;
    request_hash: string;
    status: ProvisioningStatus;
    provider_state: DomainProvisioningProviderState;
    attempts: number;
    error: string | null;
    lease_token: string | null;
    lease_until: string | null;
    created_at: string;
    updated_at: string;
}
export interface DomainProvisioningStore {
    reserve(request: DomainProvisioningRequest, requestHash: string): Promise<DomainProvisioningJob>;
    get(id: string): Promise<DomainProvisioningJob | null>;
    getByName(name: string): Promise<DomainProvisioningJob | null>;
    getPortfolioRegistration(name: string): Promise<PortfolioDomainRegistration | null>;
    listRunnable(limit: number): Promise<DomainProvisioningJob[]>;
    claim(id: string, leaseToken: string, leaseUntil: string): Promise<DomainProvisioningJob | null>;
    update(id: string, patch: Partial<Pick<DomainProvisioningJob, "status" | "provider_state" | "attempts" | "error" | "lease_token" | "lease_until">>, leaseToken?: string): Promise<DomainProvisioningJob | null>;
    markPortfolioReady(job: DomainProvisioningJob, detail: RegisteredDomainDetail): Promise<void>;
    reserveAdoption(request: DomainProvisioningRequest, requestHash: string, detail: RegisteredDomainDetail, providerState?: DomainProvisioningProviderState): Promise<DomainProvisioningJob>;
    reserveDnsReconciliation(input: {
        job: DomainProvisioningJob;
        idempotencyKey: string;
        requestHash: string;
        records: HostedDnsRecord[];
    }): Promise<DomainDnsReconciliation>;
    listDnsReconciliations(name: string, query: {
        id?: string;
        idempotency_key?: string;
        limit: number;
        offset: number;
    }): Promise<DomainDnsReconciliation[]>;
    withDnsZoneLock<T>(zoneId: string, work: (store: DomainProvisioningStore) => Promise<T>): Promise<T>;
    claimDnsReconciliation(id: string, leaseToken: string, leaseUntil: string): Promise<DomainDnsReconciliation | null>;
    updateDnsReconciliation(id: string, patch: Partial<Pick<DomainDnsReconciliation, "status" | "result" | "error" | "lease_token" | "lease_until">>, leaseToken: string): Promise<DomainDnsReconciliation | null>;
}
export interface AvailabilityQuote {
    available: boolean;
    price_usd?: number;
    registration_price_usd?: number;
    renewal_price_usd?: number;
    currency?: string;
    is_premium?: boolean;
}
export interface ProviderOperationStatus {
    status: string;
    message?: string;
}
export interface Route53AccountBinding {
    route53_account_ref: string;
    route53_account_binding_sha256: string;
}
export interface RegisteredDomainDetail {
    registered_at?: string;
    expires_at?: string;
    auto_renew?: boolean;
    nameservers: string[];
    registrar?: string;
}
export interface DelegationDnsPreservation {
    count: number;
    sha256: string;
}
export interface CloudflareZoneState {
    id: string;
    status: string;
    nameservers: string[];
}
export interface DomainProvisioningProviders {
    checkAvailability(name: string): Promise<AvailabilityQuote>;
    submitRegistration(input: {
        name: string;
        years: number;
        autoRenew: boolean;
    }): Promise<{
        operationId: string;
    }>;
    getOperationStatus(operationId: string, route53Binding?: Route53AccountBinding): Promise<ProviderOperationStatus>;
    getDomainDetail(name: string, registrar?: ProvisioningRegistrar, route53Binding?: Route53AccountBinding): Promise<RegisteredDomainDetail | null>;
    resolveAdoptionAccount?(name: string): Promise<(Route53AccountBinding & {
        detail: RegisteredDomainDetail;
    }) | null>;
    ensureCloudflareZone(name: string): Promise<CloudflareZoneState>;
    updateNameservers(name: string, nameservers: string[], registrar?: ProvisioningRegistrar, route53Binding?: Route53AccountBinding): Promise<{
        operationId: string;
        completed?: boolean;
    }>;
    resolvePublicNameservers(name: string): Promise<string[]>;
    preserveRegistrarDnsBeforeDelegation(input: {
        hostname: string;
        zoneId: string;
        registrar: ProvisioningRegistrar;
        route53Binding?: Route53AccountBinding;
    }): Promise<DelegationDnsPreservation>;
    bindWorkerDomain(input: {
        hostname: string;
        zoneId: string;
        workerName: string;
    }): Promise<void>;
    workerDomainReady(input: {
        hostname: string;
        zoneId: string;
        workerName: string;
    }): Promise<boolean>;
    configureWebsiteOrigin(input: {
        hostname: string;
        zoneId: string;
        originHostname: string;
    }): Promise<ProvisionedWebRecord[]>;
    ensureWebsiteOriginTls(input: {
        zoneId: string;
        requestedMode: OriginTlsMode;
    }): Promise<{
        mode: OriginTlsMode | "origin_pull";
        changed: boolean;
        downgradeRefused: boolean;
    }>;
    websiteOriginReady(input: {
        hostname: string;
        zoneId: string;
        originHostname: string;
        originTlsMode: OriginTlsMode;
    }): Promise<boolean>;
    getDnsRecordState?(input: {
        zoneId: string;
        recordName: string;
        recordType: HostedDnsRecordType;
    }): Promise<DnsRecordGroupState>;
    reconcileDnsRecords(input: {
        hostname: string;
        zoneId: string;
        records: HostedDnsRecord[];
        mode?: "preserve_mail";
        assertLease?: () => Promise<void>;
        deadline?: number;
        expectedCurrentRecordsSha256?: string;
    }): Promise<AppliedHostedDnsRecords>;
    dnsRecordsReady(input: {
        assertLease?: () => Promise<void>;
        deadline?: number;
        hostname: string;
        zoneId: string;
        records: HostedDnsRecord[];
        mode?: "preserve_mail";
        verifiedGroup?: DnsRecordGroupState;
    }): Promise<boolean>;
    listRoute53HostedZoneIds?(name: string): Promise<string[]>;
    cleanupRoute53HostedZone?(input: {
        name: string;
        hostedZoneId: string;
        registrarNameservers: string[];
    }): Promise<boolean>;
}
export declare class RegistrarActionRequiredError extends Error {
    constructor(message: string);
}
export interface ProvisioningWorkerOptions {
    intervalMs?: number;
    batchSize?: number;
    leaseMs?: number;
    maxAttempts?: number;
    shortlinksWorkerName?: string;
    now?: () => Date;
    log?: (event: string, detail: Record<string, unknown>) => void;
}
/** Current canonical hash for a normalized provisioning request. */
export declare function provisioningRequestHash(request: DomainProvisioningRequest): string;
/**
 * Accept a stored request hash only when it is either the current canonical
 * hash or the exact pre-origin-fields hash used by legacy Shortlinks purchase
 * jobs. The caller-supplied current hash must itself be canonical, so an
 * arbitrary mismatched hash cannot use this compatibility path.
 */
export declare function provisioningRequestHashMatches(storedHash: string, currentHash: string, request: DomainProvisioningRequest): boolean;
export declare function normalizeHostedDnsRecords(value: unknown, domainValue: string, allowRetainedAutomaticTtl?: boolean): HostedDnsRecord[];
/** A hosted website origin is an AWS ALB DNS hostname, never a URL or an IP. */
export declare function normalizeWebsiteOriginHostname(value: unknown): string;
export declare class DomainProvisioningService {
    private readonly store;
    private readonly providers;
    private readonly intervalMs;
    private readonly batchSize;
    private readonly leaseMs;
    private readonly maxAttempts;
    private readonly shortlinksWorkerName;
    private readonly now;
    private readonly log;
    private timer;
    private scheduledRunActive;
    constructor(store: DomainProvisioningStore, providers: DomainProvisioningProviders, options?: ProvisioningWorkerOptions);
    quote(name: string): Promise<{
        name: string;
    } & AvailabilityQuote>;
    request(input: Partial<DomainProvisioningRequest>): Promise<DomainProvisioningJob>;
    adopt(input: {
        name?: unknown;
        idempotency_key?: unknown;
        dns_provider?: unknown;
        target?: unknown;
        worker_name?: unknown;
        origin_hostname?: unknown;
        origin_tls_mode?: unknown;
    }): Promise<DomainProvisioningJob>;
    get(id: string): Promise<DomainProvisioningJob | null>;
    getByName(name: string): Promise<DomainProvisioningJob | null>;
    listDnsReconciliations(name: string, query?: {
        id?: unknown;
        idempotency_key?: unknown;
        limit?: unknown;
        offset?: unknown;
    }): Promise<{
        reconciliations: DomainDnsReconciliation[];
        next_offset: number | null;
    }>;
    getDnsState(name: string, recordName: unknown, recordType: unknown): Promise<DnsRecordGroupState & {
        domain_name: string;
        provisioning_job_id: string;
    }>;
    reconcileDns(name: string, input: {
        idempotency_key?: unknown;
        records?: unknown;
        mode?: unknown;
        expected_current_records_sha256?: unknown;
    }): Promise<DomainDnsReconciliation>;
    advance(id: string): Promise<DomainProvisioningJob>;
    private advanceClaimed;
    runOnce(): Promise<{
        processed: number;
        ready: number;
        failed: number;
    }>;
    private runScheduledOnce;
    start(): void;
    stop(): void;
}
export declare function isProvisioningTerminal(status: ProvisioningStatus): boolean;
export type PublicDomainProvisioningJob = Omit<DomainProvisioningJob, "lease_token"> & {
    result: DomainProvisioningResult | null;
};
/** Strip the worker lease credential from every API response. */
export declare function publicProvisioningJob(job: DomainProvisioningJob): PublicDomainProvisioningJob;
//# sourceMappingURL=provisioning.d.ts.map