import React from 'react';
import {httpRequestJsonLocal} from 'core/helpers/network';
import {noop} from 'lodash';

/**
 * Wraps a promise in order to ignore AbortError and break the promise chain when it occurs.
 */
export function ignoreAbortError<T>(promise: Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
        promise
            .then(resolve)
            .catch((err) => {
                if (err?.name !== 'AbortError') {
                    reject(err);
                }
            });
    });
}

export class SuperdeskReactComponent<IProps = {}, IState = {}> extends React.PureComponent<IProps, IState> {
    public abortController: AbortController;

    /**
     * Will automatically abort in-progress asynchronous operations(only those in asyncHelpers) when unmounting.
     */
    public asyncHelpers: {
        fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
        httpRequestJsonLocal: <T>(options: Parameters<typeof httpRequestJsonLocal>[0]) => Promise<T>;
    };

    constructor(props: IProps) {
        super(props);

        const abortController = new AbortController();

        this.abortController = abortController;

        this.asyncHelpers = {
            fetch: (input: RequestInfo, init?: RequestInit): Promise<Response> => {
                return ignoreAbortError(
                    fetch(input, {...(init ?? {}), signal: abortController.signal}),
                );
            },
            httpRequestJsonLocal: (options: Parameters<typeof httpRequestJsonLocal>[0]) => {
                return ignoreAbortError(
                    httpRequestJsonLocal({...options, abortSignal: abortController.signal}),
                );
            },
        };

        // Save before overwriting.
        const componentWillUnmountChild = this.componentWillUnmount?.bind(this) ?? noop;

        this.componentWillUnmount = () => {
            abortController.abort();

            componentWillUnmountChild();
        };
    }
}
