import React, { useEffect, useState } from 'react';
import { ReactWidget } from '@jupyterlab/apputils'
import { Dialog as jupyterlabDialog } from '@jupyterlab/apputils';
import { Box, Button, Chip, Grid, Snackbar, TextField, Link, CircularProgress, Typography } from '@mui/material';
import CloudQueue from '@mui/icons-material/CloudQueue';
import { DEPLOYMENT_ENDPOINTS, DOCS } from './const/env';
import { requestAPI } from './utils/util';


export function showDeploymentDialog(panel: any, context: any) {
    const dialogWidget = new DeployWidget({ notebookPath: panel.context.contentsModel.path, metadata: panel.model.metadata, context: context });
    var deploymentDialog = new jupyterlabDialog({
        title: 'Deploy Notebook', body: dialogWidget, buttons: [
            {
                label: "Close",
                caption: "",
                className: "bg-info",
                accept: false,
                actions: [],
                displayType: "default",
                iconClass: "",
                iconLabel: "",
                ariaLabel: "Close dialog",
            }
        ]
    })
    return deploymentDialog.launch()
}

export function showUploadDialog(panel: any, context: any) {
    const dialogWidget = new UploadWidget({ notebookPath: panel.context.contentsModel.path, metadata: panel.model.metadata, context: context });
    var deploymentDialog = new jupyterlabDialog({
        title: 'Share Notebook', body: dialogWidget, buttons: [
            {
                ariaLabel: "Close dialog",
                label: "Close",
                caption: "",
                className: "bg-info",
                accept: false,
                actions: [],
                displayType: "default",
                iconClass: "",
                iconLabel: "",
            }
        ]
    })
    return deploymentDialog.launch()
}

const ErrorMessageArea = (props: any): JSX.Element => {


    // Special handle for missing file
    if (props?.message?.type == "missing file") {
        return (
            <div data-testid="error-message-area">

                {/* <Chip label={deploymentURL} variant="outlined" onClick={() => {
                                                    window.open(deploymentURL);
                                                    setIsShowSnackbar(true)
                                                    setSnakebarMessage("Deployment Success")
                                                }} /> */}
                {/* A requirements.txt file with dependencies is required to deploy your notebook. Please add it at {PATH}. To learn more, see the docs */}
                <Typography variant="subtitle1" gutterBottom> A <code>{props?.message?.detail?.fileName}</code> file with dependencies is required to deploy your notebook. Please add it at <code>{props?.message?.detail?.filePath}</code>. To learn more, see the <a target="_blank" rel="noopener noreferrer" href="https://docs.cloud.ploomber.io/en/latest/apps/jupyterlab-plugin.html">docs</a>
                </Typography>
            </div>
        )
    }
    else {
        return (
            <Typography variant="subtitle1" gutterBottom>{props?.message?.detail}</Typography>
        )
    }
}

