import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Opt } from '../../fields/Doc';
import { Networking } from '../Network';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { MainViewModal } from '../views/MainViewModal';
import './GoogleAuthenticationManager.scss';
import { ObservableReactComponent } from '../views/ObservableReactComponent';

const AuthenticationUrl = 'https://accounts.google.com/o/oauth2/v2/auth';
const prompt = 'Paste authorization code here...';

@observer
export class GoogleAuthenticationManager extends ObservableReactComponent<object> {
    // eslint-disable-next-line no-use-before-define
    public static Instance: GoogleAuthenticationManager;
    private authenticationLink: Opt<string> = undefined;
    @observable private openState = false;
    @observable private authenticationCode: Opt<string> = undefined;
    @observable private showPasteTargetState = false;
    @observable private success: Opt<boolean> = undefined;
    @observable private displayLauncher = true;
    @observable private credentials: { user_info: { name: string; picture: string }; access_token: string } | undefined = undefined;
    private disposer: Opt<IReactionDisposer>;

    constructor(props: object) {
        super(props);
        makeObservable(this);
        GoogleAuthenticationManager.Instance = this;
    }

    private set isOpen(value: boolean) {
        runInAction(() => (this.openState = value));
    }

    private set shouldShowPasteTarget(value: boolean) {
        runInAction(() => (this.showPasteTargetState = value));
    }

    public cancel() {
        this.openState && this.resetState(0, 0);
    }

    public fetchOrGenerateAccessToken = async (displayIfFound = false) => {
        const response = await Networking.FetchFromServer('/readGoogleAccessToken');
        // if this is an authentication url, activate the UI to register the new access token
        if (new RegExp(AuthenticationUrl).test(response)) {
            this.isOpen = true;
            this.authenticationLink = response;

            // GETS STUCK AT THIS PROMISE!!
            return new Promise<string>(resolve => {
                this.disposer?.();
                this.disposer = reaction(
                    () => this.authenticationCode,
                    async authenticationCode => {
                        if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) {
                            resolve(authenticationCode);
                            this.disposer?.();
                            // const response2 = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode });
                            // runInAction(() => {
                            //     this.success = true;
                            //     this.credentials = response2 as { user_info: { name: string; picture: string }; access_token: string };
                            // });
                            // resolve((response2 as { access_token: string }).access_token);
                            this.resetState();
                        }
                    }
                );
            });
        }

        // otherwise, we already have a valid, stored access token and user info
        const response2 = JSON.parse(response) as { user_info: { name: string; picture: string }; access_token: string };
        if (displayIfFound) {
            runInAction(() => {
                this.success = true;
                this.credentials = response2;
            });
            this.resetState(-1, -1);
            this.isOpen = true;
        }
        return (response2 as { access_token: string }).access_token;
    };

    resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => {
        if (!visibleForMS && !fadesOutInMS) {
            runInAction(() => {
                this.isOpen = false;
                this.success = undefined;
                this.displayLauncher = true;
                this.credentials = undefined;
                this.shouldShowPasteTarget = false;
                this.authenticationCode = undefined;
            });
            return;
        }
        this.authenticationCode = undefined;
        this.displayLauncher = false;
        this.shouldShowPasteTarget = false;
        if (visibleForMS > 0 && fadesOutInMS > 0) {
            setTimeout(
                action(() => {
                    this.isOpen = false;
                    setTimeout(
                        action(() => {
                            this.success = undefined;
                            this.displayLauncher = true;
                            this.credentials = undefined;
                        }),
                        fadesOutInMS
                    );
                }),
                visibleForMS
            );
        }
    });

    private get renderPrompt() {
        return (
            <div className={'authorize-container'}>
                {this.displayLauncher ? (
                    <button
                        className={'dispatch'}
                        onClick={() => {
                            window.open(this.authenticationLink);
                            setTimeout(() => (this.shouldShowPasteTarget = true), 500);
                        }}
                        style={{ marginBottom: this.showPasteTargetState ? 15 : 0 }}>
                        Authorize a Google account...
                    </button>
                ) : null}
                {this.showPasteTargetState ? <input className={'paste-target'} onChange={action(e => (this.authenticationCode = e.currentTarget.value))} placeholder={prompt} /> : null}
                {this.credentials ? (
                    <>
                        <img className={'avatar'} src={this.credentials.user_info.picture} />
                        <span className={'welcome'}>Welcome to Dash, {this.credentials.user_info.name}</span>
                        <div
                            className={'disconnect'}
                            onClick={async () => {
                                await Networking.FetchFromServer('/revokeGoogleAccessToken');
                                this.resetState(0, 0);
                            }}>
                            Disconnect Account
                        </div>
                    </>
                ) : null}
            </div>
        );
    }

    private get dialogueBoxStyle() {
        const borderColor = this.success === undefined ? 'black' : this.success ? 'green' : 'red';
        return { borderColor, transition: '0.2s borderColor ease', zIndex: 1002 };
    }

    render() {
        return (
            <div className="hell">
                <MainViewModal isDisplayed={this.openState} interactive={true} contents={this.renderPrompt} dialogueBoxStyle={this.dialogueBoxStyle} overlayStyle={{ zIndex: 1001 }} closeOnExternalClick={action(() => (this.isOpen = false))} />
            </div>
        );
    }
}

ScriptingGlobals.add('GoogleAuthenticationManager', GoogleAuthenticationManager);
