import React, { useContext, useState, useMemo } from 'react';
import classNames from 'classnames';
import { AccordionSkeleton, Button, DataTableSkeleton, Layer } from '@carbon/react';
import { useTranslation } from 'react-i18next';
import { useLayoutType, TreeViewAltIcon, useConfig } from '@openmrs/esm-framework';
import { EmptyState, ErrorState } from '@openmrs/esm-patient-common-lib';
import { type ConfigObject } from '../../config-schema';
import { type GroupedObservation, type viewOpts } from '../../types';
import FilterSet, { FilterContext } from '../filter';
import GroupedTimeline, { useGetManyObstreeData } from '../grouped-timeline';
import IndividualResultsTable from '../individual-results-table/individual-results-table.component';
import TabletOverlay from '../tablet-overlay';
import styles from '../results-viewer/results-viewer.scss';

interface TreeViewProps {
  patientUuid: string;
  expanded: boolean;
  view?: viewOpts;
  error?: string;
}

const GroupedPanelsTables: React.FC<{ patientUuid: string; className: string; loadingPanelData: boolean }> = ({
  className,
  loadingPanelData,
  patientUuid,
}) => {
  const { t } = useTranslation();
  const { checkboxes, someChecked, tableData } = useContext(FilterContext);
  const selectedCheckboxes = Object.keys(checkboxes).filter((key) => checkboxes[key]);

  // An entry can represent several collapsed branches; match a checkbox against
  // every flatName that contributed to it, not just the one it carries.
  const entryMatchesCheckbox = (entry: GroupedObservation['entries'][number], selectedKey: string) =>
    (entry.flatNames ?? [entry.flatName]).includes(selectedKey) || entry.key === selectedKey;

  const tableFilteredSubRows = useMemo(
    () =>
      tableData
        ?.filter(
          (row) =>
            !someChecked ||
            (row.entries &&
              Array.isArray(row.entries) &&
              row.entries.some((entry) =>
                selectedCheckboxes.some((selectedKey) => entryMatchesCheckbox(entry, selectedKey)),
              )),
        )
        .map((subRows: GroupedObservation, index) => {
          return {
            ...subRows,
            entries:
              subRows.entries && Array.isArray(subRows.entries)
                ? subRows.entries.filter(
                    (entry) =>
                      !someChecked ||
                      selectedCheckboxes.some((selectedKey) => entryMatchesCheckbox(entry, selectedKey)),
                  )
                : [],
          };
        }),
    [tableData, someChecked, selectedCheckboxes],
  );

  if (!tableData?.length) {
    return <EmptyState displayText={t('data', 'data')} headerTitle={t('dataTimelineText', 'Data timeline')} />;
  }

  return (
    <Layer className={className}>
      {tableFilteredSubRows.map((filteredSubRows, index) => {
        return filteredSubRows.entries?.length > 0 ? (
          <div
            key={index}
            className={classNames({
              [styles.border]: filteredSubRows?.entries.length,
            })}
          >
            <IndividualResultsTable
              patientUuid={patientUuid}
              isLoading={loadingPanelData}
              subRows={filteredSubRows}
              index={index}
              title={filteredSubRows.key}
            />
          </div>
        ) : null;
      })}
    </Layer>
  );
};

const TreeView: React.FC<TreeViewProps> = ({ patientUuid, expanded, view }) => {
  const { t } = useTranslation();
  const tablet = useLayoutType() === 'tablet';
  const [showTreeOverlay, setShowTreeOverlay] = useState(false);
  const config = useConfig<ConfigObject>();
  const conceptUuids = config?.resultsViewerConcepts?.map((c) => c.conceptUuid) ?? [];
  const { roots, error } = useGetManyObstreeData(patientUuid, conceptUuids);

  const { timelineData, tableData, totalResultsCount, filteredResultsCount, resetTree, isLoading } =
    useContext(FilterContext);

  if (error) {
    return <ErrorState error={error} headerTitle={t('dataLoadError', 'Data Load Error')} />;
  }

  // Don't show empty state while loading - wait for data to finish loading
  if (isLoading) {
    return <DataTableSkeleton role="progressbar" />;
  }

  if (!roots || roots.length === 0) {
    return (
      <EmptyState
        headerTitle={t('testResults_title', 'Test Results')}
        displayText={t('testResultsData', 'test results data')}
      />
    );
  }

  if (tablet) {
    return (
      <>
        <div>
          {!isLoading && view === 'over-time' ? (
            <GroupedTimeline patientUuid={patientUuid} />
          ) : view === 'individual-test' ? (
            <GroupedPanelsTables
              className={styles.groupPanelsTables}
              loadingPanelData={isLoading}
              patientUuid={patientUuid}
            />
          ) : (
            <DataTableSkeleton role="progressbar" />
          )}
        </div>
        <div className={styles.floatingTreeButton}>
          <Button
            renderIcon={TreeViewAltIcon}
            hasIconOnly
            onClick={() => setShowTreeOverlay(true)}
            iconDescription={t('showTree', 'Show tree')}
          />
        </div>
        {showTreeOverlay && (
          <TabletOverlay
            headerText={t('tree', 'Tree')}
            close={() => setShowTreeOverlay(false)}
            buttonsGroup={
              <div className={styles.overlay}>
                <Button kind="secondary" size="xl" onClick={resetTree} disabled={isLoading}>
                  {t('resetTreeText', 'Reset tree')}
                </Button>
                <Button kind="primary" size="xl" onClick={() => setShowTreeOverlay(false)} disabled={isLoading}>
                  {`${t('view', 'View')} ${!isLoading ? filteredResultsCount : ''} ${t('resultsText', 'results')}`}
                </Button>
              </div>
            }
          >
            {!isLoading ? <FilterSet hideFilterSetHeader /> : <AccordionSkeleton open count={4} align="start" />}
          </TabletOverlay>
        )}
      </>
    );
  }

  return (
    <>
      {!expanded && (
        <div className={styles.leftSection}>
          {!isLoading ? <FilterSet /> : <AccordionSkeleton open count={4} align="start" />}
        </div>
      )}
      <div className={classNames(styles.rightSection, expanded ? styles.fullView : styles.splitView)}>
        {isLoading ? (
          <DataTableSkeleton />
        ) : view === 'individual-test' ? (
          tableData && tableData.length > 0 ? (
            <div className={styles.panelViewTimeline}>
              <GroupedPanelsTables
                patientUuid={patientUuid}
                className={styles.groupPanelsTables}
                loadingPanelData={isLoading}
              />
            </div>
          ) : (
            <GroupedPanelsTables
              patientUuid={patientUuid}
              className={styles.groupPanelsTables}
              loadingPanelData={isLoading}
            />
          )
        ) : view === 'over-time' ? (
          <GroupedTimeline patientUuid={patientUuid} />
        ) : null}
      </div>
    </>
  );
};

export default TreeView;
