/**
 * Copyright (c) TonTech.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

import { createContext, useCallback, useContext, useMemo, useState } from 'react';
import type { FC, PropsWithChildren } from 'react';
import type { SendTransactionReturnType } from '@ton/appkit';

import { useSendTransaction } from '../../hooks/use-send-transaction';
import type { SendRequest } from '../transaction/send';

export interface SendContextType {
    /** Function to submit the transaction */
    onSubmit: () => void;
    /** Whether the transaction is currently loading */
    isLoading: boolean;
    /** The error object if the transaction failed */
    error?: Error | null;
    /** The receipt of the successful transaction */
    receipt?: SendTransactionReturnType | null;
    /** Disable the button/interaction */
    disabled?: boolean;
}

export const SendContext = createContext<SendContextType>({
    onSubmit: () => {
        throw new Error('onSubmit is not defined');
    },
    isLoading: false,
});

export function useSendContext() {
    const context = useContext(SendContext);

    return context;
}

export interface SendProviderProps extends PropsWithChildren {
    /** The transaction request parameters */
    request: SendRequest;
    /** Callback when an error occurs */
    onError?: (error: Error) => void;
    /** Callback when the transaction is successful */
    onSuccess?: (response: SendTransactionReturnType) => void;
    /** Disable the button/interaction */
    disabled?: boolean;
}

export const SendProvider: FC<SendProviderProps> = ({ children, request, onError, onSuccess, disabled = false }) => {
    const [receipt, setReceipt] = useState<SendTransactionReturnType | null>(null);
    const [isPreparing, setIsPreparing] = useState(false);

    const {
        mutateAsync: sendTransaction,
        isPending,
        error: mutationError,
    } = useSendTransaction({
        mutation: {
            onSuccess: (data) => {
                setReceipt(data);
                onSuccess?.(data);
            },
            onError: (err) => {
                onError?.(err);
            },
        },
    });

    const handleSubmit = useCallback(async () => {
        if (disabled || isPreparing || isPending) {
            return;
        }

        setIsPreparing(true);

        try {
            const transactionRequest = typeof request === 'function' ? await request() : await request;

            if (!transactionRequest) {
                return;
            }

            await sendTransaction(transactionRequest);
        } catch (e) {
            const error = e instanceof Error ? e : new Error(String(e));
            onError?.(error);
        } finally {
            setIsPreparing(false);
        }
    }, [sendTransaction, request, disabled, isPreparing, isPending, onError]);

    const value = useMemo(
        () => ({
            error: mutationError,
            isLoading: isPreparing || isPending,
            onSubmit: handleSubmit,
            receipt,
            disabled,
        }),
        [mutationError, isPreparing, isPending, handleSubmit, receipt, disabled],
    );

    return <SendContext.Provider value={value}>{children}</SendContext.Provider>;
};
