import * as React from 'react';
import type {
    PaymentComponentProps,
    PaymentComponentState,
} from '../../../../types/elements';

export default class BaseElement<
    T extends PaymentComponentProps,
    S extends PaymentComponentState,
> extends React.Component<T, S> {
    readonly state = {
        element: null,
        mounted: false,
        ready: false,
    } as S;

    protected elementNode: HTMLDivElement | null = null;

    componentWillUnmount() {
        if (this.state.mounted && !this.state.element) {
            console.warn(
                `WARNING Element does not exist, please fix the setupElement method and add setState({element})`,
            );
            return;
        }
        if (this.state.element) {
            this.state.element.destroy();
        }
    }

    componentDidMount() {
        this.handleSetupElement();
    }

    setupElement() {
        throw new Error(`Please implement method setupElement`);
    }

    handleSetupElement() {
        if (!this.props.Framepay.ready) {
            /**
             * The remote api isn't ready
             */
            return;
        }
        if (this.state.mounted) {
            /**
             * The field already mounted
             */
            return;
        }
        if (!this.elementNode) {
            /**
             * Component dom element not mounted
             */
            return;
        }
        /**
         * Setup field
         */
        // @ts-expect-error todo fix type issue
        // eslint-disable-next-line react/no-direct-mutation-state
        this.state.mounted = true;
        this.setupElement();
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    shouldComponentUpdate(nextProps: any, nextState: any) {
        // we can't use the componentDidUpdate, componentWillReceiveProps methods
        // also, we can't return true here (to avoid the dom element re-render)
        // so, in that case we had to use that method as componentDidUpdate or componentWillReceiveProps
        // with some magic
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const rules: ReadonlyArray<any> = [
            [this.props.Framepay.ready, nextProps.Framepay.ready],
            [this.state.mounted, nextState.mounted],
            [this.state.ready, nextState.ready],
            [Boolean(this.state.element), Boolean(nextState.element)],
        ];

        const shouldUpdate = rules.find(([prev, next]) => prev !== next);

        if (shouldUpdate) {
            // @ts-expect-error todo fix typing issues
            this.props = nextProps;
            this.handleSetupElement();
            // @ts-expect-error todo fix typing issues
            // eslint-disable-next-line react/no-direct-mutation-state
            this.state = { ...nextState };
        }

        return false;
    }

    render() {
        return (
            <div
                id={this.props.id}
                ref={(node) => {
                    this.elementNode = node;
                }}
            />
        );
    }
}
