import React, { useState, useMemo, useCallback } from "react"
import { useTranslation } from "react-i18next"
import styled from "styled-components"
import _groupBy from "lodash/groupBy"
import _keyBy from "lodash/keyBy"
import Typography from "@material-ui/core/Typography"
import MenuItem from "@material-ui/core/MenuItem"

import { bps } from "@/ui/theme"
import SEO from "@/components/templates/SEO"
import Layout from "@components/templates/Layout"
import { useAllCasesData } from "@components/data/useAllCasesData"
import TagStyleFilter from "@/components/molecules/TagStyleFilter"
import { createDedupOptions } from "@/utils/search"
import { mapColorForStatus } from "@/utils/colorHelper"
import { PageContent } from "@/components/atoms/Container"
import { WarsCaseBoxContainer } from "@/components/organisms/CaseBoxContainer"
import { WarsCaseCard } from "@components/organisms/CaseCard"
import InfiniteScroll from "@/components/molecules/InfiniteScroll"
import ConfirmedCasesSummary from "@/components/molecules/ConfirmedCasesSummary"
import { ResponsiveWrapper } from "@components/atoms/ResponsiveWrapper"
import ContextStore from "@/contextStore"
import { CASES_BOX_VIEW, CASES_CARD_VIEW } from "@/reducers/cases"
import { Accordion } from "@components/atoms/Accordion"
import { DefaultSelect } from "@components/atoms/Select"
import { trackCustomEvent } from "gatsby-plugin-google-analytics"
import MaleIcon from "@/components/icons/male.svg"
import FemaleIcon from "@/components/icons/female.svg"
import ImportIcon from "@/components/icons/import.svg"
import UnknownIcon from "@/components/icons/unknown.svg"
import QuestionIcon from "@/components/icons/question.svg"
import BoxViewIcon from "@/components/icons/box_view.svg"
import CardViewIcon from "@/components/icons/card_view.svg"
import SortIcon from "@/components/icons/sort.svg"
import moment from "moment"
import { useLocation } from "@reach/router"
import { withLanguage } from "@/utils/i18n"

const TitleContainer = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;

  svg {
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-self: center;
    cursor: pointer;
  }

  svg:first-child {
    margin-right: 16px;
  }

  svg g {
    fill: #d5d5d5;
  }

  svg.active g {
    fill: ${props => props.theme.palette.primary.main};
  }
`

const SelectedCardContainer = styled.div`
  display: flex;
  justify-content: center;
  align-items: flex-start;
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  bottom: -56px;
  background: rgba(0, 0, 0, 0.15);
  padding: 0 24px;
  z-index: 1000;
  [class*="CaseCard"] {
    max-width: 800px;
  }

  ${bps.up("sm")} {
    padding-left: 260px;
  }
`

const LegendTitle = styled.div`
  display: flex;

  svg {
    width: 14px;
    height: 14px;
    display: inline-flex;
    align-self: center;
    margin-right: 6px;
  }

  span {
    font-weight: 700;
  }
`
const LegendContent = styled.div`
  display: flex;
  flex-wrap: wrap;

  .item {
    width: 64px;
    margin: 0 24px 24px 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;

    span {
      margin-top: 8px;
      font-size: 14px;
      font-weight: 700;
      line-height: 0.75rem;
    }
  }
`

const Circle = styled.div`
  height: ${props => props.height || 25}px;
  width: ${props => props.width || 25}px;
  background-color: ${props => props.bgColor || "#000"};
  border-radius: 50%;
  display: inline-block;
