import React, { useState, useEffect, useCallback } from 'react';
import { useAuth } from '../../../lib/useAuth';
import { Box, CircularProgress, IconButton, Typography, FormControl, Select, MenuItem, TextField, Menu, Button, Card, CardHeader, CardContent, CardActions } from '@material-ui/core';
import { getIntegrations, createIntegration, modifyIntegration, deleteIntegration } from '../../../lib/backend/integrations';
import AddIcon from '@material-ui/icons/Add';
import EditIcon from '@material-ui/icons/Edit';
import CheckIcon from '@material-ui/icons/Check';
import DeleteIcon from '@material-ui/icons/Delete';
import * as serverApi from '../../../api';
import StoreLinkIcon, { ExternalStore } from '../../common/StoreLinkIcon';
import { v4 as genUuid } from 'uuid';
import { testSpotify } from '../../../lib/integration/spotify/spotifyClientCreds';
let _ = require('lodash')

interface EditorIntegrationState extends serverApi.IntegrationDetailsResponse {
    secretDetails?: any,
}

interface EditIntegrationProps {
    upstreamId: number | null,
    integration: EditorIntegrationState,
    original: EditorIntegrationState,
    editing: boolean,
    submitting: boolean,
    onChange: (p: EditorIntegrationState, editing: boolean) => void,
    onSubmit: () => void,
    onDelete: () => void,
}

function EditSpotifyClientCredentialsDetails(props: {
    clientId: string,
    clientSecret: string | null,
    editing: boolean,
    onChangeClientId: (v: string) => void,
    onChangeClientSecret: (v: string) => void,
}) {
    return <Box>
        <Box mt={1} mb={1}>
            <TextField
                variant="outlined"
                disabled={!props.editing}
                value={props.clientId || ""}
                label="Client id"
                fullWidth
                onChange={(e: any) => props.onChangeClientId(e.target.value)}
            />
        </Box>
        <Box mt={1} mb={1}>
            <TextField
                variant="outlined"
                disabled={!props.editing}
                value={props.clientSecret === null ? "••••••••••••••••" : props.clientSecret}
                label="Client secret"
                fullWidth
                onChange={(e: any) => {
                    props.onChangeClientSecret(e.target.value)
                }}
                onFocus={(e: any) => {
                    if(props.clientSecret === null) {
                        // Change from dots to empty input
                        console.log("Focus!")
                        props.onChangeClientSecret('');
                    }
                }}
            />
        </Box>
    </Box>;
}

function EditIntegration(props: EditIntegrationProps) {
    let IntegrationHeaders: Record<any, any> = {
        [serverApi.IntegrationType.SpotifyClientCredentials]:
            <Box display="flex" alignItems="center">
                <Box mr={1}><StoreLinkIcon
                    style={{ height: '40px', width: '40px' }}
                    whichStore={ExternalStore.Spotify}
                /></Box>
                <Typography>Spotify (using Client Credentials)</Typography>
            </Box>
    }
    let IntegrationDescription: Record<any, any> = {
        [serverApi.IntegrationType.SpotifyClientCredentials]:
            <Typography>
                This integration allows using the Spotify API to make requests that are
                tied to any specific user, such as searching items and retrieving item
                metadata.<br/>
                Please see the Spotify API documentation on how to generate a client ID
                and client secret. Once set, you will only be able to overwrite the secret
                here, not read it.
            </Typography>
    }

    return <Card variant="outlined">
        <CardHeader
            avatar={
                IntegrationHeaders[props.integration.type]
            }
        >
        </CardHeader>
        <CardContent>
            <Box mb={2}>{IntegrationDescription[props.integration.type]}</Box>
            <Box mt={1} mb={1}>
                <TextField
                    variant="outlined"
                    value={props.integration.name || ""}
                    label="Integration name"
                    fullWidth
                    disabled={!props.editing}
                    onChange={(e: any) => props.onChange({
                        ...props.integration,
                        name: e.target.value,
                    }, props.editing)}
                />
            </Box>
            {props.integration.type === serverApi.IntegrationType.SpotifyClientCredentials &&
                <EditSpotifyClientCredentialsDetails
                    clientId={props.integration.details.clientId}
                    clientSecret={
                        (props.integration.secretDetails &&
                            props.integration.secretDetails.clientSecret !== null) ?
                            props.integration.secretDetails.clientSecret : null}
                    editing={props.editing}
                    onChangeClientId={(v: string) => props.onChange({
                        ...props.integration,
                        details: {
                            ...props.integration.details,
                            clientId: v,
                        }
                    }, props.editing)}
                    onChangeClientSecret={(v: string) => props.onChange({
                        ...props.integration,
                        secretDetails: {
                            ...props.integration.secretDetails,
                            clientSecret: v,
                        }
                    }, props.editing)}
                />
            }
        </CardContent>
        <CardActions>
            {!props.editing && !props.submitting && <IconButton
                onClick={() => { props.onChange(props.integration, true); }}
            ><EditIcon /></IconButton>}
            {props.editing && !props.submitting && <IconButton
                onClick={() => { props.onSubmit(); }}
            ><CheckIcon /></IconButton>}
            {!props.submitting && <IconButton
                onClick={() => { props.onDelete(); }}
            ><DeleteIcon /></IconButton>}
            {!props.submitting && !props.editing && props.upstreamId !== null && <Button
                onClick={() => testSpotify(props.upstreamId || 0)}
            >Test</Button>}
            {props.submitting && <CircularProgress />}
        </CardActions>
    </Card>
}

