import React, { useEffect, useRef, useState } from 'react';

import Button from '@leafygreen-ui/button';
// leafygreen-ui
import { cx } from '@leafygreen-ui/emotion';
import { usePrevious, useViewportSize } from '@leafygreen-ui/hooks';
// @ts-expect-error
import SettingsIcon from '@leafygreen-ui/icon/dist/Settings';
import { useDarkMode } from '@leafygreen-ui/leafygreen-provider';
import {
  FocusableMenuItem,
  Menu,
  MenuItem,
  MenuSeparator,
} from '@leafygreen-ui/menu';

import { breakpoints } from '../../breakpoints';
import { useMongoNavContext } from '../../MongoNavContext';
// mongo-nav
import { useOnElementClick } from '../../on-element-click-provider';
import {
  CurrentOrganizationInterface,
  Mode,
  MongoNavInterface,
  NavElement,
  OrganizationInterface,
  PlanType,
  URLS,
} from '../../types';
// mongo-select
import Input from '../mongo-select-helpers/Input';
import { onKeyDown } from '../mongo-select-helpers/onKeyDown';
import {
  menuContainerStyle,
  menuItemContainerStyle,
  searchInputStyle,
  settingsButtonIconActiveThemeStyle,
  settingsButtonIconDisabledStyle,
  settingsButtonIconThemeStyle,
} from '../styles';
import { BaseMongoSelectProps } from '../types';

import {
  emptyStateStyle,
  linkThemeStyle,
  menuSeparatorStyles,
  orgMenuContainerStyle,
  orgSelectWrapperStyle,
  orgSettingsButtonBaseStyle,
  orgSettingsButtonThemeStyle,
  viewAllThemeStyle,
} from './OrgSelect.styles';
import { orgSelectSegmentStyle, OrgSelectTrigger } from './OrgSelectTrigger';

// types
interface OrganizationMongoSelectProps extends BaseMongoSelectProps {
  data?: Array<OrganizationInterface>;
  current?: CurrentOrganizationInterface | null;
  constructOrganizationURL: NonNullable<
    MongoNavInterface['constructOrganizationURL']
  >;
  isOnPrem?: boolean;
  isActive?: boolean;
  disabled?: boolean;
  urls: Partial<URLS['mongoSelect']>;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const formattedPlanTypes: Record<PlanType, string> = {
  [PlanType.Atlas]: 'Atlas',
  [PlanType.Cloud]: 'Cloud Manager',
  [PlanType.OnPrem]: 'Ops Manager',
} as const;

interface OrgFilterResponse extends OrganizationInterface {
  groups: null;
}

function OrgSelect({
  current,
  mode,
  hosts = {},
  data = [],
  urls,
  onChange: onChangeProp,
  constructOrganizationURL,
  admin = false,
  isActive = false,
  disabled: disabledProp = false,
  className,
}: OrganizationMongoSelectProps) {
  const [value, setValue] = useState('');
  const [filteredData, setFilteredData] = useState(data);
  const [isFetching, setIsFetching] = useState(false);
  const [open, setOpen] = useState(false);
  const wasOpen = usePrevious(open);
  const onElementClick = useOnElementClick();
  const viewportSize = useViewportSize();
  const { theme } = useDarkMode();
  const { isLoading } = useMongoNavContext();
  const buttonRef = useRef<HTMLButtonElement>(null);

  const isFiltered = value !== '';
  const isAdminSearch = isFiltered && admin && mode === Mode.Production;

  const isDisabled = isLoading || disabledProp;

  const renderedData = isFiltered ? filteredData : data;

  const toggleOpen = () => {
    setOpen(curr => !curr);
    if (!open) {
      setValue('');
    }
  };

  const hasOnChangeProp = !!onChangeProp;

  useEffect(() => {
    // Skip if we're not filtering
    if (!isFiltered) {
      return;
    }

    // defer all behavior to user-provided filtering
    if (hasOnChangeProp) {
      return;
    }

    // serverside filtering (admin users only)
    if (isAdminSearch) {
      setIsFetching(true);

      const controller = new AbortController();

      const fetchOrgsAsAdmin = (searchTerm = '') => {
        const queryString = searchTerm ? `?term=${searchTerm}` : '';
        const endpointURI = `${hosts.cloud}/user/shared/organizations/search${queryString}`;
        return fetch(endpointURI, {
          credentials: 'include',
          mode: 'cors',
          method: 'GET',
          signal: controller.signal,
        });
      };

      fetchOrgsAsAdmin(value)
        .then(response => response.json())
        .then((response: Array<OrgFilterResponse>) => {
          const data = response.map(({ groups, ...org }) => org);

          setFilteredData(data);
          setIsFetching(false);
        })
        .catch(console.error);

      return () => {
        controller.abort();
      };
    }

    // clientside filtering (default)
    const normalizedValue = value.toLowerCase();
    const filtered = data?.filter(
      datum => datum.orgName.toLowerCase().indexOf(normalizedValue) !== -1,
    );

    setFilteredData(filtered);
  }, [isFiltered, hasOnChangeProp, isAdminSearch, hosts.cloud, data, value]);

  // on change, make sure value actually changes
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const val = e.target.value.replace(/\\/g, '\\');
    setValue(val);

    if (open === true && wasOpen !== open) {
      // Opt out of type-checking as we just want to trigger the onElementClick handler
      onElementClick(NavElement.OrgNavOrgSelectSearch)(e as any);
    }

    return onChangeProp?.({
      value,
      setData: setFilteredData,
      event: e,
    });
  };