export const DeployDialogContent = (props: any): JSX.Element => {
    // For deployment workflow, we need:
    // 1. The path of notebook file
    // 2. project_id value stored in notebook file
    const notebook_relative_path = props.notebook_path;
    const [projectId] = useState(props?.metadata?.get("ploomber")?.project_id || "");

    const [isLoadingRemoteAPI, setIsLoadingRemoteAPI] = useState(true);
    const [isLoadingDeployStatus, setIsLoadingDeployStatus] = useState(false);
    const [isShowSnackbar, setIsShowSnackbar] = useState(false)
    const [isShowFirstTimeDeployPrompt, setIsShowFirstTimeDeployPrompt] = useState(false)

    const [APIKey, setAPIKey] = useState("");
    const [deploymentURL, setDeploymentURL] = useState("");
    const [APIValidStatus, setAPIValidStatus] = useState("init")
    const [deployErrorMessage, setDeployErrorMessage] = useState(null)
    const [snakebarMessage, setSnakebarMessage] = useState("")

    useEffect(() => {
        fetchAPIKey()
    }, [])

    // When API Key is verified, init. the first time deployment flow
    useEffect(() => {
        if (APIValidStatus === "success") {
            if (!projectId) {
                setIsShowFirstTimeDeployPrompt(true)
            } else {
                setIsShowFirstTimeDeployPrompt(false)
                deployNotebook()
            }
        }
    }, [APIValidStatus])


    // The API Key should stored in config file
    const fetchAPIKey = async () => {

        await requestAPI<any>('apikey')
            .then(response => {
                if (response?.data != null) {
                    setAPIKey(response.data)
                    setAPIValidStatus("success")
                }
            }).catch(reason => {
                console.error(
                    `The ploomber_extension server extension appears to be missing.\n${reason}`
                );
            });
        setIsLoadingRemoteAPI(false);
    }

    const onSaveAPIKey = async () => {
        // When the user enters the API Key, store in the config file through /dashboard/apikey API
        setIsLoadingRemoteAPI(true);

        const dataToSend = { 'api_key': APIKey };

        await requestAPI<any>('apikey', {
            body: JSON.stringify(dataToSend),
            method: 'POST'
        }).then(response => {
            if (response?.result == "success") {
                setAPIValidStatus("success")
            }
            else {
                setAPIValidStatus("error")
            }
        }).catch(reason => {
            console.error(
                `Error on POST ${dataToSend}.\n${reason}`
            );
        });
        setIsLoadingRemoteAPI(false)
    }

    const onClickFirstTimeDeploy = async () => {
        setIsShowFirstTimeDeployPrompt(false)
        await deployNotebook()
    }

    const deployNotebook = async () => {
        setIsLoadingDeployStatus(true)
        const dataToSend = { 'notebook_path': notebook_relative_path, 'api_key': APIKey, 'project_id': projectId };
        await requestAPI<any>('job', {
            body: JSON.stringify(dataToSend),
            method: 'POST'
        }).then(reply => {
            var result = reply["deployment_result"]
            var errorMsg: {
                type: string,
                detail: any
            } = {
                type: "generic",
                detail: ""
            }
            if (result?.type === "missing file" && result?.detail) {
                errorMsg.type = result.type
                errorMsg.detail = {
                    fileName: "requirements.txt",
                    filePath: result.detail
                }
                setDeployErrorMessage(errorMsg)
            }
            else if (result?.detail || result?.message) {

                errorMsg.detail = result.detail || result.message
                setDeployErrorMessage(errorMsg)
            } else {
                setDeploymentURL(DEPLOYMENT_ENDPOINTS.NEW_JOB + result?.project_id + "/" + result?.id)
                props.metadata.ploomber = { "project_id": result?.project_id }
                props?.context?.save()
            }
            // Write into notebook projectID
        })

        setIsLoadingDeployStatus(false)

    }

    const APITextFieldProps: { [status: string]: any } = {
        "init": {
            label: "API Key",
            variant: "outlined",
            color: "primary"
        },
        "success": {
            label: "Valid API Key",
            variant: "filled",
            color: "success"
        },
        "error": {
            label: "Please enter valid API Key",
            variant: "filled",
            color: "warning"
        }
    }
    return (
        <Box p={6} style={{ width: 600 }}>
            {isLoadingRemoteAPI || isLoadingDeployStatus ?
                <Box sx={{
                    display: 'flex', justifyContent: "center",
                    alignItems: "center"
                }} >
                    <CircularProgress />
                </Box>
                : <>
                    <Grid container spacing={4} alignItems="center" direction="column">
                        {APIValidStatus !== "success" &&
                            <div>
                                <Grid item container direction='row' alignItems="center" justifyContent="flex-start" width={"100%"} my={2}>
                                    Deploy this notebook as a web application with Ploomber Cloud. <Link href={DOCS.VOILA_EXAMPLES} target="_blank" rel="noopener noreferrer"> Click here to learn more.</Link>
                                </Grid>
                                <Grid item container direction='row' alignItems="center" width={"100%"}>
                                    <Grid container direction="row" alignItems="center" spacing={1}>
                                        <Grid item xs={10}>
                                            <TextField id="api-key-input"
                                                size="small"
                                                onChange={(val) => { setAPIKey(val.target.value) }}
                                                value={APIKey}
                                                label={APITextFieldProps[APIValidStatus]["label"]}
                                                variant={APITextFieldProps[APIValidStatus]["variant"]}
                                                color={APITextFieldProps[APIValidStatus]["color"]}
                                                error={APIValidStatus == "error"}
                                                fullWidth={true}
                                                focused
                                            />
                                        </Grid>
                                        <Grid item xs={2} alignItems="center" justifyContent="center">
                                            <Button onClick={onSaveAPIKey} variant="contained" size="small">CONFIRM</Button>
                                        </Grid>
                                    </Grid>
                                </Grid>
                                <Grid item container direction='row' alignItems="center" width={"100%"} my={2}>
                                    You need an API key to deploy this notebook.&nbsp;<Link href={DOCS.GET_API_KEY} target="_blank" rel="noopener noreferrer">Click here to get an API Key</Link>
                                </Grid>
                            </div>
                        }
                        {APIValidStatus === "success" &&
                            <Grid item container alignItems="center" spacing={4} direction="column">
                                {!isShowFirstTimeDeployPrompt ? <>
                                    {deployErrorMessage ? <ErrorMessageArea message={deployErrorMessage} /> :
                                        <>
                                            <Grid item justifyContent="center" xs={12}>
                                                Check your deployment status here:

                                            </Grid>
                                            <Grid item justifyContent="center" xs={12}>
                                                <Chip label={deploymentURL} variant="outlined" onClick={() => {
                                                    window.open(deploymentURL);
                                                    setIsShowSnackbar(true)
                                                    setSnakebarMessage("Deployment Success")
                                                }} />
                                                <Snackbar
                                                    open={isShowSnackbar}
                                                    onClose={() => setIsShowSnackbar(false)}
                                                    autoHideDuration={2000}
                                                    message={snakebarMessage}
                                                />
                                            </Grid>
                                        </>
                                    }
                                </> : <>
                                    <>
                                        <Typography variant="subtitle1" gutterBottom>
                                            Confirm that you want to deploy this notebook to Ploomber Cloud
                                        </Typography>
                                        <Button onClick={onClickFirstTimeDeploy} variant="contained" size="small" color="primary" disabled={deploymentURL !== ""} endIcon={<CloudQueue />}>CONFIRM </Button>
                                    </>
                                </>}
                            </Grid>
                        }
                    </Grid>

                </>}
        </Box >
    );
};

