import { PaymentRequest as PaymentRequest_2 } from '@stripe/stripe-js';
import { Subject } from 'rxjs';
import { z } from 'zod';

declare type AllCallbacks = {
    onFocus?: (elementId: string, field: AllFieldNames) => void;
    onBlur?: (elementId: string, field: AllFieldNames) => void;
    onChange?: (elementId: string, field: AllFieldNames, errors?: string[]) => void;
    onLoad?: (totalAmountAtoms?: number, currency?: string) => void;
    onLoadError?: (message: string) => void;
    onValidationError?: OnValidationError;
    onCheckoutStarted?: OnCheckoutStarted;
    onCheckoutSuccess?: OnCheckoutSuccess;
    onSetupPaymentMethodSuccess?: OnSetupPaymentMethodSuccess;
    onCheckoutError?: OnCheckoutError;
    onPaymentRequestLoad?: (paymentRequests: PRStatuses) => void;
};

declare const AllFieldNames = z.union([FieldNameEnum, PrivateFieldNameEnum]);

declare type AllFieldNames = z.infer<typeof AllFieldNames>;

/**
 * Payment requests
 */
declare const Amount = z.object({
    amountAtom: z.number(),
    /**
     * The three-letter ISO 4217 currency code for the payment.
     */
    currency: RequiredString,
});

declare type Amount = z.infer<typeof Amount>;

declare type ApplePayFlowCustomParams = {
    /**
     * In case of multiple processors available for ApplePay, the preferredProcessor setting will
     * determine which processor to use.
     * If `preferredProcessor` is not set or is not available, the first available processor will be used
     *
     * **Note**: this processor choice is for the whole process of showing PaymentRequest UI, verification and charging.
     * It won't perform fallback processor charging when payment failed.
     */
    preferredProcessor?: PaymentProcessor;
    overridePaymentRequest?: {
        amount: Amount;
        pending?: boolean;
        label?: string;
        applePayPaymentRequest?: ApplePayJS.ApplePayPaymentRequest;
    };
    defaultFieldValues?: DefaultFieldValues;
};

declare enum Blockchain {
    EVM = 'EVM',
    SOL = 'Solana',
}

declare type BlockchainNetwork = keyof typeof Blockchain;

declare type CdeConnection = {
    send: (data: CdeMessage) => Promise<unknown>;
};

declare type CdeMessage = {
    type: string;
} & Record<string, unknown>;

/**
 * Core models
 */
declare const CheckoutPaymentMethod = z.object({
    provider: z.string(),
    processor_name: nullOrUndefOr(z.string()),
    metadata: nullOrUndefOr(z.record(z.string(), z.any())),
});

declare type CheckoutPaymentMethod = z.infer<typeof CheckoutPaymentMethod>;

declare type CommonSubmitSettings = {
    defaultFieldValues?: DefaultFieldValues;
};

export declare type Config = ElementsFormProps & AllCallbacks;

declare type CurrentStatus<T> = InitialStatus | SuccessStatus<T> | ErrorStatus;

declare interface CustomerResponse {
    /**
     * The unique identifier that represents the customer
     * @example "1234567890abcdef"
     */
    customerId: string;
    /**
     * The external customer reference ID used to tie this customer to a customer in an external system.
     * @example "1234567890abcdef"
     */
    customerRefId: string | null;
    /**
     * The payment methods configured for this customer to make payments with.
     */
    paymentMethods: Omit<PaymentMethodResponse, 'entityId' | 'customer' | 'dateCreated'>[];
    /**
     * The date the customer record was created, represented as a Unix timestamp in seconds.
     * @example 1716211200
     */
    dateCreated: number;
}

declare type CustomInitParams = {
    // You can put custom params for your init flows here

    // For stripe link
    stripeLink?: {
        /**
         * The height of the Stripe Link button. By default, the height of the buttons are 44px.
         * You can override this to specify a custom button height in the range of 40px-55px.
         *
         * See more: https://docs.stripe.com/js/elements_object/create_express_checkout_element#express_checkout_element_create-options-buttonHeight
         */
        buttonHeight?: number;

        /**
         * If this function returns false, the stripe link submit process is aborted.
         * This can be used for additional pre-submit checks (e.g. additional form validation).
         * Note that this function must complete within 1 second, or the submission will fail.
         */
        overrideLinkSubmit?: () => Promise<boolean>;

        /**
         * By default, the stripe link button is mounted on OJS initialization.
         * If this value is true, the stripe link is not mounted on init, and should instead be manually mounted.
         */
        doNotMountOnInit?: boolean;
    };

    googlePay?: {
        env: 'demo' | 'prod';
        doNotMountOnInit?: boolean;
    };

    applePay?: {
        env: 'demo' | 'prod';
        doNotMountOnInit?: boolean;
    };
};

