import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable, 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 { Doc } from '../../fields/Doc';
import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Networking } from '../Network';
import { GestureOverlay } from '../views/GestureOverlay';
import { MainViewModal } from '../views/MainViewModal';
import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
import { GroupManager } from './GroupManager';
import './SettingsManager.scss';
import { undoBatch } from './UndoManager';

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

export enum freeformScrollMode {
    Pan = 'pan',
    Zoom = 'zoom',
}

@observer
export class SettingsManager extends React.Component<{}> {
    public static Instance: SettingsManager;
    static _settingsStyle = addStyleSheet();
    @observable public isOpen = false;
    @observable private passwordResultText = '';
    @observable private playgroundMode = false;

    @observable private curr_password = '';
    @observable private new_password = '';
    @observable private new_confirm = '';
    @observable activeTab = 'Accounts';

    @observable public static propertiesWidth: number = 0;
    @observable public static headerBarHeight: number = 0;

    constructor(props: {}) {
        super(props);
        SettingsManager.Instance = this;
        this.matchSystem();
        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
            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)
        });
    }
    matchSystem = () => {
        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);
        }
    };

    public close = action(() => (this.isOpen = false));
    public open = action(() => (this.isOpen = true));

    private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true));
    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 { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle);
            runInAction(() => (this.passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!'));
        }
    };

    @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);
    }

    @undoBatch selectUserMode = action((mode: string) => (Doc.noviceMode = mode === 'Novice'));
    @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined));
    @undoBatch changeFontFamily = action((font: string) => (Doc.UserDoc().fontFamily = font));
    @undoBatch changeFontSize = action((val: number) => (Doc.UserDoc().fontSize = val));
    @undoBatch switchUserBackgroundColor = action((color: string) => {
        Doc.UserDoc().userBackgroundColor = color;
        addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` });
    });
    @undoBatch switchUserColor = action((color: string) => (Doc.UserDoc().userColor = color));
    @undoBatch switchUserVariantColor = action((color: string) => (Doc.UserDoc().userVariantColor = color));
    @undoBatch userThemeSystemToggle = action(() => {
        Doc.UserDoc().userThemeSystem = !Doc.UserDoc().userThemeSystem;
        this.matchSystem();
    });
    @undoBatch playgroundModeToggle = action(() => {
        this.playgroundMode = !this.playgroundMode;
        if (this.playgroundMode) {
            DocServer.Control.makeReadOnly();
            addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
        } else Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable();
    });

    @undoBatch
    @action
    changeColorScheme = 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;
        }
    });

    @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}
                    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={e => (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={'Show Full Toolbar'}
                    formLabelPlacement={'right'}
                    toggleType={ToggleType.SWITCH}
                    onClick={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])}
                    toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel={'Show Button Labels'}
                    formLabelPlacement={'right'}
                    toggleType={ToggleType.SWITCH}
                    onClick={e => (FontIconBox.ShowIconLabels = !FontIconBox.ShowIconLabels)}
                    toggleStatus={FontIconBox.ShowIconLabels}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel={'Recognize Ink Gestures'}
                    formLabelPlacement={'right'}
                    toggleType={ToggleType.SWITCH}
                    onClick={e => (GestureOverlay.RecognizeGestures = !GestureOverlay.RecognizeGestures)}
                    toggleStatus={GestureOverlay.RecognizeGestures}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel={'Hide Labels In Ink Shapes'}
                    formLabelPlacement={'right'}
                    toggleType={ToggleType.SWITCH}
                    onClick={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)}
                    toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)}
                    size={Size.XSMALL}
                    color={SettingsManager.userColor}
                />
                <Toggle
                    formLabel={'Open Ink Docs in Lightbox'}
                    formLabelPlacement={'right'}
                    toggleType={ToggleType.SWITCH}
                    onClick={e => (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={e => (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}
                        numberDropdownType={'slider'}
                        min={6}
                        max={60}
                        step={2}
                        type={Type.TERT}
                        unit={'px'}
                        setNumber={val => console.log('GOT: ' + (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'];
        const fontSizes = ['7px', '8px', '9px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '32px', '48px', '72px'];

        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} numberDropdownType={'input'} min={0} max={50} step={2} type={Type.TERT} number={0} unit={'px'} setNumber={() => {}} />
                            <Dropdown
                                items={fontFamilies.map(val => {
                                    return {
                                        text: val,
                                        val: val,
                                        style: {
                                            fontFamily: val,
                                        },
                                    };
                                })}
                                closeOnSelect={true}
                                dropdownType={DropdownType.SELECT}
                                type={Type.TERT}
                                selectedVal={StrCast(Doc.UserDoc().fontFamily)}
                                setSelectedVal={val => {
                                    this.changeFontFamily(val as string);
                                }}
                                color={SettingsManager.userColor}
                                fillWidth
                            />
                        </Group>
                    </div>
                </div>
            </div>
        );
    }

    @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;
        }
    };

    @computed get passwordContent() {
        return (
            <div className="password-content">
                <EditableText placeholder="Current password" type={Type.SEC} color={SettingsManager.userColor} val={''} setVal={val => this.changeVal(val as string, 'curr')} fillWidth password />
                <EditableText placeholder="New password" type={Type.SEC} color={SettingsManager.userColor} val={''} setVal={val => this.changeVal(val as string, 'new')} fillWidth password />
                <EditableText placeholder="Confirm new password" type={Type.SEC} color={SettingsManager.userColor} 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} />
                <Button type={Type.TERT} text={'Submit'} onClick={this.changePassword} color={SettingsManager.userColor} />
            </div>
        );
    }

    @computed get accountOthersContent() {
        return (
            <div className="account-others-content">
                <Button type={Type.TERT} text={'Connect to Google'} 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>
        );
    }

    setFreeformScrollMode = (mode: string) => {
        Doc.UserDoc().freeformScrollMode = mode;
    };

    @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={true}
                            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}
                            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={true}
                            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}
                        />
                    </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} onClick={() => GroupManager.Instance?.open()} color={SettingsManager.userColor} />
                        <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))} />
                    </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 }}>
                            {Doc.CurrentUserEmail}
                        </div>
                        <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} onClick={() => window.location.assign(Utils.prepend('/logout'))} />
                    </div>
                </div>

                <div className="close-button">
                    <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} 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>
        );
    }

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