import React, { useEffect, useState } from 'react'
import { ReactTerminal, TerminalContextProvider } from 'react-terminal'

import './app.css'
import {
  clusterControllerExecCtl,
  clusterControllerGetTopology
} from 'apiClient/services/devops'

const Terminal = ({
  cluster_id,
  deploy_type
}: {
  cluster_id: string
  deploy_type: string
}) => {
  const [theme, setTheme] = React.useState('light')
  const [controlBar, setControlBar] = React.useState(false)
  const [controlButtons, setControlButtons] = React.useState(false)
  const [prompt, setPrompt] = React.useState('>>>')
  const [history, setHistory] = useState<JSX.Element[]>([])
  const [clusterName, setClusterName] = useState<string>()
  const [welcomeMessage, setWelcomeMessage] = useState<JSX.Element[]>()

  var tikv_or_cse = 'tikv-ctl'
  var tikv_or_cse_desc = `Execute tikv-ctl on cluster. The certificate is already specified.`
  if (deploy_type === 'DEPLOY_TYPE_NEXTGEN') {
    tikv_or_cse = 'cse-ctl'
    tikv_or_cse_desc =
      'Execute cse-ctl on cluster. If you want to set the certificate, please input `--cacert /client-ssl/ca.crt --cert /client-ssl/tls.crt --key /client-ssl/tls.key`.'
  }

  const commands = {
    help: (
      <span>
        <strong>topology</strong> - Get host and port of PD/TiKV <br />
        <strong>pd-ctl</strong> - Execute pd-ctl on cluster. The certificate is
        already specified. <br />
        <strong>{tikv_or_cse}</strong> - {tikv_or_cse_desc} <br />
        <strong>history</strong> - Show command history <br />
        <strong>clear</strong> - clears the console. <br />
        <strong>change_prompt &lt;PROMPT&gt;</strong> - Change the prompt of the
        terminal. <br />
        <strong>change_theme &lt;THEME&gt;</strong> - Changes the theme of the
        terminal. Allowed themes - light, dark, material-light, material-dark,
        material-ocean, matrix and dracula. <br />
        <strong>toggle_control_bar</strong> - Hides / Display the top control
        bar. <br />
        <strong>toggle_control_buttons</strong> - Hides / Display the top
        buttons on control bar. <br />
        <strong>evaluate_math_expression &lt;EXPR&gt;</strong> - Evaluates a
        mathematical expression (eg, <strong>4*4</strong>) by hitting a public
        API, api.mathjs.org.
      </span>
    ),

    change_prompt: (prompt: string) => {
      setPrompt('[' + clusterName + ']' + prompt)
    },

    change_theme: (theme: string) => {
      const validThemes = [
        'light',
        'dark',
        'material-light',
        'material-dark',
        'material-ocean',
        'matrix',
        'dracula'
      ]
      if (!validThemes.includes(theme)) {
        return `Theme ${theme} not valid. Try one of ${validThemes.join(', ')}`
      }
      setTheme(theme)
    },

    toggle_control_bar: () => {
      setControlBar(!controlBar)
    },

    toggle_control_buttons: () => {
      setControlButtons(!controlButtons)
    },

    evaluate_math_expression: async (expr: string) => {
      const response = await fetch(
        `https://api.mathjs.org/v4/?expr=${encodeURIComponent(expr)}`
      )
      return await response.text()
    },

    history: () => {
      return history.map((value) => {
        return value
      })
    },

    topology: () => {
      return runTopology(cluster_id).then()
    }
  }

  const runTopology = async (clusterId: string) => {
    try {
      let resp = await clusterControllerGetTopology(clusterId)

      setClusterName(resp.data.topology?.cluster_name)
      setPrompt('[' + resp.data.topology?.cluster_name + '] ' + '>>>')

      return (
        <span style={{ color: 'green', whiteSpace: 'pre-wrap' }}>
          <div style={{ color: 'brown' }}>
            PD Network:
            <br />
          </div>
          {resp.data.topology?.pd?.map((value) => {
            return value.host + ':' + value.port + '\n'
          })}
          <div style={{ color: 'brown' }}>
            TiKV Network:
            <br />
          </div>
          {resp.data.topology?.tikv?.map((value) => {
            return value.host + ':' + value.port + '\n'
          })}
        </span>
      )
    } catch (error) {
      return (
        <span style={{ color: 'red' }}>
          Error: {error.response.data.errcode}
          <br />
          {error.response.data.errmsg}
          <br />
        </span>
      )
    }
  }

  const runCommandReq = async (command: string, commandArguments: string) => {
    history.push(
      <span>
        {command} {commandArguments} <br />
      </span>
    )

    try {
      const resp = await clusterControllerExecCtl(cluster_id, {
        command: command + ' ' + commandArguments
      })
      return <div style={{ whiteSpace: 'pre-wrap' }}>{resp.data.stdout}</div>
    } catch (error) {
      return (
        <>
          <div style={{ whiteSpace: 'pre-wrap' }}>
            {error.response.data.errcode + ' : ' + error.response.data.errmsg}
          </div>
          <div>
            Please contact with <strong>wangkang@pingcap.com</strong>
          </div>
        </>
      )
    }
  }

  let init = async (clusterId: string) => {
    let welcomeMessageList: JSX.Element[] = []
    welcomeMessageList.push(
      <span>
        Welcome to using the PD/TiKV Tools!
        <br />
        Type "help" for all available commands. <br />
        When executed for the first time, it may take several seconds. <br />
      </span>
    )

    try {
      let resp = await clusterControllerGetTopology(clusterId)

      setClusterName(resp.data.topology?.cluster_name)
      setPrompt('[' + resp.data.topology?.cluster_name + '] ' + '>>>')

      welcomeMessageList.push(
        <span style={{ color: 'green', whiteSpace: 'pre-wrap' }}>
          <div style={{ color: 'brown' }}>
            PD Network:
            <br />
          </div>
          {resp.data.topology?.pd?.map((value) => {
            return value.host + ':' + value.port + '\n'
          })}
          <div style={{ color: 'brown' }}>
            TiKV Network:
            <br />
          </div>
          {resp.data.topology?.tikv?.map((value) => {
            return value.host + ':' + value.port + '\n'
          })}
        </span>
      )
    } catch (error) {
      welcomeMessageList.push(
        <span style={{ color: 'red' }}>
          Error: {error.response.data.errcode}
          <br />
          {error.response.data.errmsg}
          <br />
        </span>
      )
    }

    setWelcomeMessage(welcomeMessageList)
  }

  useEffect(() => {
    init(cluster_id).then()
  }, [])

  return (
    <div className="Terminal">
      <TerminalContextProvider>
        <ReactTerminal
          prompt={prompt}
          theme={theme}
          showControlBar={controlBar}
          showControlButtons={controlButtons}
          welcomeMessage={welcomeMessage}
          commands={commands}
          defaultHandler={runCommandReq}
        />
      </TerminalContextProvider>
    </div>
  )
}

export default Terminal
