import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from '@dash/components';
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { BsGoogle } from 'react-icons/bs';
import { FaFillDrip, FaPalette } from 'react-icons/fa';
import { ClientUtils, addStyleSheet, addStyleSheetRule } from '../../ClientUtils';
import { Doc } from '../../fields/Doc';
import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
import { DocServer } from '../DocServer';
import { Networking } from '../Network';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { MainViewModal } from '../views/MainViewModal';
import { GroupManager } from './GroupManager';
import './SettingsManager.scss';
import { SnappingManager, freeformScrollMode } from './SnappingManager';
import { undoable } from './UndoManager';

export enum ColorScheme {
    Dark = 'Dark',
    Light = 'Light',
    Custom = 'Custom',
    CoolBlue = 'CoolBlue',
    Cupcake = 'Cupcake',
}

@observer
export class SettingsManager extends React.Component<object> {
    // eslint-disable-next-line no-use-before-define
    public static Instance: SettingsManager;
    static _settingsStyle = addStyleSheet().sheet;
    @observable private _passwordResultText = '';
    @observable private _playgroundMode = false;

    @observable private _curr_password = '';
    @observable private _new_password = '';
    @observable private _new_confirm = '';
    @observable private _activeTab = 'Accounts';
    @observable private _isOpen = false;

