import React, { useEffect, useState } from "react";
import { SelectableList } from "./SelectableList";
import { Box, Text, UseInput } from "./Ink";
import { TextInput } from "./TextInput";
import { NumberInput } from "./NumberInput";
import { getApiKey } from "../../zygonApi";
import { DateFilter } from "./DateFilter";

const API_ENDPOINT = process.env.API_ENDPOINT ?? "https://api.zygon.tech";

export type Filter = {
  id: string;
  type:
    | "string"
    | "date"
    | "number"
    | "boolean"
    | "singleOption"
    | "multiOption"
    | "multiTextOption"
    | "json";
};

type OptionOperator =
  | "isAny"
  | "isNone"
  | "isSet"
  | "isNotSet"
  | "haveAny"
  | "haveNone"
  | "haveAll"
  | "areSet"
  | "areNotSet"
  | "anyContain"
  | "noneContain";

type TextOperator = "startsWith" | "endsWith" | "contains";

type DateOperator =
  | "lteDate"
  | "gteDate"
  | "betweenDate"
  | "today"
  | "inLessThan"
  | "inMoreThan"
  | "lessThanAgo"
  | "moreThanAgo"
  | "inTheNext"
  | "inTheLast";

type NumberOperator = "lteInt" | "gteInt" | "eqInt" | "betweenInt";

type HierarchyOperator = "isAbove" | "isBelow" | "managed" | "manages";

type BooleanOperator = "is";

type Operator =
  | OptionOperator
  | TextOperator
  | DateOperator
  | NumberOperator
  | HierarchyOperator
  | BooleanOperator;

const operators = {
  string: ["startsWith", "endsWith", "contains"],
  number: ["lteInt", "gteInt", "eqInt", "betweenInt"],
  boolean: ["is"],
  singleOption: [
    "isAny",
    "isNone",
    "isSet",
    "isNotSet",
    "haveAny",
    "haveNone",
    "haveAll",
    "areSet",
    "areNotSet",
    "anyContain",
    "noneContain",
  ],
  multiOption: [
    "isAny",
    "isNone",
    "isSet",
    "isNotSet",
    "haveAny",
    "haveNone",
    "haveAll",
    "areSet",
    "areNotSet",
    "anyContain",
    "noneContain",
  ],
  multiTextOption: [
    "isAny",
    "isNone",
    "isSet",
    "isNotSet",
    "haveAny",
    "haveNone",
    "haveAll",
    "areSet",
    "areNotSet",
    "anyContain",
    "noneContain",
  ],
  date: [
    "lteDate",
    "gteDate",
    "betweenDate",
    "today",
    "inLessThan",
    "inMoreThan",
    "lessThanAgo",
    "moreThanAgo",
    "inTheNext",
    "inTheLast",
  ],
  json: [],
} satisfies Record<Filter["type"], Array<Operator>>;

export type ConfiguredFilter = {
  id: string;
  filters?: Filter[];
  operator?: Operator;
  negation?: boolean;
  value?: string;
  values?: string[];
  dateMin?: Date;
  dateMax?: Date;
  valueMin?: number;
  valueMax?: number;
  relativeDays?: number;
};

type FilterConfigurationProps = {
  width: number;
  height: number;
  filter: Filter;
  onFilterConfigured: (filter: ConfiguredFilter) => void;
  useInput: UseInput;
};

