import * as React from 'react';
import { ContextProvider } from '../context';
import { injectScript } from '../dom-util';
import FramePayError from '../framepay-error';
import getFramepayApi from '../get-framepay-api';
import type { ProviderProps } from '../../../types/provider';
import type { FramePayContext } from '../../../types/context';
import type { TokenData } from '@rebilly/framepay';

export default class Provider extends React.Component<
    ProviderProps,
    FramePayContext
> {
    static readonly defaultProps = {
        injectScript: true,
        onError: () => ({}),
        onReady: () => ({}),
        onTokenReady: () => ({}),
    };

    readonly state: FramePayContext = {
        api: getFramepayApi(),
        error: null,
        ready: false,
    };

    componentDidMount() {
        if (this.props.injectScript) {
            injectScript({
                onError: () => this.onApiError(),
                onReady: () => this.onApiReady(),
            });
        }
    }

    onApiError() {
        return this.setState(
            {
                error: FramePayError.codes.remoteScriptError,
                ready: false,
            },
            () => {
                const error = FramePayError({
                    code: FramePayError.codes.remoteScriptError,
                });
                throw error;
            },
        );
    }

    async onApiReady() {
        const api = getFramepayApi();
        try {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { children, ...settings } = this.props;
            api.initialize(settings);
            api.on('ready', () => {
                this.setState({ ready: true, api, error: null });

                // call onReady callback
                if (this.props.onReady) {
                    this.props.onReady();
                }
            });
            api.on('error', (error: object) => {
                this.setState({
                    api,
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    error: (error as any)?.code ?? 0,
                });

                // call error callback
                if (this.props.onError) {
                    this.props.onError(error);
                }
            });
            api.on('token-ready', (token: TokenData) => {
                if (this.props.onTokenReady) {
                    this.props.onTokenReady(token);
                }
            });
        } catch (e) {
            return this.setState(
                {
                    error: FramePayError.codes.initializeError,
                    ready: false,
                },
                () => {
                    throw FramePayError({
                        code: FramePayError.codes.initializeError,
                        details: 'Api initialize error',
                        trace: e,
                    });
                },
            );
        }
    }

    render() {
        return (
            <ContextProvider value={this.state}>
                {this.props.children}
            </ContextProvider>
        );
    }
}
