import React, { useEffect, useState } from "react"
import styled from "@emotion/styled"
import { nanoid } from "nanoid"
import { reduce } from "lodash"
import {
  TextField,
  Card,
  CardHeader,
  CardContent,
  Collapse,
  Grid,
  Box,
  Tabs,
  Tab,
  Button,
  Divider,
} from "@material-ui/core"

import { Close, Add } from "@material-ui/icons"
import {
  fileToBase64,
  formatDocumentRequest,
  parseDocumentRequest,
} from "../../helpers/utils"

const TextInput = styled(TextField)`
  width: 100%;
`

const FileInput = styled.input`
  display: none;
`

type TabPanelProps = {
  children: any
  value: number
  index: number
}

function TabPanel({ children, value, index }: TabPanelProps) {
  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`vertical-tabPanel-${index}`}
      aria-labelledby={`vertical-tab-${index}`}
    >
      {value === index && <Box p={2}>{children}</Box>}
    </div>
  )
}

type Props = {
  requestBody: string
  defaultRequestBody: string
  setRequestBody: (body: string) => void
}

export const DocumentRequest = ({
  requestBody,
  defaultRequestBody,
  setRequestBody,
}: Props) => {
  const [textDocuments, setTextDocuments] = useState("")
  const [uris, setURIs] = useState<string[]>([])
  const [showCustom, setShowCustom] = useState(false)
  const [rows, setRows] = useState<string[]>([])
  const [placeholders, setPlaceholders] = useState<{ [key: string]: string }>(
    {}
  )
  const [keys, setKeys] = useState<{ [key: string]: string }>({})
  const [values, setValues] = useState<{ [key: string]: string }>({})

  const toggleShowCustom = () => setShowCustom((prev) => !prev)

  useEffect(() => {
    const {
      rows: initialRows,
      keys: initialKeys,
      values: initialValues,
      text: initialText,
      uris: initialURIs,
      placeholders: initialPlaceholders,
    } = parseDocumentRequest(requestBody, defaultRequestBody)

    setPlaceholders(initialPlaceholders)
    setTextDocuments(initialText)
    setRows(initialRows.length ? initialRows : [nanoid()])
    setValues(initialValues)
    setKeys(initialKeys)
    setURIs(initialURIs)
  }, []) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    const handleUpdate = async () => {
      const formattedBody = await formatDocumentRequest(
        textDocuments,
        uris,
        rows,
        keys,
        values
      )
      setRequestBody(formattedBody)
    }
    handleUpdate()
  }, [textDocuments, uris, rows, keys, values, setRequestBody])

  const addRow = () => {
    const rowId = nanoid()
    setRows((prev) => {
      return [...prev, rowId]
    })
  }

  const handleFileSelect = async (files: FileList | null) => {
    const uris: string[] = []
    const filesArray = Array.from(files || [])

    for (let file of filesArray) {
      const uri = await fileToBase64(file)
      uris.push(uri)
    }

    setURIs(uris)
  }

  const removeRow = (rowId: string) => {
    if (placeholders[rowId]) return setValue(rowId, "")

    const index = rows.indexOf(rowId)
    setRows((prev) => {
      prev.splice(index, 1)
      return prev.length === 0 ? [nanoid()] : [...prev]
    })
  }

  const setKey = (rowId: string, key: string) => {
    setKeys((prev) => {
      prev[rowId] = key
      return { ...prev }
    })
  }

  const setValue = (rowId: string, value: string) => {
    setValues((prev) => {
      prev[rowId] = value
      return { ...prev }
    })
  }

  const removeFiles = () => {
    setURIs([])
  }

  const numCustomFields = reduce(
    rows,
    (acc, rowId) => {
      if (values[rowId] && keys[rowId]) return acc + 1
      return acc
    },
    0
  )

  return (
    <>
      <Box mb={2}>
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <TextInput
              label="Text Documents"
              placeholder="Text Documents"
              variant="outlined"
              multiline
              minRows={3}
              maxRows={25}
              type="custom-text"
              value={textDocuments}
              onChange={(e) => setTextDocuments(e.target.value)}
            />
          </Grid>
        </Grid>
      </Box>
      <Grid container>
        <Grid item xs={6}>
          <FileInput
            type="file"
            multiple
            id="attach-files-button"
            onChange={(e) => handleFileSelect(e.target.files)}
          />
          <label htmlFor="attach-files-button">
            <Button size="large" component="span">
              Select Files
            </Button>
          </label>
          {uris?.length ? (
            <Box display="inline" marginLeft={3}>
              {uris.length} files selected{" "}
              <Button onClick={removeFiles}>Remove</Button>
            </Box>
          ) : (
            ""
          )}
        </Grid>
        <Grid item xs={6}>
          <Box textAlign="right" onClick={toggleShowCustom}>
            <Button size="large">
              {showCustom ? "Hide " : "Show "}Additional Fields
              {numCustomFields ? ` (${numCustomFields})` : ""}
            </Button>
          </Box>
        </Grid>
      </Grid>
      <Collapse in={showCustom}>
        <Box width="100%">
          <Divider />
          {rows.map((id) => (
            <Grid key={id} container spacing={2} paddingTop={3}>
              <Grid item xs={4}>
                <TextInput
                  label="Key"
                  variant="outlined"
                  type="custom-input"
                  disabled={typeof placeholders[id] === "string"}
                  value={keys[id] || ""}
                  onChange={(e) => setKey(id, e.target.value)}
                />
              </Grid>
              <Grid item xs={7}>
                <TextInput
                  label="Value"
                  variant="outlined"
                  type="custom-input"
                  value={values[id] || ""}
                  placeholder={placeholders[id] || ""}
                  onChange={(e) => setValue(id, e.target.value)}
                  InputLabelProps={{
                    shrink: true,
                  }}
                />
              </Grid>
              <Grid item xs={1}>
                {!placeholders[id] || values[id] ? (
                  <Button size="large" onClick={() => removeRow(id)}>
                    <Close />
                  </Button>
                ) : (
                  <></>
                )}
              </Grid>
            </Grid>
          ))}

          <Box paddingTop={3}>
            <Button size="large" onClick={addRow}>
              <Add /> Add Field
            </Button>
          </Box>
        </Box>
      </Collapse>
    </>
  )
}

