// Copyright 2022 The Parca Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {useRef, useState} from 'react';

import {Switch} from '@headlessui/react';
import {RpcError} from '@protobuf-ts/runtime-rpc';
import {type SelectInstance} from 'react-select';

import {ProfileTypesResponse, QueryServiceClient} from '@parca/client';
import {Button, DateTimeRange, DateTimeRangePicker, useParcaContext} from '@parca/components';
import {ProfileType, Query} from '@parca/parser';
import {TEST_IDS, testId} from '@parca/test-utils';

import MatchersInput from '../MatchersInput';
import PreSelectedMatchers from '../PreSelectedMatchers';
import {QuerySelection} from '../ProfileSelector';
import ProfileTypeSelector from '../ProfileTypeSelector';
import {SelectWithRefresh} from '../SelectWithRefresh';
import SimpleMatchers from '../SimpleMatchers/';
import {useLabelNames} from '../hooks/useLabels';

export interface ExternalProfilerComponentProps {
  disableProfileTypesDropdown?: boolean;
  defaultProfileType?: string;
  configuredLabelNames?: string[];
  disableExplorativeQuerying?: boolean;
  profileFilterDefaults?: unknown[];
}

interface SelectOption {
  label: string;
  value: string;
}

interface QueryControlsProps {
  queryClient: QueryServiceClient;
  query: Query;
  profileType: string | ProfileType;
  timeRangeSelection: DateTimeRange;
  setTimeRangeSelection: (range: DateTimeRange) => void;
  setMatchersString: (matchers: string) => void;
  setQueryExpression: (updateTs?: boolean) => void;
  searchDisabled: boolean;
  showProfileTypeSelector?: boolean;
  showSumBySelector?: boolean;
  showAdvancedMode?: boolean;
  disableExplorativeQuerying?: boolean;
  profileTypesData?: ProfileTypesResponse;
  profileTypesLoading?: boolean;
  selectedProfileName?: string;
  setProfileName?: (name: string | undefined) => void;
  profileTypesError?: RpcError;
  externalProfilerComponent?: ExternalProfilerComponentProps;
  setQueryBrowserMode?: (mode: string) => void;
  advancedModeForQueryBrowser?: boolean;
  setAdvancedModeForQueryBrowser?: (mode: boolean) => void;
  queryBrowserRef?: React.RefObject<HTMLDivElement>;
  labels?: string[];
  sumBySelection?: string[];
  sumBySelectionLoading?: boolean;
  setUserSumBySelection?: (sumBy: string[]) => void;
  sumByRef?: React.RefObject<SelectInstance>;
  draftSelection: QuerySelection;
  setDraftMatchers: (selection: string) => void;
  draftParsedQuery: Query | null;
  commitDraft: () => void;
}

