import React, { useState, useEffect } from 'react';
import { Box, CircularProgress, IconButton, Typography, MenuItem, TextField, Menu, Button, Card, CardHeader, CardContent, CardActions, Dialog, DialogTitle } 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 ClearIcon from '@material-ui/icons/Clear';
import * as serverApi from '../../../api/api';
import { v4 as genUuid } from 'uuid';
import { useIntegrations, IntegrationClasses, IntegrationState, isIntegrationState, makeDefaultIntegrationProperties, makeIntegration } from '../../../lib/integration/useIntegrations';
import Alert from '@material-ui/lab/Alert';
import Integration from '../../../lib/integration/Integration';
let _ = require('lodash')

// This widget is used to either display or edit a few
// specifically needed for Spotify Client credentials integration.
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>;
}

// An editing widget which is meant to either display or edit properties
// of an integration.
function EditIntegration(props: {
    upstreamId?: number,
    integration: serverApi.PostIntegrationRequest,
    editing?: boolean,
    showSubmitButton?: boolean | "InProgress",
    showDeleteButton?: boolean | "InProgress",
    showEditButton?: boolean,
    showTestButton?: boolean | "InProgress",
    showCancelButton?: boolean,
    flashMessage?: React.ReactFragment,
    isNew: boolean,
    onChange?: (p: serverApi.PostIntegrationRequest) => void,
    onSubmit?: (p: serverApi.PostIntegrationRequest) => void,
    onDelete?: () => void,
    onEdit?: () => void,
    onTest?: () => void,
    onCancel?: () => void,
}) {
    let IntegrationHeaders: Record<any, any> = {
        [serverApi.IntegrationImpl.SpotifyClientCredentials]:
            <Box display="flex" alignItems="center">
                <Box mr={1}>
                    {new IntegrationClasses[serverApi.IntegrationImpl.SpotifyClientCredentials](-1).getIcon({
                        style: { height: '40px', width: '40px' }
                    })}
                </Box>
                <Typography>Spotify (using Client Credentials)</Typography>
            </Box>,
        [serverApi.IntegrationImpl.YoutubeWebScraper]:
            <Box display="flex" alignItems="center">
                <Box mr={1}>
                    {new IntegrationClasses[serverApi.IntegrationImpl.YoutubeWebScraper](-1).getIcon({
                        style: { height: '40px', width: '40px' }
                    })}
                </Box>
                <Typography>Youtube Music (using experimental web scraper)</Typography>
            </Box>,
    }
    let IntegrationDescription: Record<any, any> = {
        [serverApi.IntegrationImpl.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>,
        [serverApi.IntegrationImpl.YoutubeWebScraper]:
            <Typography>
                This integration allows using the public Youtube Music search page to scrape
            for music metadata. <br />
            Because it relies on reverse-engineering of a web page that may change in the
            future, this is considered to be experimental and unstable. However, the music links acquired
            using this method are expected to remain reasonably stable.
        </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.onChange({
                        ...props.integration,
                        name: e.target.value,
                    })}
                />
            </Box>
            {props.integration.type === serverApi.IntegrationImpl.SpotifyClientCredentials &&
                <EditSpotifyClientCredentialsDetails
                    clientId={'clientId' in props.integration.details &&
                        props.integration.details.clientId || ""}
                    clientSecret={props.integration.secretDetails && 'clientSecret' in props.integration.secretDetails ?
                        props.integration.secretDetails.clientSecret :
                        (props.isNew ? "" : null)}
                    editing={props.editing || false}
                    onChangeClientId={(v: string) => props.onChange && props.onChange({
                        ...props.integration,
                        details: {
                            ...props.integration.details,
                            clientId: v,
                        }
                    })}
                    onChangeClientSecret={(v: string) => props.onChange && props.onChange({
                        ...props.integration,
                        secretDetails: {
                            ...props.integration.secretDetails,
                            clientSecret: v,
                        }
                    })}
                />
            }
            {props.flashMessage && props.flashMessage}
        </CardContent>
        <CardActions>
            {props.showEditButton && <IconButton
                onClick={props.onEdit}
            ><EditIcon /></IconButton>}
            {props.showSubmitButton && <IconButton
                onClick={() => props.onSubmit && props.onSubmit(props.integration)}
            ><CheckIcon /></IconButton>}
            {props.showDeleteButton && <IconButton
                onClick={props.onDelete}
            ><DeleteIcon /></IconButton>}
            {props.showCancelButton && <IconButton
                onClick={props.onCancel}
            ><ClearIcon /></IconButton>}
            {props.showTestButton && <Button
                onClick={props.onTest}
            >Test</Button>}
        </CardActions>
    </Card>
}

let EditorWithTest = (props: any) => {
    const [testFlashMessage, setTestFlashMessage] =
        React.useState<React.ReactFragment | undefined>(undefined);
    let { integration, ...rest } = props;
    return <EditIntegration
        onTest={() => {
            integration.integration.test({})
                .then(() => {
                    setTestFlashMessage(
                        <Alert severity="success">Integration is active.</Alert>
                    )
                })
        }}
        flashMessage={testFlashMessage}
        showTestButton={true}
        integration={integration.properties}
        {...rest}
    />;
}

function AddIntegrationMenu(props: {
    position: null | number[],
    open: boolean,
    onClose?: () => void,
    onAdd?: (type: serverApi.IntegrationImpl) => 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 && props.onAdd(serverApi.IntegrationImpl.SpotifyClientCredentials);
                props.onClose && props.onClose();
            }}
        >Spotify via Client Credentials</MenuItem>
        <MenuItem
            onClick={() => {
                props.onAdd && props.onAdd(serverApi.IntegrationImpl.YoutubeWebScraper);
                props.onClose && props.onClose();
            }}
        >Youtube Music Web Scraper</MenuItem>
    </Menu>
}