function AddIntegrationMenu(props: {
    position: null | number[],
    open: boolean,
    onClose: () => void,
    onAdd: (type: serverApi.IntegrationType) => void,
}) {
    const pos = props.open && props.position ?
        { left: props.position[0], top: props.position[1] }
        : { left: 0, top: 0 }

    return <Menu
        open={props.open}
        anchorReference="anchorPosition"
        anchorPosition={pos}
        keepMounted
        onClose={props.onClose}
    >
        <MenuItem
            onClick={() => {
                props.onAdd(serverApi.IntegrationType.SpotifyClientCredentials);
                props.onClose();
            }}
        >Spotify</MenuItem>
    </Menu>
}

export default function IntegrationSettingsEditor(props: {}) {
    interface EditorState {
        id: string, //uniquely identifies this editor in the window.
        upstreamId: number | null, //back-end ID for this integration if any.
        integration: EditorIntegrationState,
        original: EditorIntegrationState,
        editing: boolean,
        submitting: boolean,
    }
    let [editors, setEditors] = useState<EditorState[] | null>(null);
    const [addMenuPos, setAddMenuPos] = React.useState<null | number[]>(null);

    const onOpenAddMenu = (e: any) => {
        setAddMenuPos([e.clientX, e.clientY])
    };
    const onCloseAddMenu = () => {
        setAddMenuPos(null);
    };

    const submitEditor = (state: EditorState) => {
        let integration: any = state.integration;

        if (state.upstreamId === null) {
            if (!state.integration.secretDetails) {
                throw new Error('Cannot create an integration without its secret details set.')
            }
            createIntegration(integration).then((response: any) => {
                if (!response.id) {
                    throw new Error('failed to submit integration.')
                }
                let cpy = _.cloneDeep(editors);
                cpy.forEach((s: any) => {
                    if (s.id === state.id) {
                        s.submitting = false;
                        s.editing = false;
                        s.upstreamId = response.id;
                    }
                })
                setEditors(cpy);
            })
        } else {
            modifyIntegration(state.upstreamId, integration).then(() => {
                let cpy = _.cloneDeep(editors);
                cpy.forEach((s: any) => {
                    if (s.id === state.id) {
                        s.submitting = false;
                        s.editing = false;
                    }
                })
                setEditors(cpy);
            })
        }
    }

    const deleteEditor = (state: EditorState) => {
        let promise: Promise<void> = state.upstreamId ?
            deleteIntegration(state.upstreamId) :
            (async () => { })();

        promise.then((response: any) => {
            let cpy = _.cloneDeep(editors).filter(
                (e: any) => e.id !== state.id
            );
            setEditors(cpy);
        })
    }

    useEffect(() => {
        getIntegrations()
            .then((integrations: serverApi.ListIntegrationsResponse) => {
                setEditors(integrations.map((i: any, idx: any) => {
                    return {
                        integration: { ...i },
                        original: { ...i },
                        id: genUuid(),
                        editing: false,
                        submitting: false,
                        upstreamId: i.id,
                    }
                }));
            });
    }, []);

    return <>
        <Box>
            {editors === null && <CircularProgress />}
            {editors && <Box display="flex" flexDirection="column" alignItems="center" flexWrap="wrap">
                {editors.map((state: EditorState) => <Box m={1} width="90%">
                    <EditIntegration
                        upstreamId={state.upstreamId}
                        integration={state.integration}
                        original={state.original}
                        editing={state.editing}
                        submitting={state.submitting}
                        onChange={(p: EditorIntegrationState, editing: boolean) => {
                            if (!editors) {
                                throw new Error('cannot change editors before loading integrations.')
                            }
                            let cpy: EditorState[] = _.cloneDeep(editors);
                            cpy.forEach((s: any) => {
                                if (s.id === state.id) {
                                    s.integration = p;
                                    s.editing = editing;
                                }
                            })
                            setEditors(cpy);
                        }}
                        onSubmit={() => {
                            if (!editors) {
                                throw new Error('cannot submit editors before loading integrations.')
                            }
                            let cpy: EditorState[] = _.cloneDeep(editors);
                            cpy.forEach((s: EditorState) => {
                                if (s.id === state.id) {
                                    s.submitting = true;
                                    s.integration.secretDetails = undefined;
                                }
                            })
                            setEditors(cpy);
                            submitEditor(state);
                        }}
                        onDelete={() => {
                            if (!editors) {
                                throw new Error('cannot submit editors before loading integrations.')
                            }
                            let cpy: EditorState[] = _.cloneDeep(editors);
                            cpy.forEach((s: any) => {
                                if (s.id === state.id) {
                                    s.submitting = true;
                                }
                            })
                            setEditors(cpy);
                            deleteEditor(state);
                        }}
                    />
                </Box>)}
                <IconButton onClick={onOpenAddMenu}>
                    <AddIcon />
                </IconButton>
            </Box>}
        </Box>
        <AddIntegrationMenu
            position={addMenuPos}
            open={addMenuPos !== null}
            onClose={onCloseAddMenu}
            onAdd={(type: serverApi.IntegrationType) => {
                let cpy = _.cloneDeep(editors);
                cpy.push({
                    integration: {
                        type: serverApi.IntegrationType.SpotifyClientCredentials,
                        details: {
                            clientId: '',
                        },
                        secretDetails: {
                            clientSecret: '',
                        },
                        name: '',
                    },
                    original: null,
                    id: genUuid(),
                    editing: true,
                    submitting: false,
                    upstreamId: null,
                })
                setEditors(cpy);
            }}
        />
    </>;
}