export function QueryControls({
  profileType,
  timeRangeSelection,
  setTimeRangeSelection,
  setQueryExpression,
  searchDisabled,
  showProfileTypeSelector = false,
  showSumBySelector = false,
  showAdvancedMode = true,
  profileTypesData,
  profileTypesLoading = false,
  selectedProfileName,
  setProfileName,
  profileTypesError,
  externalProfilerComponent,
  setQueryBrowserMode,
  advancedModeForQueryBrowser = false,
  setAdvancedModeForQueryBrowser,
  queryBrowserRef,
  labels = [],
  sumBySelection = [],
  sumBySelectionLoading = false,
  setUserSumBySelection,
  sumByRef,
  queryClient,
  draftSelection,
  setDraftMatchers,
  draftParsedQuery,
  commitDraft,
}: QueryControlsProps): JSX.Element {
  const {timezone} = useParcaContext();
  const defaultQueryBrowserRef = useRef<HTMLDivElement>(null);
  const actualQueryBrowserRef = queryBrowserRef ?? defaultQueryBrowserRef;
  const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState<number>(0);

  const {refetch: refetchLabelNames} = useLabelNames(
    queryClient,
    profileType as string,
    timeRangeSelection.getFromMs(),
    timeRangeSelection.getToMs()
  );

  return (
    <div
      className="flex w-full flex-wrap items-start gap-2"
      {...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER)}
    >
      {showProfileTypeSelector && (
        <div>
          <label className="text-xs" {...testId(TEST_IDS.PROFILE_TYPE_LABEL)}>
            Profile type
          </label>
          <ProfileTypeSelector
            profileTypesData={profileTypesData}
            loading={profileTypesLoading}
            selectedKey={selectedProfileName}
            onSelection={setProfileName ?? (() => {})}
            error={profileTypesError}
            disabled={externalProfilerComponent?.disableProfileTypesDropdown}
          />
        </div>
      )}

      <div
        className="w-full flex-1 flex flex-col gap-1 mt-auto"
        ref={actualQueryBrowserRef}
        {...testId(TEST_IDS.QUERY_BROWSER_CONTAINER)}
      >
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-3">
            <label className="text-xs" {...testId(TEST_IDS.QUERY_LABEL)}>
              Query
            </label>
            {showAdvancedMode && externalProfilerComponent?.disableExplorativeQuerying !== true && (
              <>
                <Switch
                  checked={advancedModeForQueryBrowser}
                  onChange={() => {
                    setAdvancedModeForQueryBrowser?.(!advancedModeForQueryBrowser);
                    setQueryBrowserMode?.(advancedModeForQueryBrowser ? 'simple' : 'advanced');
                  }}
                  className={`${
                    advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800'
                  } relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
                  {...testId(TEST_IDS.ADVANCED_MODE_SWITCH)}
                >
                  <span className="sr-only">Use setting</span>
                  <span
                    aria-hidden="true"
                    className={`${
                      advancedModeForQueryBrowser ? 'translate-x-6' : 'translate-x-0'
                    } pointer-events-none inline-block h-[16px] w-[16px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
                  />
                </Switch>
                <label className="text-xs" {...testId(TEST_IDS.QUERY_MODE_LABEL)}>
                  Advanced Mode
                </label>
              </>
            )}
          </div>
        </div>

        {externalProfilerComponent?.configuredLabelNames !== undefined &&
        externalProfilerComponent?.configuredLabelNames.length >= 1 ? (
          <PreSelectedMatchers labelNames={externalProfilerComponent.configuredLabelNames} />
        ) : showAdvancedMode && advancedModeForQueryBrowser ? (
          <MatchersInput
            setDraftMatchers={setDraftMatchers}
            draftParsedQuery={draftParsedQuery}
            commitDraft={commitDraft}
          />
        ) : (
          <SimpleMatchers
            queryBrowserRef={actualQueryBrowserRef}
            searchExecutedTimestamp={searchExecutedTimestamp}
            draftSelection={draftSelection}
            setDraftMatchers={setDraftMatchers}
            draftParsedQuery={draftParsedQuery}
          />
        )}
      </div>

      {showSumBySelector && (
        <div {...testId(TEST_IDS.SUM_BY_CONTAINER)}>
          <div className="mb-0.5 mt-1.5 flex items-center justify-between">
            <label className="text-xs" {...testId(TEST_IDS.SUM_BY_LABEL)}>
              Sum by
            </label>
          </div>
          <SelectWithRefresh<SelectOption, true>
            id="h-sum-by-selector"
            data-testid={testId(TEST_IDS.SUM_BY_SELECT)['data-testid']}
            defaultValue={[]}
            isMulti
            isClearable={false}
            name="colors"
            options={labels.map(label => ({label, value: label}))}
            className="parca-select-container text-sm w-full max-w-80"
            classNamePrefix="parca-select"
            value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))}
            onChange={newValue => {
              setUserSumBySelection?.(newValue.map(option => option.value));
            }}
            placeholder="Labels..."
            styles={{
              indicatorSeparator: () => ({display: 'none'}),
              menu: provided => ({
                ...provided,
                marginBottom: 0,
                boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
                marginTop: 10,
                zIndex: 50,
                minWidth: '320px',
                position: 'absolute',
              }),
            }}
            isLoading={sumBySelectionLoading}
            isDisabled={!(profileType as ProfileType)?.delta}
            // @ts-expect-error
            ref={sumByRef}
            onKeyDown={e => {
              const currentRef = sumByRef?.current as unknown as SelectInstance | null;
              if (currentRef == null) {
                return;
              }
              const inputRef = currentRef.inputRef;
              if (inputRef == null) {
                return;
              }

              if (
                e.key === 'Enter' &&
                inputRef.value === '' &&
                currentRef.state.focusedOptionId === null // menu is not open
              ) {
                setQueryExpression(true);
                currentRef.blur();
              }
            }}
            onRefresh={refetchLabelNames}
            refreshTitle="Refresh label names"
            refreshTestId="sum-by-refresh-button"
            menuTestId={TEST_IDS.SUM_BY_SELECT_FLYOUT}
          />
        </div>
      )}

      <DateTimeRangePicker
        onRangeSelection={setTimeRangeSelection}
        range={timeRangeSelection}
        timezone={timezone}
        {...testId(TEST_IDS.DATE_TIME_RANGE_PICKER)}
      />

      <div>
        <label className="text-xs" {...testId(TEST_IDS.SEARCH_BUTTON_LABEL)}>
          &nbsp;
        </label>
        <Button
          disabled={searchDisabled}
          onClick={(e: React.MouseEvent<HTMLElement>) => {
            e.preventDefault();
            setSearchExecutedTimestamp(Date.now());
            setQueryExpression(true);
          }}
          id="h-matcher-search-button"
          {...testId(TEST_IDS.SEARCH_BUTTON)}
        >
          Search
        </Button>
      </div>
    </div>
  );
}