declare type CustomStyles = string;

declare type DefaultFieldValues = Partial<Record<FieldNameEnum, string>>;

declare type ElementProps<PlaceholderType extends z.ZodTypeAny = z.ZodString> = {
    styles?: ElementsStyle<z.ZodOptional<PlaceholderType>>;
};

export declare type ElementsFormProps = {
    className?: string;
    checkoutSecureToken: string;
    baseUrl?: string;
    formTarget?: string;
    customInitParams?: CustomInitParams;
};

declare const ElementsStyle = <T extends z.ZodTypeAny>(placeholderType: T) =>
BaseElementsStyle.extend({
    placeholder: placeholderType,
    placeholderStyle: z
    .object({
        color: OptionalString,
        fontSize: OptionalString,
        fontWeight: OptionalString,
        fontFamily: OptionalString,
        letterSpacing: OptionalString,
        lineHeight: OptionalString,
    })
    .optional(),
});

declare type ElementsStyle<T extends z.ZodTypeAny> = z.infer<ReturnType<typeof ElementsStyle<T>>>;

declare const ElementType = z.nativeEnum(ElementTypeEnum);

declare type ElementType = z.infer<typeof ElementType>;

declare enum ElementTypeEnum {
    CARD = 'card',
    CARD_NUMBER = 'card-number',
    CARD_EXPIRY = 'card-expiry',
    CARD_CVC = 'card-cvc',
    CDE_BRIDGE = 'cde-bridge',
}

declare type ElementTypeEnumValue = ElementTypeEnum[keyof ElementTypeEnum];

declare enum Environment {
    Local = 'local',
    Development = 'development',
    Staging = 'staging',
    Demo = 'demo',
    Production = 'production',
}

declare type ErrorStatus = {
    status: 'error';
    isSuccess: false;
    error: unknown; // Anything can be thrown, so we don't want to be strict here
    errMsg: string;
};

/**
 * Expected input fields
 */

// Supplied by the user
export declare enum FieldName {
    FIRST_NAME = 'firstName',
    LAST_NAME = 'lastName',
    EMAIL = 'email',
    ZIP_CODE = 'zipCode',
    CITY = 'city',
    STATE = 'state',
    COUNTRY = 'country',
    ADDRESS = 'address',
    PHONE = 'phone',
    PROMOTION_CODE = 'promotionCode',
}

declare const FieldNameEnum = z.nativeEnum(FieldName);

declare type FieldNameEnum = z.infer<typeof FieldNameEnum>;

declare class FormCallbacks {
    private static readonly NOOP = () => {};
    private _callbacks: Required<AllCallbacks>;

    constructor() {
        this._callbacks = FormCallbacks.createEmptyCallbacks();
    }

    private static createEmptyCallbacks = (): Required<AllCallbacks> => {
        const x: AllCallbacks = {};
        Object.keys(ZodFormCallbacks.keyof().enum).forEach((key) => {
            // @ts-expect-error - trust the process
            x[key] = FormCallbacks.NOOP;
        });
        return x as Required<AllCallbacks>;
    };

    static fromObject = (obj: unknown) => {
        const instance = new FormCallbacks();
        instance.setCallbacks(ZodFormCallbacks.parse(obj));
        return instance;
    };

    /**
     * Sets ALL form callbacks. Note that all old callbacks are removed.
     */
    setCallbacks = (rawCallbacks: AllCallbacks) => {
        // Making sure to reinitialize
        this._callbacks = FormCallbacks.createEmptyCallbacks();
        Object.entries(rawCallbacks).forEach(([key, rawCallback]) => {
            const safeCallback = makeCallbackSafe(key, rawCallback ?? FormCallbacks.NOOP, err__);
            // @ts-expect-error - trust the process
            this._callbacks[key] = safeCallback;
        });
    };

    /**
     * Returns a read-only version of the callbacks object.
     */
    get get() {
        return { ...this._callbacks };
    }
}