`

const CasesPageMain = ({ dispatch, view }) => {
  const data = useAllCasesData()
  const { i18n, t } = useTranslation()
  const patientTrackKeyedByCaseNo = useMemo(
    () => _keyBy(data.patient_track.group, "fieldValue"),
    [data]
  )
  // Do the sorting here since case_no is string instead of int
  const [cases, groupArrayColumnOptions] = useMemo(() => {
    const groupArray = data.allWarsCaseRelation.edges.flatMap(
      ({ node }, index) =>
        node.case_no.split`,`.map(nodeCase => ({
          ...node,
          id: index + 1,
          related_cases: node.case_no,
          case_no: +nodeCase,
        }))
    )
    const groupArrayByCaseNo = _groupBy(groupArray, "case_no")
    const groupArrayColumnOptions = data.allWarsCaseRelation.edges.map(
      ({ node }, index) => ({
        value: index + 1,
        label: node[`name_${i18n.language}`],
      })
    )
    const cases = data.allWarsCase.edges
      .map(i => ({
        node: {
          ...i.node,
          case_no_num: +i.node.case_no,
          age_num: +i.node.age,
          groups: groupArrayByCaseNo[i.node.case_no] || [],
          group_ids: (groupArrayByCaseNo[i.node.case_no] || []).map(i => i.id),
        },
      }))
      .sort((edge1, edge2) => {
        const res = edge2.node.confirmation_date.localeCompare(
          edge1.node.confirmation_date
        )
        if (res === 0) {
          return parseInt(edge2.node.case_no) - parseInt(edge1.node.case_no)
        }
        return res
      })
    return [cases, groupArrayColumnOptions]
  }, [data, i18n.language])
  const [internalCount, setInternalCounter] = useState(0)
  const [filteredCases, setFilteredCases] = useState([])
  const [selectedCase, setSelectedCase] = useState(null)
  // 1: by date   : from latest to oldest
  // 2: by date   : from oldest to latest
  // 3: by area   : from greatest to least
  // 4: by area   : from least to greatest
  // 5: by group  : from more to less
  // 6: by group  : from less to more
  // 7: by status

  const [selectedGroupButton, setGroupButton] = useState(1)
  const { pathname } = useLocation()
  const caseCodeMatch = pathname.match(/cases\/([^/]+)/)
  const toFilterEntry = ([key, value]) => [`node.${key}`, value]
  const parseToFilter = str => {
    if (/^[-A-Z0-9]+\.\.+[-A-Z0-9]+$/i.test(str))
      return { between: str.split(/\.\.+/) }
    if (/^[><]=[-A-Z0-9]+$/i.test(str))
      return { [str[0] === ">" ? "gte" : "lte"]: str.slice(2, str.length) }
    if (/^[><][-A-Z0-9]+$/i.test(str))
      return { [str[0] === ">" ? "gt" : "lt"]: str.slice(1, str.length) }
    if (/^[-A-Z0-9]+$/i.test(str)) return str
    return
  }
  const dateRangeOptionPresets = [
    {
      label: t("cases.filters_last_n_days", { n: 7 }),
      value: `${moment()
        .subtract(6, "day")
        .format("YYYY-MM-DD")}..${moment().format(`YYYY-MM-DD`)}`,
    },
    {
      label: t("cases.filters_previous_n_days", { n: 7 }),
      value: `${moment()
        .subtract(13, "day")
        .format("YYYY-MM-DD")}..${moment()
        .subtract(7, "day")
        .format(`YYYY-MM-DD`)}`,
    },
    {
      label: t("cases.filters_last_n_days", { n: 14 }),
      value: `${moment()
        .subtract(13, "day")
        .format("YYYY-MM-DD")}..${moment().format(`YYYY-MM-DD`)}`,
    },
    {
      label: t("cases.filters_previous_n_days", { n: 14 }),
      value: `${moment()
        .subtract(27, "day")
        .format("YYYY-MM-DD")}..${moment()
        .subtract(14, "day")
        .format(`YYYY-MM-DD`)}`,
    },
    {
      label: t("cases.filters_this_month"),
      value: `${moment().format(`[>]YYYY-MM`)}`,
    },
    {
      label: t("cases.filters_previous_month"),
      value: `${moment()
        .subtract(1, "month")
        .format("YYYY-MM")}..${moment().format(`YYYY-MM`)}`,
    },
  ]
  const options = useMemo(() => {
    const stringOrFilterEntry = ([key, value]) => {
      const filterPhrases = value
        .split(/,|\s+/g)
        .filter(phase => phase && /\w$/.test(phase))
        .map(parseToFilter)
        .reduce(
          (acc, curr) => {
            if (curr) {
              curr.constructor === String
                ? acc.inq.push(curr)
                : acc.or.push(curr)
            }
            return acc
          },
          { or: [], inq: [] }
        )

      return [
        filterPhrases.inq.length
          ? { [`node.${key}`]: { inq: filterPhrases.inq } }
          : undefined,
        ...filterPhrases.or.map(phrase => ({ [`node.${key}`]: phrase })),
      ].filter(Boolean)
    }
    return [
      {
        label: t("search.group"),
        options: groupArrayColumnOptions,
        orderOptionsByFilterCount: true,
        realFieldName: "group_ids",
        toFilterEntry,
      },
      {
        label: t("search.classification"),
        options: createDedupOptions(i18n, cases, "classification"),
        orderOptionsByFilterCount: true,
        realFieldName: "classification_" + i18n.language,
        toFilterEntry,
      },
      {
        label: t("search.district"),
        options: createDedupOptions(i18n, cases, "citizenship_district"),
        orderOptionsByFilterCount: true,
        realFieldName: "citizenship_district_" + i18n.language,
        toFilterEntry,
      },
      {
        label: t("search.citizenship"),
        options: createDedupOptions(i18n, cases, "citizenship"),
        orderOptionsByFilterCount: true,
        realFieldName: "citizenship_" + i18n.language,
        toFilterEntry,
      },
      {
        label: t("search.case_status"),
        options: createDedupOptions(i18n, cases, "status"),
        orderOptionsByFilterCount: true,
        realFieldName: "status_" + i18n.language,
        toFilterEntry,
      },
      {
        label: t("search.hospital"),
        options: createDedupOptions(i18n, cases, "hospital"),
        orderOptionsByFilterCount: true,
        realFieldName: "hospital_" + i18n.language,
        toFilterEntry,
      },
      {
        label: t("search.case_no"),
        realFieldName: "case_no_num",
        filterType: "string",
        options: [],
        toFilterEntry: stringOrFilterEntry,
        isOrFilter: true,
        filterPlaceholder: "e.g. 1,3,10..20",
      },
      {
        label: t("dashboard.patient_confirm_date"),
        realFieldName: "confirmation_date",
        filterType: "string",
        options: dateRangeOptionPresets,
        toFilterEntry: stringOrFilterEntry,
        isOrFilter: true,
        filterPlaceholder: "e.g. 2020-06..2020-07-21",
      },
      {
        label: t("dashboard.patient_onset_date"),
        realFieldName: "onset_date",
        options: [
          { label: t("cases.status_asymptomatic"), value: "asymptomatic,none" },
          ...dateRangeOptionPresets,
        ],
        filterType: "string",
        toFilterEntry: stringOrFilterEntry,
        isOrFilter: true,
        filterPlaceholder: "e.g. 2020-06..2020-07-21",
      },
      {
        label: t("cases_visual.age"),
        realFieldName: "age_num",
        filterType: "string",
        options: [],
        toFilterEntry: stringOrFilterEntry,
        isOrFilter: true,
        filterPlaceholder: "e.g. 10..20,>60,>=50",
      },
      {
        label: t("cases_visual.gender"),
        realFieldName: "gender",
        options: [
          {
            value: "M",
            label: t("dashboard.gender_M"),
          },
          {
            value: "F",
            label: t("dashboard.gender_F"),
          },
        ],
        toFilterEntry,
      },
    ]
  }, [cases, dateRangeOptionPresets, groupArrayColumnOptions, i18n, t])
  const onListFiltered = useCallback(data => {
    if (data !== filteredCases) {
      setFilteredCases(data)
      setInternalCounter(i => i + 1)
    }
  }, [filteredCases])
  // Calculate how much cards we should preload in order to scorll to that position
  let preloadedCases = cases.length - parseInt(selectedCase) + 1
  if (isNaN(preloadedCases)) {
    preloadedCases = 15
  }
  const renderCaseCard = useMemo(() => {
    const RenderSingleCaseCard = (node, i) => (
      <WarsCaseCard
        node={node}
        i18n={i18n}
        t={t}
        key={`${i}-${node.id}`}
        // isSelected={selectedCase === item.node.case_no}
        // ref={selectedCase === item.node.case_no ? selectedCard : null}
        patientTrack={
          patientTrackKeyedByCaseNo[node.case_no]
            ? [patientTrackKeyedByCaseNo[node.case_no]]
            : null
        }
        handleClose={
          view === CASES_BOX_VIEW ? e => setSelectedCase(null) : undefined
        }
      />
    )
    return RenderSingleCaseCard
  }, [i18n, patientTrackKeyedByCaseNo, t, view])

  const Legend = () => {
    const items = [
      {
        icon: <MaleIcon />,
        text: t("dashboard.gender_M"),
      },
      {
        icon: <FemaleIcon />,
        text: t("dashboard.gender_F"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("discharged").main}
          />
        ),
        text: t("cases.status_discharged"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("pending_admission").main}
          />
        ),
        text: t("cases.status_pending_admission"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("stable").main}
          />
        ),
        text: t("cases.status_stable"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("hospitalised_again").main}
          />
        ),
        text: t("cases.status_hospitalised_again"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("serious").main}
          />
        ),
        text: t("cases.status_serious"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("critical").main}
          />
        ),
        text: t("cases.status_critical"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("deceased").main}
          />
        ),
        text: t("cases.status_deceased"),
      },
      {
        icon: (
          <Circle
            width={48}
            height={48}
            bgColor={mapColorForStatus("no_admission").main}
          />
        ),
        text: t("cases.status_no_admission"),
      },
      {
        icon: <ImportIcon />,
        text: t("cases.imported"),
      },
      {
        icon: <UnknownIcon />,
        text: t("cases.unknown"),
      },
    ]

    return (
      <Accordion
        style={{ marginBottom: 16 }}
        title={
          <LegendTitle>
            <QuestionIcon />
            <span>{t("cases.legend")}</span>
          </LegendTitle>
        }
        content={
          <LegendContent>
            {items.map((item, i) => (
              <div key={i} className="item">
                {item.icon}
                <span>{item.text}</span>
              </div>
            ))}
          </LegendContent>
        }
      />
    )
  }

  const toggleGroupingButtons = [
    "cases.toggle_date",
    "cases.toggle_date_reverse",
    "cases.toggle_area",
    "cases.toggle_area_reverse",
    "cases.toggle_group",
    "cases.toggle_group_reverse",
    "cases.toggle_status",
  ]

  const handleBoxClick = item => {
    setSelectedCase(item)

    trackCustomEvent({
      category: "cases",
      action: "click_avatar",
      label: item.case_no,
    })
  }
  const isCaseNumberMatch =
    caseCodeMatch && filteredCases.length === 1 && filteredCases[0]
  return (
    <Layout
      onClick={e =>
        typeof e.target.className === "string" &&
        !e.target.className.includes("wars_box") &&
        setSelectedCase(null)
      }
    >
      {isCaseNumberMatch ? (
        <SEO
          titleOveride={t("case.title")}
          // TODO: duplicated entries, filter out in SEO later?
          meta={[
            {
              property: `og:title`,
              content: `${t("index.title")} | ${t("case.case_no", {
                case_no: filteredCases[0].node.case_no,
              })}`,
            },
            {
              property: `og:description`,
              content: withLanguage(i18n, filteredCases[0].node, "detail"),
            },
          ]}
        />
      ) : (
        <SEO title="ConfirmedCasePage" />
      )}
      <TitleContainer>
        <Typography variant="h2">{t("cases.title")}</Typography>
        <span>
          <BoxViewIcon
            className={view === CASES_BOX_VIEW && "active"}
            onClick={() => {
              dispatch({
                type: CASES_BOX_VIEW,
              })
              trackCustomEvent({
                category: "cases",
                action: "toggle_view",
                label: "BOX_VIEW",
              })
            }}
          />
          <CardViewIcon
            className={view === CASES_CARD_VIEW && "active"}
            onClick={() => {
              dispatch({
                type: CASES_CARD_VIEW,
              })
              trackCustomEvent({
                category: "cases",
                action: "toggle_view",
                label: "CARD_VIEW",
              })
            }}
          />
        </span>
      </TitleContainer>
      <PageContent>
        <ConfirmedCasesSummary />
        {view === CASES_BOX_VIEW && <Legend />}
        <Typography variant="h5" style={{ marginTop: 16 }}>
          {t("cases.filters")}
        </Typography>
        <TagStyleFilter
          key={pathname}
          list={cases}
          placeholder={t("search.case_placeholder")}
          options={options}
          searchKey="case"
          onListFiltered={onListFiltered}
          filterWithOr={false}
          initialFilters={
            caseCodeMatch
              ? [
                  {
                    label: caseCodeMatch[1],
                    filterName: t("search.case_no"),
                    realFieldName: "case_no_num",
                    field: "case_no_num",
                    value: caseCodeMatch[1],
                  },
                ]
              : []
          }
        />
        {view === CASES_BOX_VIEW && (
          <DefaultSelect
            value={selectedGroupButton}
            onChange={event => {
              setGroupButton(event.target.value)
              trackCustomEvent({
                category: "cases",
                action: "set_grouping",
                label: toggleGroupingButtons[event.target.value - 1],
              })
            }}
            displayEmpty
            IconComponent={SortIcon}
          >
            {toggleGroupingButtons.map((groupBy, index) => (
              <MenuItem key={index} value={index + 1}>
                {t(groupBy)}
              </MenuItem>
            ))}
          </DefaultSelect>
        )}

        <Typography variant="h6" style={{ marginTop: 16 }}>
          {filteredCases.length > 1
            ? t("cases.filter_results_plural", { count: filteredCases.length })
            : t("cases.filter_results", { count: filteredCases.length })}
        </Typography>
      </PageContent>
      {view === CASES_BOX_VIEW ? (
        <>
          <WarsCaseBoxContainer
            filteredCases={filteredCases}
            handleBoxClick={handleBoxClick}
            selectedGroupButton={selectedGroupButton}
          />
          {selectedCase && (
            <SelectedCardContainer>
              {renderCaseCard(selectedCase)}
            </SelectedCardContainer>
          )}
        </>
      ) : (
        <InfiniteScroll
          key={internalCount} // The state of this InfiniteScroll Component need to be discarded when filteredCases changes.
          list={filteredCases.map(c => c.node)}
          step={{ mobile: 5, preload: preloadedCases }}
          onItem={renderCaseCard}
          Wrapper={ResponsiveWrapper}
        />
      )}
    </Layout>
  )
}

const CasesPageMainMemoed = React.memo(CasesPageMain)

const CasesPage = () => {
  const {
    cases: {
      dispatch,
      state: { view },
    },
  } = React.useContext(ContextStore)
  return <CasesPageMainMemoed dispatch={dispatch} view={view} />
}

export default CasesPage