export const UploadDialogContent = (props: any): JSX.Element => {
    const notebook_relative_path = props.notebook_path;

    const [isLoadingRemoteAPI, setIsLoadingRemoteAPI] = useState(true);
    const [isLoadingDeployStatus, setIsLoadingDeployStatus] = useState(false);
    const [isShowSnackbar, setIsShowSnackbar] = useState(false)
    const [isShowFirstTimeDeployPrompt, setIsShowFirstTimeDeployPrompt] = useState(false)

    const [APIKey, setAPIKey] = useState("");
    const [deploymentURL, setDeploymentURL] = useState("");
    const [APIValidStatus, setAPIValidStatus] = useState("init")
    const [deployErrorMessage, setDeployErrorMessage] = useState(null)
    const [snakebarMessage, setSnakebarMessage] = useState("")

    useEffect(() => {
        fetchAPIKey()
    }, [])

    // When API Key is verified, init. the first time deployment flow
    useEffect(() => {
        if (APIValidStatus === "success") {
            setIsShowFirstTimeDeployPrompt(true)
        }
    }, [APIValidStatus])


    // The API Key should stored in config file
    const fetchAPIKey = async () => {

        await requestAPI<any>('apikey')
            .then(response => {
                if (response?.data != null) {
                    setAPIKey(response.data)
                    setAPIValidStatus("success")
                }
            }).catch(reason => {
                console.error(
                    `The ploomber_extension server extension appears to be missing.\n${reason}`
                );
            });
        setIsLoadingRemoteAPI(false);
    }

    const onSaveAPIKey = async () => {
        // When the user enters the API Key, store in the config file through /dashboard/apikey API
        setIsLoadingRemoteAPI(true);

        const dataToSend = { 'api_key': APIKey };
        await requestAPI<any>('apikey', {
            body: JSON.stringify(dataToSend),
            method: 'POST'
        }).then(response => {
            if (response?.result == "success") {
                setAPIValidStatus("success")
            }
            else {
                setAPIValidStatus("error")
            }
        }).catch(reason => {
            console.error(
                `Error on POST ${dataToSend}.\n${reason}`
            );
        });
        setIsLoadingRemoteAPI(false)
    }

    const onClickFirstTimeDeploy = async () => {
        setIsShowFirstTimeDeployPrompt(false)
        await uploadNotebook()
    }

    const uploadNotebook = async () => {
        setIsLoadingDeployStatus(true)
        const dataToSend = { 'notebook_path': notebook_relative_path, 'api_key': APIKey };
        await requestAPI<any>('nb-upload', {
            body: JSON.stringify(dataToSend),
            method: 'POST'
        }).then(reply => {
            var result = reply["deployment_result"]
            var errorMsg: {
                type: string,
                detail: any
            } = {
                type: "generic",
                detail: ""
            }
            if (result?.type === "missing file" && result?.detail) {
                errorMsg.type = result.type
                errorMsg.detail = {
                    fileName: "requirements.txt",
                    filePath: result.detail
                }
                setDeployErrorMessage(errorMsg)
            }
            else if (result?.detail || result?.message) {

                errorMsg.detail = result.detail || result.message
                setDeployErrorMessage(errorMsg)
            } else {
                setDeploymentURL(DEPLOYMENT_ENDPOINTS.NEW_NOTEBOOK + "/" + result?.id)
                props.metadata.ploomber = { "project_id": result?.project_id }
                props?.context?.save()
            }
            // Write into notebook projectID
        })

        setIsLoadingDeployStatus(false)

    }

    const APITextFieldProps: { [status: string]: any } = {
        "init": {
            label: "API Key",
            variant: "outlined",
            color: "primary"
        },
        "success": {
            label: "Valid API Key",
            variant: "filled",
            color: "success"
        },
        "error": {
            label: "Please enter valid API Key",
            variant: "filled",
            color: "warning"
        }
    }
    return (
        <Box p={6} style={{ width: 600 }}>
            {isLoadingRemoteAPI || isLoadingDeployStatus ?
                <Box sx={{
                    display: 'flex', justifyContent: "center",
                    alignItems: "center"
                }} >
                    <CircularProgress />
                </Box>
                : <>
                    <Grid container spacing={4} alignItems="center" direction="column">
                        {APIValidStatus !== "success" &&
                            <div>
                                <Grid item container direction='row' alignItems="center" justifyContent="flex-start" width={"100%"} my={2}>
                                    Upload this notebook to Ploomber Cloud to share it with anyone. <Link href={DOCS.VOILA_EXAMPLES} target="_blank" rel="noopener noreferrer"> Click here to learn more.</Link>
                                </Grid>
                                <Grid item container direction='row' alignItems="center" width={"100%"}>
                                    <Grid container direction="row" alignItems="center" spacing={1}>
                                        <Grid item xs={10}>
                                            <TextField id="api-key-input"
                                                size="small"
                                                onChange={(val) => { setAPIKey(val.target.value) }}
                                                value={APIKey}
                                                label={APITextFieldProps[APIValidStatus]["label"]}
                                                variant={APITextFieldProps[APIValidStatus]["variant"]}
                                                color={APITextFieldProps[APIValidStatus]["color"]}
                                                error={APIValidStatus == "error"}
                                                fullWidth={true}
                                                focused
                                            />
                                        </Grid>
                                        <Grid item xs={2} alignItems="center" justifyContent="center">
                                            <Button onClick={onSaveAPIKey} variant="contained" size="small">CONFIRM</Button>
                                        </Grid>
                                    </Grid>
                                </Grid>
                                <Grid item container direction='row' alignItems="center" width={"100%"} my={2}>
                                    You need an API key to upload this notebook.&nbsp;<Link href={DOCS.GET_API_KEY} target="_blank" rel="noopener noreferrer">Click here to get an API Key</Link>
                                </Grid>
                            </div>
                        }
                        {APIValidStatus === "success" &&
                            <Grid item container alignItems="center" spacing={4} direction="column">
                                {!isShowFirstTimeDeployPrompt ? <>
                                    {deployErrorMessage ? <ErrorMessageArea message={deployErrorMessage} /> :
                                        <>
                                            <Grid item justifyContent="center" xs={12}>
                                                Your notebook is available here:
                                            </Grid>
                                            <Grid item justifyContent="center" xs={12}>
                                                <Chip label={deploymentURL} variant="outlined" onClick={() => {
                                                    window.open(deploymentURL);
                                                    setIsShowSnackbar(true)
                                                    setSnakebarMessage("Deployment Success")
                                                }} />
                                                <Snackbar
                                                    open={isShowSnackbar}
                                                    onClose={() => setIsShowSnackbar(false)}
                                                    autoHideDuration={2000}
                                                    message={snakebarMessage}
                                                />
                                            </Grid>
                                        </>
                                    }
                                </> : <>
                                    <>
                                        <Typography variant="subtitle1" gutterBottom>
                                            Confirm that you want to upload this notebook to Ploomber Cloud
                                        </Typography>
                                        <Button onClick={onClickFirstTimeDeploy} variant="contained" size="small" color="primary" disabled={deploymentURL !== ""} endIcon={<CloudQueue />}>CONFIRM </Button>
                                    </>
                                </>}
                            </Grid>
                        }
                    </Grid>

                </>}
        </Box >
    );
};

class DeployWidget extends ReactWidget {
    private state: any
    constructor(props: any) {
        super();
        this.state = {
            notebookPath: props.notebookPath,
            metadata: props.metadata,
            context: props.context
        }
    }
    render(): JSX.Element {
        return <DeployDialogContent notebook_path={this.state.notebookPath} metadata={this.state.metadata} context={this.state.context} />;
    }
}


class UploadWidget extends ReactWidget {
    private state: any
    constructor(props: any) {
        super();
        this.state = {
            notebookPath: props.notebookPath,
            metadata: props.metadata,
            context: props.context
        }
    }
    render(): JSX.Element {
        return <UploadDialogContent notebook_path={this.state.notebookPath} metadata={this.state.metadata} context={this.state.context} />;
    }
}