declare type GooglePayFlowCustomParams = {
    /**
     * In case of multiple processors available for GooglePay, the preferredProcessor setting will
     * determine which processor to use.
     * If `preferredProcessor` is not set or is not available, the first available processor will be used
     *
     * **Note**: this processor choice is for the whole process of showing PaymentRequest UI, verification and charging.
     * It won't perform fallback processor charging when payment failed.
     */
    preferredProcessor?: PaymentProcessor;
    overridePaymentRequest?: {
        amount: Amount;
        pending?: boolean;
        label?: string;
        googlePayPaymentRequest?: PaymentDataRequest;
    };
    defaultFieldValues?: DefaultFieldValues;
};

declare type InitApplePayFlowResult = {
    processor: PaymentProcessor;
    isAvailable: boolean;
    isLoading: boolean;
    startFlow: (customParams?: ApplePayFlowCustomParams) => Promise<void>;
};

declare interface InitFailedEvent {
    type: 'initFailed';
    message: string;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    data: Record<string, any>;
}

declare type InitGooglePayFlowResult = {
    processor: PaymentProcessor;
    isAvailable: boolean;
    isLoading: boolean;
    startFlow: (customParams?: GooglePayFlowCustomParams) => Promise<void>;
};

declare interface InitializedEvent {
    entityId: string;
}

declare type InitialStatus = {
    status: 'initial';
    isSuccess: false;
};

declare type InitLoopCryptoFlowSuccess =
| {
    isAvailable: true;
    widgetProps: LoopCryptoWidgetProps;
    initLoopConnectProps: LoopConnectConfig;
}
| {
    isAvailable: false;
};

declare type InitOjsFlow<T extends InitOjsFlowResult> = (params: InitOjsFlowParams) => Promise<T>;

declare type InitOjsFlowParams = {
    /**
     * Contains the context where OJS is run.
     * Ideally this only contains "background" context (OJS-level objects), and not flow-level objects.
     */
    context: OjsContext;

    /**
     * Lifecycle callbacks for OJS flows.
     */
    formCallbacks: FormCallbacks;
};

declare type InitOjsFlowResult = {
    isAvailable: boolean;
};

declare type InitStripeLinkFlowResult =
| {
    isAvailable: true;
    controller: StripeLinkController;
}
| {
    isAvailable: false;
};

declare type InitStripePrFlowResult =
| InitStripePrFlowSuccess
| {
    isAvailable: false;
    reason: string;
};

declare type InitStripePrFlowSuccess = {
    processor: 'stripe';
    isAvailable: true;
    // Passed to RunFlow
    pr: PaymentRequest_2;
    // Passed to the user
    availableProviders: {
        applePay: boolean;
        googlePay: boolean;
    };
    startFlow: (provider: PaymentRequestProvider, params?: PaymentRequestStartParams) => Promise<void>;
};

declare const LoadedEventPayload = z.object({
    type: z.literal(EventType.enum.LOADED),
    sessionId: RequiredString,
    totalAmountAtoms: z.number(),
    currency: OptionalString,
    checkoutPaymentMethods: z.array(CheckoutPaymentMethod),
});

declare type LoadedEventPayload = z.infer<typeof LoadedEventPayload>;

declare class LoadedOncePublisher<T> {
    private _current: CurrentStatus<T>;
    private _subject: Subject<T>;

    constructor() {
        this._current = { status: 'initial', isSuccess: false };
        this._subject = new Subject<T>();
    }

    set = (value: T) => {
        if (this._current.status === 'success') {
            throw new Error('LoadedOnce is already in success state');
        }
        this._current = { status: 'success', isSuccess: true, loadedValue: value };
        this._subject.next(value);
        this._subject.complete();
    };

    throw = (error: unknown, errMsg: string) => {
        if (this._current.status === 'success') {
            throw new Error('LoadedOnce is already in success state');
        }
        this._current = { status: 'error', isSuccess: false, error, errMsg };
        this._subject.error(error);
        // Do not complete the subject since a set() call might be made after this
    };

    get current() {
        return this._current;
    }

    getValueIfLoadedElse = <T_ELSE>(valueIfNotLoaded: T_ELSE): T | T_ELSE => {
        if (this._current.status === 'success') return this._current.loadedValue;
        return valueIfNotLoaded;
    };