function EditIntegrationDialog(props: {
    open: boolean,
    onClose?: () => void,
    upstreamId?: number,
    integration: IntegrationState,
    onSubmit?: (p: serverApi.PostIntegrationRequest) => void,
    isNew: boolean,
}) {
    let [editingIntegration, setEditingIntegration] =
        useState<IntegrationState>(props.integration);

    useEffect(() => { setEditingIntegration(props.integration); }, [props.integration]);

    return <Dialog
        onClose={props.onClose}
        open={props.open}
        disableBackdropClick={true}
    >
        <DialogTitle>Edit Integration</DialogTitle>
        <EditIntegration
            isNew={props.isNew}
            editing={true}
            upstreamId={props.upstreamId}
            integration={editingIntegration.properties}
            showCancelButton={true}
            showSubmitButton={props.onSubmit !== undefined}
            showTestButton={false}
            onCancel={props.onClose}
            onSubmit={props.onSubmit}
            onChange={(i: any) => {
                setEditingIntegration({
                    ...editingIntegration,
                    properties: i,
                    integration: makeIntegration(i, editingIntegration.id),
                });
            }}
        />
    </Dialog>
}

export default function IntegrationSettings(props: {}) {
    const [addMenuPos, setAddMenuPos] = React.useState<null | number[]>(null);
    const [editingState, setEditingState] = React.useState<IntegrationState | null>(null);

    let {
        state: integrations,
        addIntegration,
        modifyIntegration,
        deleteIntegration,
        updateFromUpstream,
    } = useIntegrations();

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

    return <>
        <Box>
            {integrations === null && <CircularProgress />}
            {Array.isArray(integrations) && <Box display="flex" flexDirection="column" alignItems="center" flexWrap="wrap">
                {integrations.map((state: IntegrationState) => <Box m={1} width="90%">
                    <EditorWithTest
                        upstreamId={state.id}
                        integration={state}
                        showEditButton={true}
                        showDeleteButton={true}
                        onEdit={() => { setEditingState(state); }}
                        onDelete={() => {
                            deleteIntegration(state.id)
                                .then(updateFromUpstream)
                        }}
                    />
                </Box>)}
                <IconButton onClick={onOpenAddMenu}>
                    <AddIcon />
                </IconButton>
            </Box>}
        </Box>
        <AddIntegrationMenu
            position={addMenuPos}
            open={addMenuPos !== null}
            onClose={onCloseAddMenu}
            onAdd={(type: serverApi.IntegrationImpl) => {
                let p = makeDefaultIntegrationProperties(type);
                setEditingState({
                    properties: p,
                    integration: makeIntegration(p, -1),
                    id: -1,
                })
            }}
        />
        {editingState && <EditIntegrationDialog
            open={!(editingState === null)}
            onClose={() => { setEditingState(null); }}
            integration={editingState}
            isNew={editingState.id === -1}
            onSubmit={(v: serverApi.PostIntegrationRequest) => {
                if (editingState.id >= 0) {
                    const id = editingState.id;
                    setEditingState(null);
                    modifyIntegration(id, v)
                        .then(updateFromUpstream)
                } else {
                    setEditingState(null);
                    createIntegration({
                        ...v,
                        secretDetails: v.secretDetails || {},
                    })
                        .then(updateFromUpstream)
                }
            }}
        />}
    </>;
}