const FilterConfiguration = ({
  width,
  height,
  filter,
  onFilterConfigured,
  useInput,
}: FilterConfigurationProps): React.JSX.Element => {
  const [configuredFilter, setConfiguredFilter] =
    useState<null | ConfiguredFilter>(null);

  const [options, setOptions] = useState<Array<{ id: string; label: string }>>(
    [],
  );

  useEffect(() => {
    if (
      ["singleOption", "multiOption", "multiTextOption"].includes(filter.type)
    ) {
      fetch(`${API_ENDPOINT}/zquery/filters/${filter.id}/options`, {
        method: "GET",
        headers: {
          Authorization: `Bearer ${getApiKey()}`,
          "Content-Type": "application/json",
        },
      })
        .then((response) => response.json())
        .then((options) =>
          setOptions(options as Array<{ id: string; label: string }>),
        );
    }
  }, [filter.id]);

  if (!configuredFilter) {
    return (
      <Box flexDirection="column">
        <Text>Pick an operator:</Text>
        <SelectableList
          width={width}
          height={height - 1}
          items={operators[filter.type]}
          item={{
            key: (operator) => operator,
            label: (operator) => operator,
          }}
          onItemSelected={(operator) =>
            setConfiguredFilter({
              id: filter.id,
              operator,
            })
          }
          useInput={useInput}
        />
      </Box>
    );
  }

  switch (filter.type) {
    case "string":
      return (
        <Box>
          <Text>Enter your filter text: </Text>
          <TextInput
            onValidation={(text) =>
              onFilterConfigured({
                ...configuredFilter,
                value: text,
              })
            }
            useInput={useInput}
          />
        </Box>
      );
    case "number":
      return (
        <Box>
          <Text>
            Enter your filter{" "}
            {configuredFilter.operator === "betweenInt"
              ? configuredFilter.valueMin === undefined
                ? "minimum "
                : "maximum "
              : ""}
            value:{" "}
          </Text>
          <NumberInput
            key={
              configuredFilter.operator === "lteInt" ||
              (configuredFilter.operator === "betweenInt" &&
                configuredFilter.valueMin === undefined)
                ? "max"
                : "min"
            }
            onValidation={(value) =>
              configuredFilter.operator === "betweenInt" &&
              configuredFilter.valueMin === undefined
                ? setConfiguredFilter({
                    ...configuredFilter,
                    valueMin: value,
                  })
                : onFilterConfigured({
                    ...configuredFilter,
                    [configuredFilter.operator === "lteInt" ||
                    configuredFilter.operator === "betweenInt"
                      ? "valueMax"
                      : "valueMin"]: value,
                  })
            }
            useInput={useInput}
          />
        </Box>
      );
    case "boolean":
      onFilterConfigured(configuredFilter);
      return <></>;
    case "date":
      return (
        <DateFilter
          filter={configuredFilter}
          onFilterChange={setConfiguredFilter}
          onFilterComplete={() => onFilterConfigured(configuredFilter)}
          useInput={useInput}
        />
      );
    case "singleOption":
    case "multiOption":
    case "multiTextOption":
      return (
        <Box flexDirection="column">
          <Text>Pick your filter: </Text>
          <SelectableList
            width={width}
            height={height - 1}
            items={options}
            item={{
              key: (option) => option.id,
              label: (option) => option.label,
            }}
            onItemSelected={(option) =>
              onFilterConfigured({
                ...configuredFilter,
                values: [option.id],
              })
            }
            useInput={useInput}
          />
        </Box>
      );
    case "json":
      onFilterConfigured(configuredFilter);
      return <></>;
  }
};

type FiltersPickerProps = {
  width: number;
  height: number;
  filters: Array<Filter>;
  onFiltersSelection: (filters: Array<ConfiguredFilter>) => void;

  useInput: UseInput;
};

export const FiltersPicker = ({
  width,
  height,
  filters,
  onFiltersSelection,
  useInput,
}: FiltersPickerProps) => {
  const [currentlyEditingFilter, setCurrentlyEditingFilter] =
    useState<null | Filter>(null);
  const [configuredFilters, setConfiguredFilters] = useState<
    Array<ConfiguredFilter>
  >([]);

  useInput((input, key) => {
    if (key.return) {
      if (currentlyEditingFilter === null) {
        onFiltersSelection(configuredFilters);
      }
    }
  });

  return (
    <>
      {currentlyEditingFilter && (
        <FilterConfiguration
          width={width}
          height={height}
          filter={currentlyEditingFilter}
          onFilterConfigured={(filter) => {
            setConfiguredFilters([...configuredFilters, filter]);
            setCurrentlyEditingFilter(null);
          }}
          useInput={useInput}
        />
      )}
      {!currentlyEditingFilter && (
        <SelectableList
          width={width}
          height={height}
          items={filters}
          item={{ key: (filter) => filter.id, label: (filter) => filter.id }}
          onItemSelected={setCurrentlyEditingFilter}
          useInput={useInput}
        />
      )}
    </>
  );
};