    subscribe = (fn: (value: Exclude<CurrentStatus<T>, InitialStatus>) => void) => {
        if (this._current.status === 'success') {
            fn(this._current);
            return;
        }

        if (this._current.status === 'error') {
            fn(this._current);
            // Do not return, as we will still have the fn subscribed to the subject
        }

        const subscription = this._subject.subscribe({
            next: () => {
                if (this._current.status !== 'success') {
                    throw new Error('Invalid state (next): please make sure to update _current before the subject');
                }
                fn(this._current);
                subscription.unsubscribe();
            },
            error: () => {
                if (this._current.status !== 'error') {
                    throw new Error('Invalid state (error): please make sure to update _current before the subject');
                }
                fn(this._current);
                // Do not unsubscribe
            },
        });
    };

    waitForLoad = (timeoutConfig: { timeoutSec: number; timeoutErrMsg: string }): Promise<T> => {
        if (this._current.status === 'success') return Promise.resolve(this._current.loadedValue);
        if (this._current.status === 'error') return Promise.reject(this._current.error);
        if (this._current.status !== 'initial') assertNever(this._current);

        const timeoutParams = {
            first: timeoutConfig.timeoutSec * 1000,
            with: () => throwError(() => new Error(timeoutConfig.timeoutErrMsg)),
        };

        /*
        * Note: lastValueFrom converts the observable to a promise (https://rxjs.dev/api/index/function/lastValueFrom)
        *
        * We use lastValueFrom to wait for the observable to close successfully before resolving the Promise.
        * To avoid hanging threads forever, we use timeoutParams to throw an error if it takes too long.
        * firstValueFrom is theoretically also usable, but it might result in bugs
        * if we do decide to emit more values in this Observable/Subject in the future.
        *
        * For more details, see: https://rxjs.dev/deprecations/to-promise
        */
        return lastValueFrom(this._subject.pipe(timeout(timeoutParams)));
    };
}

declare interface LoopConnectConfig {
    apiKey: string;
    entityId: string;
    merchantId?: string;
    environment?: Environment | `${Environment}`;
    customStyles?: CustomStyles;
    onInitialized?: (detail: InitializedEvent) => void;
    onInitFailed?: (detail: InitFailedEvent) => void;
    onWalletChange?: (detail: WalletChangeEvent) => void;
    onNetworkChange?: (detail: NetworkChangeEvent) => void;
}

declare type LoopCryptoFlowCustomParams =
| {
    customer: Omit<CustomerResponse, 'paymentMethods' | 'merchants' | 'dateCreated'>;
    payin: PayInCompleteEvent;
    success: true;
}
| {
    failureDetails: PayInFailedEvent;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    debugInfo?: Record<string, any>;
    success: false;
}
| undefined;

declare type LoopCryptoWidgetProps = {
    paymentUsdAmount: number;
    suggestedAuthorizationUsdAmount: number;
    subscriptionRefId?: string;
    customerRefId?: string;
    invoiceRefId?: string;
};

declare interface NetworkChangeEvent {
    id: number;
    name: string;
    chain: BlockchainNetwork;
}

declare type OjsContext = {
    /**
     * The secure token for the checkout session.
     */
    checkoutSecureToken: string;

    /**
     * The form element for the OJS form (non-CDE form).
     */
    formDiv: HTMLElement;

    /**
     * The elements/partial session ID.
     */
    elementsSessionId: string;

    /**
     * All the checkout payment methods available in the session.
     */
    checkoutPaymentMethods: CheckoutPaymentMethod[];

    /**
     * All the CDE connection objects (one for each CDE iframe).
     */
    anyCdeConnection: CdeConnection;

    /**
     * Custom init params for init flows.
     */
    customInitParams: CustomInitParams;

    /**
     * The base URL of the CDE iframe.
     */
    baseUrl: string;
};

declare type OjsFlow<T_PARAMS = unknown, T_INIT_RESULT extends InitOjsFlowResult = InitOjsFlowResult> = {
    init?: InitOjsFlow<T_INIT_RESULT>;
    run: RunOjsFlow<T_PARAMS, T_INIT_RESULT>;
};