const DocumentCard = styled(Card)`
  textarea {
    min-height: auto !important;
    background: none;
    border: none;
    padding: none !important;
    font-size: 1rem;
    font-family: inherit !important;
    font-weight: 400;
  }

  textarea:focus {
    border: none;
  }

  input[disabled] {
    background-color: transparent;
  }
`

export const DocumentRequestCard = ({
  requestBody,
  defaultRequestBody,
  setRequestBody,
}: Props) => {
  let numDocuments = 0

  try {
    const req = JSON.parse(requestBody)
    numDocuments = req.data.length
  } catch (e) {}

  return (
    <DocumentCard>
      <CardHeader
        title={`Documents (${numDocuments})`}
        titleTypographyProps={{ variant: "subtitle1" }}
      />
      <CardContent>
        <DocumentRequest
          defaultRequestBody={defaultRequestBody}
          requestBody={requestBody}
          setRequestBody={setRequestBody}
        />
      </CardContent>
    </DocumentCard>
  )
}

const Request = ({
  requestBody,
  defaultRequestBody,
  setRequestBody,
}: Props) => {
  const [tab, setTab] = useState(0)

  return (
    <Card>
      <CardHeader
        title="Request Body"
        titleTypographyProps={{ variant: "subtitle1" }}
      />
      <CardContent>
        <Grid container direction={"row"}>
          <Grid item xs={2}>
            <Box p={2}>
              <Tabs
                orientation="vertical"
                aria-label="request type"
                value={tab}
                onChange={(e, v) => setTab(v)}
              >
                <Tab label="Formatted" />
                <Tab label="Raw" />
              </Tabs>
            </Box>
          </Grid>
          <Grid item xs={10}>
            <TabPanel value={tab} index={0}>
              <DocumentRequest
                defaultRequestBody={defaultRequestBody}
                requestBody={requestBody}
                setRequestBody={setRequestBody}
              />
            </TabPanel>
            <TabPanel value={tab} index={1}>
              <TextInput
                id="filled-textarea"
                label="Request body"
                placeholder="Request body"
                multiline
                minRows={10}
                maxRows={20}
                variant="outlined"
                value={requestBody}
                onChange={(e) => setRequestBody(e.target.value)}
              />
            </TabPanel>
          </Grid>
        </Grid>
      </CardContent>
    </Card>
  )
}

export default Request