  const isTablet = viewportSize
    ? viewportSize.width < breakpoints.medium
    : false;

  const renderOrganizationOption = (datum: OrganizationInterface) => {
    const { orgId, orgName, planType } = datum;
    const isCurrentOrg = orgId ? orgId === current?.orgId : false;
    const active = isCurrentOrg && !isDisabled;

    return (
      <MenuItem
        data-testid="org-option"
        key={orgId}
        active={active}
        className={menuItemContainerStyle}
        onClick={toggleOpen}
        onKeyDown={onKeyDown}
        href={constructOrganizationURL(datum)}
        description={formattedPlanTypes[planType]}
      >
        {orgName}
      </MenuItem>
    );
  };

  return (
    <div className={cx(orgSelectWrapperStyle, className)}>
      {/*
        TODO: Replace this with a SplitButton component when that is available
      */}
      <OrgSelectTrigger
        ref={buttonRef}
        open={open}
        onClick={toggleOpen}
        isTablet={isTablet}
        disabled={isDisabled}
        current={current}
      />
      <Menu
        open={open}
        usePortal={false}
        setOpen={toggleOpen}
        refEl={buttonRef}
        spacing={8}
        maxHeight={384}
        justify="start"
        className={cx(menuContainerStyle, orgMenuContainerStyle)}
      >
        {data && (
          <FocusableMenuItem>
            <Input
              data-testid="org-filter-input"
              variant="organization"
              onChange={onChange}
              onKeyDown={onKeyDown}
              value={value}
              className={searchInputStyle}
            />
          </FocusableMenuItem>
        )}

        {isAdminSearch && isFetching && (
          <MenuItem className={emptyStateStyle}>Searching...</MenuItem>
        )}

        {!isFetching && renderedData.length === 0 && (
          <MenuItem className={emptyStateStyle}>No matches found.</MenuItem>
        )}

        {renderedData?.map(renderOrganizationOption) ?? (
          <MenuItem className={emptyStateStyle} onKeyDown={onKeyDown}>
            You do not belong to any organizations. Create an organization on
            the{' '}
            <a
              href={urls.viewAllOrganizations}
              className={linkThemeStyle[theme]}
            >
              Organizations
            </a>{' '}
            page.
          </MenuItem>
        )}

        {renderedData && (
          <>
            <MenuSeparator className={menuSeparatorStyles} />
            <MenuItem
              onKeyDown={onKeyDown}
              href={urls.viewAllOrganizations}
              data-testid="org-select-view-all-orgs"
              onClick={onElementClick(NavElement.OrgNavViewAllOrganizations)}
            >
              <strong className={viewAllThemeStyle[theme]}>
                View All Organizations
              </strong>
            </MenuItem>
          </>
        )}
      </Menu>

      {!isDisabled && (
        <Button
          href={urls.orgSettings}
          aria-label="settings"
          data-testid="org-trigger-settings"
          disabled={isDisabled}
          aria-disabled={isDisabled}
          tabIndex={isDisabled ? -1 : 0}
          onClick={onElementClick(NavElement.OrgNavOrgSettings)}
          onKeyDown={onKeyDown}
          className={cx(
            orgSelectSegmentStyle,
            orgSettingsButtonBaseStyle,
            orgSettingsButtonThemeStyle[theme],
            settingsButtonIconThemeStyle[theme],
            {
              [settingsButtonIconActiveThemeStyle[theme]]: isActive,
              [settingsButtonIconDisabledStyle]: isDisabled, // TODO: these styles would not show up since the button only shows up when its not disabled?
            },
          )}
          rightGlyph={
            <SettingsIcon data-testid="settings-icon" role="presentation" />
          }
        />
      )}
    </div>
  );
}

OrgSelect.displayName = 'OrgSelect';

export default OrgSelect;