declare type OjsFlowParams<T_PARAMS = void, T_INIT_RESULT = void> = {
    /**
     * Contains the context where OJS is run.
     * Ideally this only contains "background" context (OJS-level objects), and not flow-level objects.
     */
    context: OjsContext;

    /**
     * The checkout payment method object to be used for the flow.
     */
    checkoutPaymentMethod: CheckoutPaymentMethod;

    /**
     * Inputs from non-CDE fields (i.e. those which are part of the OJS form, but not the sensitive CDE form).
     */
    nonCdeFormInputs: Record<string, unknown>;

    /**
     * Form callbacks. Take note that these can be dynamically updated (but the object remains)
     */
    formCallbacks: FormCallbacks;

    /**
     * Custom parameters for the flow.
     */
    customParams: T_PARAMS;

    /**
     * The result of the InitOjsFlow function.
     */
    initResult: T_INIT_RESULT;
};

declare const OjsFlows = {
    // ✋ Note: For flows that require initialization, please add them to `init-flows.ts`

    // Common
    commonCC: {
        init: async () => {},
        run: runCommonCcFlow,
    },

    // Stripe
    stripePR: {
        init: initStripePrFlow,
        run: runStripePrFlow,
    },
    stripeLink: {
        init: initStripeLinkFlow,
        run: runStripeLinkFlow,
    },

    // Pockyt
    pockytPaypal: {
        run: runPockytPaypalFlow,
    },

    // Airwallex
    airwallexGooglePay: {
        init: initAirwallexGooglePayFlow,
        run: runAirwallexGooglePayFlow,
    },

    airwallexApplePay: {
        init: initAirwallexApplePayFlow,
        run: runAirwallexApplePayFlow,
    },

    // Loop
    loopCrypto: {
        init: initLoopFlow,
        run: runLoopFlow,
    },

    authorizeNetApplePay: {
        init: initAuthnetApplePayFlow,
        run: runAuthnetApplePayFlow,
    },

    authorizeNetGooglePay: {
        init: initAuthnetGooglePayFlow,
        run: runAuthnetGooglePayFlow,
    },
    // 👉 Add more flows here

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
} satisfies Record<string, OjsFlow<any, any>>;

declare type OnCheckoutError = (message: string, errorCode?: string) => void;

declare type OnCheckoutStarted = () => void;

declare type OnCheckoutSuccess = (
invoiceUrls: string[],
subscriptionIds: string[],
customerId: string,
processorsUsed: string[]
) => void;

declare type OnSetupPaymentMethodSuccess = (paymentMethodId: string) => void;

declare type OnValidationError = (field: AllFieldNames, errors: string[], elementId?: string) => void;