    private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true));

    public closeMgr = action(() => {
        this._isOpen = false;
    });
    public openMgr = action(() => {
        this._isOpen = true;
    });

    private matchSystem = undoable(() => {
        if (Doc.UserDoc().userThemeSystem) {
            if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark);
            if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light);
        }
    }, 'match system theme');
    private setFreeformScrollMode = undoable((mode: string) => {
        Doc.UserDoc().freeformScrollMode = mode;
    }, 'set scroll mode');
    private selectUserMode = undoable((mode: string) => {
        Doc.noviceMode = mode === 'Novice';
    }, 'change user mode');
    private changeFontFamily = undoable((font: string) => {
        Doc.UserDoc().fontFamily = font;
    }, 'change font family');
    private switchUserBackgroundColor = undoable((color: string) => {
        Doc.UserDoc().userBackgroundColor = color;
        addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` });
    }, 'change background color');
    private switchUserColor = undoable((color: string) => {
        Doc.UserDoc().userColor = color;
    }, 'change user color');
    switchUserVariantColor = undoable((color: string) => {
        Doc.UserDoc().userVariantColor = color;
    }, 'change variant color');
    userThemeSystemToggle = undoable(() => {
        Doc.UserDoc().userThemeSystem = !Doc.UserDoc().userThemeSystem;
        this.matchSystem();
    }, 'change theme color');
    playgroundModeToggle = undoable(
        action(() => {
            this._playgroundMode = !this._playgroundMode;
            if (this._playgroundMode) {
                DocServer.Control.makeReadOnly();
                addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
            } else if (ClientUtils.CurrentUserEmail() !== 'guest') DocServer.Control.makeEditable();
        }),
        'set playgorund mode'
    );
    changeColorScheme = undoable(
        action((scheme: string) => {
            Doc.UserDoc().userTheme = scheme;
            switch (scheme) {
                case ColorScheme.Light:
                    this.switchUserColor('#323232');
                    this.switchUserBackgroundColor('#DFDFDF');
                    this.switchUserVariantColor('#BDDDF5');
                    break;
                case ColorScheme.Dark:
                    this.switchUserColor('#DFDFDF');
                    this.switchUserBackgroundColor('#323232');
                    this.switchUserVariantColor('#4476F7');
                    break;
                case ColorScheme.CoolBlue:
                    this.switchUserColor('#ADEAFF');
                    this.switchUserBackgroundColor('#060A15');
                    this.switchUserVariantColor('#3C51FF');
                    break;
                case ColorScheme.Cupcake:
                    this.switchUserColor('#3BC7FF');
                    this.switchUserBackgroundColor('#fffdf7');
                    this.switchUserVariantColor('#FFD7F3');
                    break;
                case ColorScheme.Custom:
                    break;
                default:
            }
        }),
        'change color scheme'
    );

    constructor(props: object) {
        super(props);
        makeObservable(this);
        SettingsManager.Instance = this;
        this.matchSystem();
        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
            if (Doc.UserDoc().userThemeSystem) {
                if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark);
                if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light);
            }
            // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
        });
        reaction(
            () => [SettingsManager.userBackgroundColor, SettingsManager.userColor, SettingsManager.userVariantColor],
            ([back, user, variant]) => {
                SnappingManager.SetUserBackgroundColor(back);
                SnappingManager.SetUserVariantColor(variant);
                SnappingManager.SetUserColor(user);
            },
            { fireImmediately: true }
        );
        SnappingManager.SettingsStyle = SettingsManager._settingsStyle;
    }

    @computed public static get userColor() {
        return StrCast(Doc.UserDoc().userColor);
    }

    @computed public static get userVariantColor() {
        return StrCast(Doc.UserDoc().userVariantColor);
    }

    @computed public static get userBackgroundColor() {
        return StrCast(Doc.UserDoc().userBackgroundColor);
    }

    @computed get colorsContent() {
        const schemeMap = Array.from(Object.keys(ColorScheme));
        const userTheme = StrCast(Doc.UserDoc().userTheme);
        return (
            <div style={{ width: '100%' }}>
                <Dropdown
                    formLabel="Theme"
                    size={Size.SMALL}
                    type={Type.TERT}
                    closeOnSelect={false}
                    selectedVal={userTheme}
                    setSelectedVal={scheme => {
                        this.changeColorScheme(scheme as string);
                        Doc.UserDoc().userThemeSystem = false;
                    }}
                    items={Object.keys(ColorScheme).map((scheme, i) => ({
                        text: schemeMap[i].replace(/([a-z])([A-Z])/, '$1 $2'),
                        val: scheme,
                    }))}
                    dropdownType={DropdownType.SELECT}
                    color={SettingsManager.userColor}
                    background={SettingsManager.userBackgroundColor}
                    fillWidth
                />
                <Toggle formLabel="Match System" toggleType={ToggleType.SWITCH} color={SettingsManager.userColor} toggleStatus={BoolCast(Doc.UserDoc().userThemeSystem)} onClick={this.userThemeSystemToggle} />

                {userTheme === ColorScheme.Custom && (
                    <Group formLabel="Custom Theme">
                        <ColorPicker
                            tooltip="User Color" //
                            color={SettingsManager.userColor}
                            type={Type.SEC}
                            icon={<FaFillDrip />}
                            selectedColor={SettingsManager.userColor}
                            setSelectedColor={this.switchUserColor}
                            setFinalColor={this.switchUserColor}
                        />
                        <ColorPicker
                            tooltip="User Background Color"
                            color={SettingsManager.userColor}
                            type={Type.SEC}
                            icon={<FaPalette />}
                            selectedColor={SettingsManager.userBackgroundColor}
                            setSelectedColor={this.switchUserBackgroundColor}
                            setFinalColor={this.switchUserBackgroundColor}
                        />
                        <ColorPicker
                            tooltip="User Variant Color"
                            color={SettingsManager.userColor}
                            type={Type.SEC}
                            icon={<FaPalette />}
                            selectedColor={SettingsManager.userVariantColor}
                            setSelectedColor={this.switchUserVariantColor}
                            setFinalColor={this.switchUserVariantColor}
                        />
                    </Group>
                )}
            </div>
        );
    }

    @computed get formatsContent() {
        return (
            <div className="prefs-content">
                <Toggle
                    formLabel="Show document header"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date';
                    }}
                    toggleStatus={Doc.UserDoc().layout_showTitle !== undefined}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel="Recognize Face Images"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().recognizeFaceImages = !Doc.UserDoc().recognizeFaceImages;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().recognizeFaceImages)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel="Show Full Toolbar"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().documentLinksButton_fullMenu = !Doc.UserDoc().documentLinksButton_fullMenu;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().documentLinksButton_fullMenu)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel="Recognize Ink Gestures"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().recognizeGestures = !Doc.UserDoc().recognizeGestures;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().recognizeGestures)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel="Hide Labels In Ink Shapes"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().activeHideTextLabels = !Doc.UserDoc().activeHideTextLabels;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().activeHideTextLabels)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel="Open Ink Docs in Lightbox"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                {/* <Toggle
                    formLabel="Show Link Lines"
                    formLabelPlacement="right"
                    toggleType={ToggleType.SWITCH}
                    onClick={() => {
                        Doc.UserDoc().showLinkLines = !Doc.UserDoc().showLinkLines;
                    }}
                    toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                /> */}
                <Group formLabel="Title Height">
                    <NumberDropdown
                        number={NumCast(Doc.UserDoc().headerHeight, 30)}
                        color={SettingsManager.userColor}
                        background={SettingsManager.userBackgroundColor}
                        numberDropdownType="slider"
                        min={6}
                        max={60}
                        step={2}
                        type={Type.TERT}
                        unit="px"
                        setNumber={val => {
                            Doc.UserDoc().headerHeight = val;
                        }}
                    />
                </Group>
            </div>
        );
    }

    @computed get appearanceContent() {
        return (
            <div className="tab-content appearances-content">
                <div className="tab-column">
                    <div className="tab-column-title">Colors</div>
                    <div className="tab-column-content">{this.colorsContent}</div>
                </div>
                <div className="tab-column">
                    <div className="tab-column-title">Formats</div>
                    <div className="tab-column-content">{this.formatsContent}</div>
                </div>
            </div>
        );
    }

    @computed get textContent() {
        const fontFamilies = ['Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text', 'Roboto'];

        return (
            <div className="tab-content appearances-content">
                <div className="tab-column">
                    <div className="tab-column-title">Text</div>
                    <div className="tab-column-content">
                        {/* <NumberInput/> */}
                        <Group formLabel="Default Font">
                            <NumberDropdown
                                color={SettingsManager.userColor}
                                background={SettingsManager.userBackgroundColor}
                                numberDropdownType="slider"
                                min={0}
                                max={50}
                                step={2}
                                type={Type.PRIM}
                                number={NumCast(Doc.UserDoc().fontSize, Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')))}
                                unit="px"
                                setNumber={val => {
                                    Doc.UserDoc().fontSize = val + 'px';
                                }}
                            />
                            <ColorPicker
                                color={SettingsManager.userColor}
                                background={SettingsManager.userBackgroundColor}
                                type={Type.PRIM}
                                defaultPickerType="Classic"
                                selectedColor={StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY)}
                                icon={<FontAwesomeIcon icon="palette" size="lg" />}
                                tooltip="default text background color"
                                label="background"
                                setSelectedColor={value => {
                                    Doc.UserDoc().textBackgroundColor = value;
                                    // if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
                                }}
                                setFinalColor={value => {
                                    Doc.UserDoc().textBackgroundColor = value;
                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
                                    // this.colorBatch?.end();
                                    // this.colorBatch = undefined;
                                }}
                            />
                            <Dropdown
                                items={fontFamilies.map(val => ({
                                    text: val,
                                    val: val,
                                    style: {
                                        fontFamily: val,
                                    },
                                }))}
                                closeOnSelect
                                dropdownType={DropdownType.SELECT}
                                type={Type.TERT}
                                selectedVal={StrCast(Doc.UserDoc().fontFamily)}
                                setSelectedVal={val => {
                                    this.changeFontFamily(val as string);
                                }}
                                color={SettingsManager.userColor}
                                background={SettingsManager.userBackgroundColor}
                                fillWidth
                            />
                        </Group>
                    </div>
                </div>
            </div>
        );
    }

    @computed get passwordContent() {
        return (
            <div className="password-content">
                <EditableText placeholder="Current password" type={Type.SEC} color={SettingsManager.userColor} background={SettingsManager.userBackgroundColor} val="" setVal={val => this.changeVal(val as string, 'curr')} fillWidth password />
                <EditableText placeholder="New password" type={Type.SEC} color={SettingsManager.userColor} background={SettingsManager.userBackgroundColor} val="" setVal={val => this.changeVal(val as string, 'new')} fillWidth password />
                <EditableText placeholder="Confirm new password" type={Type.SEC} color={SettingsManager.userColor} background={SettingsManager.userBackgroundColor} val="" setVal={val => this.changeVal(val as string, 'conf')} fillWidth password />
                {!this._passwordResultText ? null : <div className={`${this._passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this._passwordResultText}</div>}
                <Button type={Type.SEC} text="Forgot Password" color={SettingsManager.userColor} background={SettingsManager.userBackgroundColor} />
                <Button type={Type.TERT} text="Submit" onClick={this.changePassword} color={SettingsManager.userColor} background={SettingsManager.userBackgroundColor} />
            </div>
        );
    }

    @computed get accountOthersContent() {
        return (
            <div className="account-others-content">
                <Button type={Type.TERT} text="Connect to Google" color={SnappingManager.userColor} background={SnappingManager.userBackgroundColor} iconPlacement="left" icon={<BsGoogle />} onClick={() => this.googleAuthorize()} />
            </div>
        );
    }

    @computed get accountsContent() {
        return (
            <div className="tab-content accounts-content">
                <div className="tab-column">
                    <div className="tab-column-title">Password</div>
                    <div className="tab-column-content">{this.passwordContent}</div>
                </div>
                <div className="tab-column">
                    <div className="tab-column-title">Others</div>
                    <div className="tab-column-content">{this.accountOthersContent}</div>
                </div>
            </div>
        );
    }

    @computed get modesContent() {
        return (
            <div className="tab-content modes-content">
                <div className="tab-column">
                    <div className="tab-column-title">Modes</div>
                    <div className="tab-column-content">
                        <Dropdown
                            formLabel="Mode"
                            closeOnSelect
                            items={[
                                {
                                    text: 'Novice',
                                    description: 'Novice mode is a user-friendly setting designed to cater to those who are new to Dash',
                                    val: 'Novice',
                                },
                                {
                                    text: 'Developer',
                                    description:
                                        'Developer mode is an advanced setting that grants you greater control and access to the underlying mechanics and tools of a software or system. Developer mode is still under development as there are experimental features.',
                                    val: 'Developer',
                                },
                            ]}
                            selectedVal={Doc.noviceMode ? 'Novice' : 'Developer'}
                            setSelectedVal={val => {
                                this.selectUserMode(val as string);
                            }}
                            dropdownType={DropdownType.SELECT}
                            type={Type.TERT}
                            placement="bottom-start"
                            color={SettingsManager.userColor}
                            background={SettingsManager.userBackgroundColor}
                            fillWidth
                        />
                        <Toggle formLabel="Playground Mode" toggleType={ToggleType.SWITCH} toggleStatus={this._playgroundMode} onClick={this.playgroundModeToggle} color={SettingsManager.userColor} />
                    </div>
                    <div className="tab-column-title" style={{ marginTop: 20, marginBottom: 10 }}>
                        Freeform Navigation
                    </div>
                    <div className="tab-column-content">
                        <Dropdown
                            formLabel="Scroll Mode"
                            closeOnSelect
                            items={[
                                {
                                    text: 'Scroll to Pan',
                                    description: 'Scrolling pans canvas, shift + scrolling zooms',
                                    val: freeformScrollMode.Pan,
                                },
                                {
                                    text: 'Scroll to Zoom',
                                    description: 'Scrolling zooms canvas',
                                    val: freeformScrollMode.Zoom,
                                },
                            ]}
                            selectedVal={StrCast(Doc.UserDoc().freeformScrollMode, 'zoom')}
                            setSelectedVal={val => this.setFreeformScrollMode(val as string)}
                            dropdownType={DropdownType.SELECT}
                            type={Type.TERT}
                            placement="bottom-start"
                            color={SettingsManager.userColor}
                            background={SettingsManager.userBackgroundColor}
                        />
                    </div>
                </div>
                <div className="tab-column">
                    <div className="tab-column-title">Permissions</div>
                    <div className="tab-column-content">
                        <Button text="Manage Groups" type={Type.TERT} color={SnappingManager.userColor} background={SnappingManager.userBackgroundColor} onClick={() => GroupManager.Instance?.open()} />
                        <Toggle
                            toggleType={ToggleType.SWITCH}
                            formLabel="Default access private"
                            color={SettingsManager.userColor}
                            toggleStatus={BoolCast(Doc.defaultAclPrivate)}
                            onClick={action(() => {
                                Doc.defaultAclPrivate = !Doc.defaultAclPrivate;
                            })}
                        />
                        <Toggle
                            toggleType={ToggleType.SWITCH}
                            formLabel="Enable Sharing UI"
                            color={SettingsManager.userColor}
                            toggleStatus={BoolCast(Doc.IsSharingEnabled)}
                            onClick={action(() => {
                                Doc.IsSharingEnabled = !Doc.IsSharingEnabled;
                            })}
                        />
                        <Toggle
                            toggleType={ToggleType.SWITCH}
                            formLabel="Disable Info UI"
                            color={SettingsManager.userColor}
                            toggleStatus={BoolCast(Doc.IsInfoUIDisabled)}
                            onClick={action(() => {
                                Doc.IsInfoUIDisabled = !Doc.IsInfoUIDisabled;
                            })}
                        />
                    </div>
                </div>
            </div>
        );
    }

    private get settingsInterface() {
        // const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
        // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];

        const tabs = [
            { title: 'Accounts', ele: this.accountsContent },
            { title: 'Modes', ele: this.modesContent },
            { title: 'Appearance', ele: this.appearanceContent },
            { title: 'Text', ele: this.textContent },
        ];
        return (
            <div className="settings-interface">
                <div className="settings-panel" style={{ background: SettingsManager.userColor }}>
                    <div className="settings-tabs">
                        {tabs.map(tab => {
                            const isActive = this._activeTab === tab.title;
                            return (
                                <div
                                    key={tab.title}
                                    style={{
                                        background: isActive ? SettingsManager.userBackgroundColor : SettingsManager.userColor,
                                        color: isActive ? SettingsManager.userColor : SettingsManager.userBackgroundColor,
                                    }}
                                    className={'tab-control ' + (isActive ? 'active' : 'inactive')}
                                    onClick={action(() => {
                                        this._activeTab = tab.title;
                                    })}>
                                    {tab.title}
                                </div>
                            );
                        })}
                    </div>

                    <div className="settings-user">
                        <div style={{ color: SettingsManager.userBackgroundColor }}>{DashVersion}</div>
                        <div className="settings-username" style={{ color: SettingsManager.userBackgroundColor }}>
                            {ClientUtils.CurrentUserEmail()}
                        </div>
                        <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} onClick={() => window.location.assign(ClientUtils.prepend('/logout'))} />
                    </div>
                </div>

                <div className="close-button">
                    <Button icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={this.closeMgr} color={SettingsManager.userColor} />
                </div>

                <div className="settings-content" style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}>
                    {tabs.map(tab => (
                        <div key={tab.title} className={'tab-section ' + (this._activeTab === tab.title ? 'active' : 'inactive')}>
                            {tab.ele}
                        </div>
                    ))}
                </div>
            </div>
        );
    }

    private changePassword = async () => {
        if (!(this._curr_password && this._new_password && this._new_confirm)) {
            runInAction(() => {
                this._passwordResultText = "Error: Hey, we're missing some fields!";
            });
        } else {
            const passwordBundle = { curr_pass: this._curr_password, new_pass: this._new_password, new_confirm: this._new_confirm };
            const reset = await Networking.PostToServer('/internalResetPassword', passwordBundle);
            const { error } = reset as { error: { msg: string }[] };
            runInAction(() => {
                this._passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!';
            });
        }
    };

    @action
    changeVal = (value: string, pass: string) => {
        switch (pass) {
            case 'curr': this._curr_password = value; break;
            case 'new':  this._new_password = value;  break;
            case 'conf': this._new_confirm = value;  break;
            default:
        } // prettier-ignore
    };

    render() {
        return (
            <MainViewModal
                contents={this.settingsInterface}
                isDisplayed={this._isOpen}
                interactive
                closeOnExternalClick={this.closeMgr}
                dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.UserDoc().userColor, 'string', null) }}
            />
        );
    }
}
