import React, { lazy, useEffect, useState } from 'react';
import { ServerContext } from '@/state/server';
import getFileContents from '@/api/server/files/getFileContents';
import useRouter from 'use-react-router';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
import { httpErrorToHuman } from '@/api/http';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import saveFileContents from '@/api/server/files/saveFileContents';
import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcrumbs';
import { useParams } from 'react-router';
import FileNameModal from '@/components/server/files/FileNameModal';
import Can from '@/components/elements/Can';
import FlashMessageRender from '@/components/FlashMessageRender';
import PageContentBlock from '@/components/elements/PageContentBlock';
import ServerError from '@/components/screens/ServerError';

const LazyAceEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/AceEditor'));

export default () => {
    const [ error, setError ] = useState('');
    const { action } = useParams();
    const { history, location: { hash } } = useRouter();
    const [ loading, setLoading ] = useState(action === 'edit');
    const [ content, setContent ] = useState('');
    const [ modalVisible, setModalVisible ] = useState(false);

    const { id, uuid } = ServerContext.useStoreState(state => state.server.data!);
    const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);

    let fetchFileContent: null | (() => Promise<string>) = null;

    if (action !== 'new') {
        useEffect(() => {
            setLoading(true);
            setError('');
            getFileContents(uuid, hash.replace(/^#/, ''))
                .then(setContent)
                .catch(error => {
                    console.error(error);
                    setError(httpErrorToHuman(error));
                })
                .then(() => setLoading(false));
        }, [ uuid, hash ]);
    }

    const save = (name?: string) => {
        if (!fetchFileContent) {
            return;
        }

        setLoading(true);
        clearFlashes('files:view');
        fetchFileContent()
            .then(content => {
                return saveFileContents(uuid, name || hash.replace(/^#/, ''), content);
            })
            .then(() => {
                if (name) {
                    history.push(`/server/${id}/files/edit#/${name}`);
                    return;
                }

                return Promise.resolve();
            })
            .catch(error => {
                console.error(error);
                addError({ message: httpErrorToHuman(error), key: 'files:view' });
            })
            .then(() => setLoading(false));
    };

    if (error) {
        return (
            <ServerError
                message={error}
                onBack={() => history.goBack()}
            />
        );
    }

    return (
        <PageContentBlock>
            <FlashMessageRender byKey={'files:view'} className={'mb-4'}/>
            <FileManagerBreadcrumbs withinFileEditor={true} isNewFile={action !== 'edit'}/>
            {(name || hash.replace(/^#/, '')).endsWith('.pteroignore') &&
            <div className={'mb-4 p-4 border-l-4 bg-neutral-900 rounded border-cyan-400'}>
                <p className={'text-neutral-300 text-sm'}>
                    You're editing a <code className={'font-mono bg-black rounded py-px px-1'}>.pteroignore</code> file.
                    Any files or directories listed in here will be excluded from backups. Wildcards are supported by
                    using an asterisk (<code className={'font-mono bg-black rounded py-px px-1'}>*</code>). You can
                    negate a prior rule by prepending an exclamation point
                    (<code className={'font-mono bg-black rounded py-px px-1'}>!</code>).
                </p>
            </div>
            }
            <FileNameModal
                visible={modalVisible}
                onDismissed={() => setModalVisible(false)}
                onFileNamed={(name) => {
                    setModalVisible(false);
                    save(name);
                }}
            />
            <div className={'relative'}>
                <SpinnerOverlay visible={loading}/>
                <LazyAceEditor
                    initialModePath={hash.replace(/^#/, '') || 'plain_text'}
                    initialContent={content}
                    fetchContent={value => {
                        fetchFileContent = value;
                    }}
                    onContentSaved={() => save()}
                />
            </div>
            <div className={'flex justify-end mt-4'}>
                {action === 'edit' ?
                    <Can action={'file.update'}>
                        <button className={'btn btn-primary btn-sm'} onClick={() => save()}>
                            Save Content
                        </button>
                    </Can>
                    :
                    <Can action={'file.create'}>
                        <button className={'btn btn-primary btn-sm'} onClick={() => setModalVisible(true)}>
                            Create File
                        </button>
                    </Can>
                }
            </div>
        </PageContentBlock>
    );
};