export declare class OpenPayForm {
    readonly config: Config;
    readonly formId: string;
    readonly formTarget: string;
    readonly ojsVersion: string;
    readonly ojsReleaseVersion: string;
    readonly formProperties: {
        height: string;
    };
    readonly referrer: string;
    readonly baseUrl: string;
    readonly formCallbacks: FormCallbacks;
    private registeredElements;
    private eventHandler;
    private connectionManager;
    static ojsFlows: typeof OjsFlows;
    private readonly cdeLoadEvent;
    private readonly anyCdeConn;
    private readonly context;
    readonly initFlows: {
        readonly stripePR: {
            publisher: LoadedOncePublisher<InitStripePrFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly stripeLink: {
            publisher: LoadedOncePublisher<InitStripeLinkFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly airwallexGooglePay: {
            publisher: LoadedOncePublisher<InitGooglePayFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly airwallexApplePay: {
            publisher: LoadedOncePublisher<InitApplePayFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly loop: {
            publisher: LoadedOncePublisher<InitLoopCryptoFlowSuccess>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly authorizeNetGooglePay: {
            publisher: LoadedOncePublisher<InitGooglePayFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
        readonly authorizeNetApplePay: {
            publisher: LoadedOncePublisher<InitApplePayFlowResult>;
            initialize: (initParams: InitOjsFlowParams) => Promise<void>;
        };
    };
    private deviceToken;
    constructor(config: Config);
    /**
     * Starts the form initialization process
     */
    private startFormInit;
    /**
     * Assign the instance to the window as a singleton
     */
    private static assignAsSingleton;
    /**
     * Get the singleton instance of OpenPayForm
     */
    static getInstance: () => OpenPayForm | null;
    get checkoutSecureToken(): string;
    setFormHeight: (height: string) => void;
    onCdeLoaded: (payload: LoadedEventPayload) => void;
    onCdeLoadError: (errMsg: string) => void;
    createElement: (elementValue: ElementTypeEnumValue, options?: ElementProps) => RegisteredElement;
    registerIframe: (type: ElementTypeEnum, frame: HTMLIFrameElement) => RegisteredElement;
    private buildQueryString;
    private connectToElement;
    private getFormDiv;
    /**
     * Builds the OJS context object. Take note that this is a pure function.
     */
    private static buildOjsFlowContext;
    /**
     * Runs the common credit card flow
     */
    submitCard: () => void;
    getAvailablePaymentMethods: () => ({
        name: string;
    } | {
        processor: PaymentProcessor;
        isAvailable: boolean;
        isLoading: boolean;
        startFlow: (customParams?: ApplePayFlowCustomParams) => Promise<void>;
        name: string;
    } | {
        processor: PaymentProcessor;
        isAvailable: boolean;
        isLoading: boolean;
        startFlow: (customParams?: GooglePayFlowCustomParams) => Promise<void>;
        name: string;
    } | {
        processor: "stripe";
        isAvailable: true;
        pr: PaymentRequest_2;
        availableProviders: {
            applePay: boolean;
            googlePay: boolean;
        };
        startFlow: (provider: PaymentRequestProvider, params?: PaymentRequestStartParams) => Promise<void>;
        name: string;
    } | {
        isAvailable: true;
        controller: StripeLinkController;
        name: string;
    } | {
        isAvailable: false;
        name: string;
    } | {
        isAvailable: true;
        widgetProps: LoopCryptoWidgetProps;
        initLoopConnectProps: LoopConnectConfig;
        name: string;
    })[] | undefined;
    generalSubmit: <T extends SubmitMethod>(method: T, settings?: SubmitSettings<T>) => Promise<void> | undefined;
    /**
     * Alias for submitCard
     */
    submit: () => void;
    destroy: () => void;
    /**
     * Updates form callbacks after initialization
     */
    updateCallbacks: (newCallbacks: AllCallbacks) => void;
}

/**
 * @deprecated Use interface PayInCreatedEvent for data returned from the creation of a PayIn
 */
declare interface PayInCompleteEvent extends PayinResponse {}

declare enum PayInFailed {
    INSUFFICIENT_BALANCE = 'insufficientBalance',
    INSUFFICIENT_AUTHORIZATION = 'insufficientAuthorization',
    SIGNED_MESSAGE_REQUIRED = 'signedMessageRequired',
    CUSTOMER_CREATION_FAILED = 'customerCreationFailed',
    PAYMENT_FAILED = 'paymentFailed',
    TRANSACTION_FAILED = 'transactionFailed',
}

declare interface PayInFailedData {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    error: Record<string, any> | undefined;
}

declare interface PayInFailedEvent {
    type: PayInFailed;
    message: string;
    data: PayInFailedData;
}

declare interface PayinPaymentMethodResponse
extends Omit<PaymentMethodResponse, 'merchantId' | 'entityId' | 'dateCreated'> {
    /**
     * The status of the payment method
     */
    status: 'ok' | 'insufficient_balance' | 'insufficient_authorization' | 'insufficient_balance_authorization';
}

declare interface PayinPayoutDestinationResponse
extends Omit<PayoutDestinationResponse, 'merchantId' | 'entityId' | 'isDefault' | 'dateCreated'> {}

declare interface PayinResponse {
    /**
     * The unique identifier for the payin
     * @example "8f47c6e9-2b3a-4d5c-9f8e-1a2b3c4d5e6f"
     */
    payinId: string;
    /**
     * The unique identifier of the merchant this payin is associated with
     * @example "67e55044-10b1-426f-9247-bb680e5fe0c8"
     */
    merchantId: string;
    /**
     * The amount to be paid, specified in either fiat or crypto based on amountType
     * @example "100.00"
     */
    amount: string;
    /**
     * The type of the amount, either "fiat" or "token"
     * @example "fiat"
     */
    amountType: 'fiat' | 'token';
    /**
     * The date the payment will take place, represented as a Unix timestamp
     * @example 1716211200
     */
    billDate: number;
    /**
     * The unique invoice identifier representing this payin transaction
     * @example "1234567890abcdef"
     */
    invoiceId: string;
    /**
     * (Optional) A description or note that provides additional context about this payin. This can be used to help identify or provide details about the payment for internal reference or customer communications.
     * @example "Payment for Developer plan"
     */
    description: string | null;
    /**
     * (Optional) The external invoice ID used to tie this payin to an invoice in an external system
     * @example "1234567890abcdef"
     */
    externalInvoiceRef: string | null;
    /**
     * The type of the payin, either "subscription" or "invoice"
     * @example "subscription"
     */
    payinType: 'subscription' | 'invoice';
    /**
     * The status of the payin, can be "scheduled", "pending", "completed", or "failed"
     * @example "scheduled"
     */
    payinStatus: 'scheduled' | 'pending' | 'completed' | 'failed' | 'canceled' | 'uncollectible' | 'draft';
    /**
     * The transaction details for the payin
     */
    transaction: PayinTransactionResponse | null;
    /**
     * The payment method used for this payin
     */
    paymentMethod: PayinPaymentMethodResponse;
    /**
     * The payout destination used for this payin
     */
    payoutDestination: PayinPayoutDestinationResponse;
    /**
     * The date the payin record was created, represented as a Unix timestamp in seconds.
     * @example 1716211200
     */
    dateCreated: number;
}

declare interface PayinTransactionResponse {
    /**
     * The transaction id generated by the blockchain network when the transaction is processed
     * @example "0xcfdfbb523c079e47e9a17ba236fa978257d4331e96ec43997e974b97522047fe"
     */
    transactionId: string;
    /**
     * The URL to view this transaction in a blockchain explorer. The specific explorer URL depends on the blockchain network the transaction was processed on
     * @example "https://etherscan.io/tx/0xcfdfbb523c079e47e9a17ba236fa978257d4331e96ec43997e974b97522047fe"
     */
    transactionUrl: string;
    /**
     * The actual token amount that was transferred  including all decimal places. For example - an amount of 1.99 USDC will result in a `amountTransferred` of "1990000"
     * @example "1990000"
     */
    amountTransferred: string | null;
    /**
     * The exchange rate that was applied when the payment was processed on the blockchain
     */
    exchangeRate: PaymentMethodTokenExchangeRate | null;
}

declare type PaymentDataRequest = google.payments.api.PaymentDataRequest;

declare interface PaymentMethodPreAuthorization {
    /**
     * The balance of the payment method formatted in the token amount
     * @example "100"
     */
    balance: string;
    /**
     * The authorization amount of the payment method formatted in the token amount
     * @example "49.9"
     */
    authorization: string;
}

declare interface PaymentMethodResponse {
    /**
     * The unique identifier for the payment method
     * @example "1234567890abcdef"
     */
    paymentMethodId: string;
    /**
     * The unique identifier of the merchant this payment method is associated with
     * @example "67e55044-10b1-426f-9247-bb680e5fe0c8"
     */
    merchantId: string;
    /**
     * The name of the payment method
     * @example "My Crypto Wallet"
     */
    paymentMethodName: string;
    /**
     * The customers that are associated with the payment method
     */
    customer: Omit<CustomerResponse, 'paymentMethods' | 'merchants' | 'dateCreated'>;
    /**
     * The blockchain network ID the payment method is associated with
     * @example 1
     */
    networkId: number;
    /**
     * The blockchain wallet address where payments will be sent from
     * @example "0x1234567890abcdef"
     */
    walletAddress: string;
    /**
     * Whether the payment method is the default payment method for wallet address
     * @example true
     */
    isDefault: boolean;
    /**
     * The token associated with the payment method
     */
    token: PaymentMethodToken;
    /**
     * The status of the payment method, including the wallet's balance and authorization status
     */
    preAuthorization: PaymentMethodPreAuthorization | null;
    /**
     * The date the payment method record was created, represented as a Unix timestamp in seconds.
     * @example 1716211200
     */
    dateCreated: number;
}

declare interface PaymentMethodToken extends Omit<TokenResponse, 'networkId'> {
    /**
     * The exchange rate of the token
     */
    exchangeRates: PaymentMethodTokenExchangeRate[];
}

declare interface PaymentMethodTokenExchangeRate {
    /**
     * The currency code. Only "USD" is supported at this time.
     * @example "USD"
     */
    currency: string;
    /**
     * The price of the token in the specified currency code. Accurate to 4 decimal places, e.g. a price of "1.9900" represents $1.99
     * @example "10000"
     */
    price: string;
    /**
     * The Unix timestamp (in seconds) when this exchange rate was last updated
     * @example 1715731200
     */
    timestamp: number;
}

declare const PaymentProcessor = z.enum(['stripe', 'airwallex', 'authorize_net']);

declare type PaymentProcessor = z.infer<typeof PaymentProcessor>;

declare const PaymentRequestProvider = z.enum(['apple_pay', 'google_pay']);

declare type PaymentRequestProvider = z.infer<typeof PaymentRequestProvider>;

declare type PaymentRequestStartParams = {
    overridePaymentRequest?: {
        amount: Amount;
        pending?: boolean;
        label?: string;
        applePayPaymentRequest?: ApplePayJS.ApplePayPaymentRequest;
    };
};

declare type PaymentRequestStatus = {
    isLoading: boolean;
    isAvailable: boolean;
    startFlow: (params?: PaymentRequestStartParams) => Promise<void>;
};

declare interface PayoutDestinationResponse {
    /**
     * The unique identifier for the payout destination
     * @example "1234567890abcdef"
     */
    payoutDestinationId: string;
    /**
     * The merchant ID associated with this payout destination
     * @example "1234567890abcdef"
     */
    merchantId: string;
    /**
     * The blockchain network ID the payout destination is associated with
     * @example 1
     */
    networkId: number;
    /**
     * The blockchain wallet address where payments will be sent. Must be a valid address for the specified network.
     * @example "0x1234567890abcdef"
     */
    walletAddress: string;
    /**
     * Whether the payout destination is the default payout destination for the merchant.
     * @example true
     */
    isDefault: boolean;
    /**
     * The date the payout destination record was created, represented as a Unix timestamp in seconds.
     * @example 1716211200
     */
    dateCreated: number;
}

declare type ProcessorSpecificSubmitSettings<T extends SubmitMethod = SubmitMethod> = {
    'pockyt-paypal': { useRedirectFlow?: boolean };
    'airwallex-google-pay': GooglePayFlowCustomParams;
    'airwallex-apple-pay': ApplePayFlowCustomParams;
    'authorizenet-google-pay': GooglePayFlowCustomParams;
    'authorizenet-apple-pay': ApplePayFlowCustomParams;
    'loop-crypto': LoopCryptoFlowCustomParams;
}[T];

declare type PRStatuses = Record<PaymentRequestProvider, PaymentRequestStatus>;

declare type RegisteredElement = {
    type: ElementType;
    node: HTMLIFrameElement;
    mount: (selector: string) => void;
};

declare type RunOjsFlow<T_PARAMS = undefined, T_INIT_RESULT = undefined> = (
params: OjsFlowParams<T_PARAMS, T_INIT_RESULT>
) => Promise<void>;

declare type StripeLinkController = {
    mountButton: () => void;
    dismountButton: () => void;
    waitForButtonToMount: () => Promise<HTMLElement>;
};

export declare const SubmitMethod = z.nativeEnum(SubmitMethods);

export declare type SubmitMethod = z.infer<typeof SubmitMethod>;

export declare enum SubmitMethods {
    pockytPaypal = 'pockyt-paypal',
    airwallexGooglePay = 'airwallex-google-pay',
    airwallexApplePay = 'airwallex-apple-pay',
    authorizenetGooglePay = 'authorizenet-google-pay',
    authorizenetApplePay = 'authorizenet-apple-pay',
    loopCrypto = 'loop-crypto',
}

export declare type SubmitSettings<T extends SubmitMethod = SubmitMethod> = ProcessorSpecificSubmitSettings<T> &
CommonSubmitSettings;

declare type SuccessStatus<T> = {
    status: 'success';
    isSuccess: true;
    loadedValue: T;
};

declare interface TokenResponse {
    /**
     * The unique identifier for the token used by the payment type.
     * @example "123e4567-e89b-12d3-a456-426614174000"
     */
    tokenId: string;
    /**
     * The blockchain network ID the token is associated with
     * @example 1
     */
    networkId: number;
    /**
     * The token symbol that identifies the token on the blockchain network
     * @example "USDC"
     */
    symbol: string;
    /**
     * The token contract address for the payment type
     * @example "0x1234567890abcdef"
     */
    address: string;
    /**
     * The number of decimal places used to represent token amounts
     * @example 6
     */
    decimals: number;
}

declare interface WalletChangeEvent {
    address: string;
    ens: string | undefined;
}

export { }
