import React, { useEffect, useMemo, useState } from "react";
import { STATUS_VARIANT } from "../../types";
import { FormGroup } from "../FormGroup";
import { Icon, ICON_TYPE } from "../Icon";
import { Label } from "../Label";
import { Stack } from "../Stack";
import { Text } from "../Text";
import { SelectOptionCategory } from "./shared/SelectOptionCategory";
import { SingleSelect } from "./SingleSelect";
import {
  SelectOption,
  SelectOptionsByCategory,
  SingleSelectedOption,
} from "./shared/types";
import { useDisclosure } from "../../hooks";
import { Button } from "../Button";
import { Modal } from "../Modal";

export const DemoSingleSelectSimple = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);

  const OPTIONS = [
    { label: "Butter Pecan", value: "butter-pecan" },
    { label: "Chocolate", value: "chocolate" },
    {
      label: "Chocolate Chip Cookie Dough",
      value: "chocolate-chip-cookie-dough",
    },
    { label: "Cookie Dough", value: "cookie-dough" },
    { label: "Cookies and Cream", value: "cookies-and-cream" },
    { label: "Mint Chocolate Chip", value: "mint-chocolate-chip" },
    { label: "Neapolitan", value: "neapolitan" },
    { label: "Moose Tracks", value: "moose-tracks" },
    { label: "Rocky Road", value: "rocky-road" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-56"
      selected={selected}
      options={OPTIONS}
      onChange={setSelected}
      isClearable
      placeholder="Favorite ice cream?"
    >
      {OPTIONS.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

interface IceCreamExtended extends SelectOption {
  dairyFree: boolean;
}

export const DemoSingleSelectCategories = () => {
  const OPTIONS_WITH_CATEGORIES: SelectOptionsByCategory<IceCreamExtended>[] = useMemo(
    () => [
      {
        title: "Nuts",
        options: [
          { label: "Maple Walnut", value: "maple-walnut", dairyFree: false },
          { label: "Moose Tracks", value: "moose-tracks", dairyFree: false },
          { label: "Pistachio", value: "pistachio", dairyFree: false },
          { label: "Rocky Road", value: "rocky-road", dairyFree: false },
        ],
      },
      {
        title: "No Nuts",
        options: [
          { label: "Butter Pecan", value: "butter-pecan", dairyFree: false },
          { label: "Chocolate", value: "chocolate", dairyFree: true },
          {
            label: "Chocolate Chip Cookie Dough",
            value: "chocolate-chip-cookie-dough",
            dairyFree: false,
          },
          { label: "Cookie Dough", value: "cookie-dough", dairyFree: false },
          {
            label: "Cookies and Cream",
            value: "cookies-and-cream",
            dairyFree: true,
          },
          { label: "Strawberry", value: "strawberry", dairyFree: true },
          { label: "Vanilla", value: "vanilla", dairyFree: false },
        ],
      },
    ],
    [],
  );

  const renderIceCream = (option: IceCreamExtended) =>
    option.dairyFree ? `${option.label} - Dairy Free` : `${option.label} 🐮`;

  const [selected, setSelected] = useState<
    SingleSelectedOption<IceCreamExtended>
  >(null);
  const [search, setSearch] = useState("");

  const filteredOptions: SelectOptionsByCategory<IceCreamExtended>[] = useMemo(() => {
    if (!search) return OPTIONS_WITH_CATEGORIES;
    return OPTIONS_WITH_CATEGORIES.reduce<
      SelectOptionsByCategory<IceCreamExtended>[]
    >((acc, cur) => {
      const filteredCatOptions = cur.options.filter(
        (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0,
      );
      return filteredCatOptions.length > 0
        ? [...acc, { ...cur, options: filteredCatOptions }]
        : acc;
    }, []);
  }, [search, OPTIONS_WITH_CATEGORIES]);

  return (
    <SingleSelect<IceCreamExtended>
      label="Favorite Ice Cream"
      optionsDisplay="categories"
      options={filteredOptions}
      selected={selected}
      onChange={setSelected}
      renderSelected={({ selectedItem }) => {
        if (!selectedItem) return null;
        return renderIceCream(selectedItem);
      }}
      inputValue={search}
      onInputChange={setSearch}
      isClearable
    >
      {filteredOptions.map((cat) => (
        <SelectOptionCategory key={cat.title} titleSlot={cat.title}>
          {cat.options.map((o) => (
            <SingleSelect.Option option={o} key={o.value}>
              {renderIceCream(o)}
            </SingleSelect.Option>
          ))}
        </SelectOptionCategory>
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectForm = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);

  const OPTIONS = [
    { label: "Chocolate", value: "chocolate" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <FormGroup isRequired>
      <Label>Favorite Ice Cream</Label>
      <SingleSelect
        selected={selected}
        options={OPTIONS}
        onChange={setSelected}
      >
        {OPTIONS.map((o) => (
          <SingleSelect.Option key={o.value} option={o}>
            {`👉 ${o.label}`}
          </SingleSelect.Option>
        ))}
      </SingleSelect>
    </FormGroup>
  );
};

export const DemoSingleSelectNoResults = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);
  const [search, setSearch] = useState("");

  const OPTIONS = useMemo(
    () => [
      { label: "Chocolate", value: "chocolate" },
      { label: "Strawberry", value: "strawberry" },
      { label: "Vanilla", value: "vanilla" },
    ],
    [],
  );

  const filteredOptions = useMemo(
    () =>
      OPTIONS.filter(
        (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0,
      ),
    [search, OPTIONS],
  );

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={filteredOptions}
      onChange={setSelected}
      inputValue={search}
      onInputChange={setSearch}
      placeholder="Search for something"
    >
      {filteredOptions.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectNoResultsCustom = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);
  const [search, setSearch] = useState("");

  const OPTIONS = useMemo(
    () => [
      { label: "Chocolate", value: "chocolate" },
      { label: "Strawberry", value: "strawberry" },
      { label: "Vanilla", value: "vanilla" },
    ],
    [],
  );

  const filteredOptions = useMemo(
    () =>
      OPTIONS.filter(
        (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0,
      ),
    [search, OPTIONS],
  );

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={filteredOptions}
      onChange={setSelected}
      inputValue={search}
      onInputChange={setSearch}
      placeholder="Search for something"
    >
      {filteredOptions.length > 0 ? (
        filteredOptions.map((o) => (
          <SingleSelect.Option key={o.value} option={o} />
        ))
      ) : (
        <SingleSelect.Status>No ice cream found 🍦❌</SingleSelect.Status>
      )}
    </SingleSelect>
  );
};

export const DemoSingleSelectLoading = () => {
  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={null}
      options={[]}
      onChange={() => {}}
      isLoading
    >
      {[]}
    </SingleSelect>
  );
};

export const DemoSingleSelectLoadingCustom = () => {
  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={null}
      options={[]}
      onChange={() => {}}
      isLoading
    >
      <SingleSelect.Status>brb fetching ice cream... 🏃‍♀️</SingleSelect.Status>
    </SingleSelect>
  );
};

export const DemoSingleSelectAsyncExample = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);
  const [search, setSearch] = useState("");
  const [options, setOptions] = useState<SelectOption[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  const OPTIONS = useMemo(
    () => [
      { label: "Chocolate", value: "chocolate" },
      { label: "Strawberry", value: "strawberry" },
      { label: "Vanilla", value: "vanilla" },
    ],
    [],
  );

  // Emulate server request
  useEffect(() => {
    if (search) {
      setIsLoading(true);
      setTimeout(() => {
        setIsLoading(false);
        setOptions(OPTIONS);
      }, 1000);
    }
  }, [search, OPTIONS]);

  const filteredOptions = useMemo(
    () =>
      options.filter(
        (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0,
      ),
    [options, search],
  );

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={filteredOptions}
      onChange={setSelected}
      isClearable
      placeholder="Favorite ice cream?"
      inputValue={search}
      onInputChange={setSearch}
      isLoading={isLoading}
    >
      {options.length === 0 && !isLoading ? (
        <SingleSelect.Status>Search for ice cream</SingleSelect.Status>
      ) : (
        filteredOptions.map((o) => (
          <SingleSelect.Option key={o.value} option={o} />
        ))
      )}
    </SingleSelect>
  );
};

export const DemoSingleSelectPlaceholder = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);

  const OPTIONS = [
    { label: "Chocolate", value: "chocolate" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={OPTIONS}
      onChange={setSelected}
      isClearable
      placeholder="Favorite ice cream?"
    >
      {OPTIONS.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectPlaceholderElements = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);

  const OPTIONS = [
    { label: "Chocolate", value: "chocolate" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={OPTIONS}
      onChange={setSelected}
      isClearable
      placeholder={
        <Stack spacing={1}>
          <Icon icon={ICON_TYPE.QUESTION_CIRCLE} />
          <Text as="span">Favorite ice cream?</Text>
          <Icon icon={ICON_TYPE.QUESTION_CIRCLE} />
        </Stack>
      }
    >
      {OPTIONS.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectClearable = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);

  const OPTIONS = [
    { label: "Chocolate", value: "chocolate" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={OPTIONS}
      onChange={setSelected}
      isClearable
      placeholder="I am clearable"
    >
      {OPTIONS.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectCreatable = () => {
  const [selected, setSelected] = useState<SingleSelectedOption>(null);
  const [search, setSearch] = useState("");
  const [isLoading, setIsLoading] = useState(false);

  const OPTIONS = [
    { label: "Chocolate", value: "chocolate" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];
  const [options, setOptions] = useState(OPTIONS);

  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={selected}
      options={options}
      onChange={setSelected}
      isClearable
      placeholder="Create a new flavor"
      isLoading={isLoading}
      onInputChange={setSearch}
      inputValue={search}
      onCreateOption={(newLabel) => {
        // Create the new option and format the value however you want
        const newOption = {
          label: newLabel,
          value: newLabel.toLowerCase().replace(/\W/g, ""),
        };

        // Create the new options array and sort it
        const newOptions = [...options, newOption].sort((a, b) =>
          a.label.toLowerCase().localeCompare(b.label.toLowerCase()),
        );
        // set selected state
        setSelected(newOption);

        // emulate creating option on server
        setIsLoading(true);
        setTimeout(() => {
          setIsLoading(false);
          // set the new options
          setOptions(newOptions);
        }, 1000);
      }}
    >
      {options.map((o) => (
        <SingleSelect.Option key={o.value} option={o} />
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectCategoriesCreatable = () => {
  const OPTIONS_WITH_CATEGORIES: SelectOptionsByCategory<IceCreamExtended>[] = [
    {
      title: "Nuts",
      options: [
        { label: "Maple Walnut", value: "maple-walnut", dairyFree: false },
        { label: "Moose Tracks", value: "moose-tracks", dairyFree: false },
        { label: "Pistachio", value: "pistachio", dairyFree: false },
        { label: "Rocky Road", value: "rocky-road", dairyFree: false },
      ],
    },
    {
      title: "No Nuts",
      options: [
        { label: "Butter Pecan", value: "butter-pecan", dairyFree: false },
        { label: "Chocolate", value: "chocolate", dairyFree: true },
        {
          label: "Chocolate Chip Cookie Dough",
          value: "chocolate-chip-cookie-dough",
          dairyFree: false,
        },
        { label: "Cookie Dough", value: "cookie-dough", dairyFree: false },
        {
          label: "Cookies and Cream",
          value: "cookies-and-cream",
          dairyFree: true,
        },
        { label: "Strawberry", value: "strawberry", dairyFree: true },
        { label: "Vanilla", value: "vanilla", dairyFree: false },
      ],
    },
  ];

  const [options, setOptions] = useState(OPTIONS_WITH_CATEGORIES);
  const [isLoading, setIsLoading] = useState(false);

  const renderIceCream = (option: IceCreamExtended) =>
    option.dairyFree ? `${option.label} - Dairy Free` : `${option.label} 🐮`;

  const [selected, setSelected] = useState<
    SingleSelectedOption<IceCreamExtended>
  >(null);
  const [search, setSearch] = useState("");

  const filteredOptions: SelectOptionsByCategory<IceCreamExtended>[] = useMemo(() => {
    if (!search) return options;
    return options.reduce<SelectOptionsByCategory<IceCreamExtended>[]>(
      (acc, cur) => {
        const filteredCatOptions = cur.options.filter(
          (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0,
        );
        return filteredCatOptions.length > 0
          ? [...acc, { ...cur, options: filteredCatOptions }]
          : acc;
      },
      [],
    );
  }, [search, options]);

  return (
    <SingleSelect<IceCreamExtended>
      label="Favorite Ice Cream"
      optionsDisplay="categories"
      options={filteredOptions}
      selected={selected}
      onChange={setSelected}
      renderSelected={({ selectedItem }) => {
        if (!selectedItem) return null;
        return renderIceCream(selectedItem);
      }}
      inputValue={search}
      onInputChange={setSearch}
      isClearable
      isLoading={isLoading}
      onCreateOption={(newLabel) => {
        const hasNuts = newLabel.includes("nuts");
        const hasDairy = newLabel.includes("dairy");

        let trimmedLabel = newLabel;
        if (hasNuts) {
          trimmedLabel = trimmedLabel.replace("nuts", "");
        }
        if (hasDairy) {
          trimmedLabel = trimmedLabel.replace("dairy", "");
        }

        // Create the new option and format the value however you want
        const newOption: IceCreamExtended = {
          label: trimmedLabel,
          value: trimmedLabel.toLowerCase().replace(/\W/g, ""),
          dairyFree: !hasDairy,
        };

        const newOptions = [...options];
        const categoryTitle = hasNuts ? "Nuts" : "No Nuts";
        const category = newOptions.find((c) => c.title === categoryTitle);
        if (category) {
          category.options = [...category.options, newOption].sort((a, b) =>
            a.label.toLowerCase().localeCompare(b.label.toLowerCase()),
          );
        }

        // set selected state locally
        setSelected(newOption);

        // emulate creating option on server
        setIsLoading(true);
        setTimeout(() => {
          setIsLoading(false);
          // set the new options
          setOptions(newOptions);
        }, 1000);
      }}
    >
      {filteredOptions.map((cat) => (
        <SelectOptionCategory key={cat.title} titleSlot={cat.title}>
          {cat.options.map((o) => (
            <SingleSelect.Option option={o} key={o.value} isDisabled>
              {renderIceCream(o)}
            </SingleSelect.Option>
          ))}
        </SelectOptionCategory>
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectVariants = () => {
  return Object.values(STATUS_VARIANT).map((v) => (
    <SingleSelect
      key={v}
      label={v}
      className="w-64 mb-8"
      selected={null}
      options={[]}
      onChange={() => {}}
      variant={v}
      placeholder={v}
    >
      <SingleSelect.Status>No options here 🤷‍♀️</SingleSelect.Status>
    </SingleSelect>
  ));
};

export const DemoSingleSelectDisabledOption = () => {
  interface IceCreamWithStock extends SelectOption {
    inStock: boolean;
  }
  const [selected, setSelected] = useState<
    SingleSelectedOption<IceCreamWithStock>
  >(null);

  const OPTIONS: IceCreamWithStock[] = [
    { label: "Butter Pecan", value: "butter-pecan", inStock: true },
    { label: "Chocolate", value: "chocolate", inStock: true },
    {
      label: "Chocolate Chip Cookie Dough",
      value: "chocolate-chip-cookie-dough",
      inStock: false,
    },
    { label: "Cookie Dough", value: "cookie-dough", inStock: true },
    { label: "Cookies and Cream", value: "cookies-and-cream", inStock: true },
    {
      label: "Mint Chocolate Chip",
      value: "mint-chocolate-chip",
      inStock: true,
    },
    { label: "Neapolitan", value: "neapolitan", inStock: true },
    { label: "Moose Tracks", value: "moose-tracks", inStock: false },
    { label: "Rocky Road", value: "rocky-road", inStock: true },
    { label: "Strawberry", value: "strawberry", inStock: false },
    { label: "Vanilla", value: "vanilla", inStock: true },
  ];

  return (
    <SingleSelect
      label="Pick a flavor"
      className="w-64"
      selected={selected}
      options={OPTIONS}
      onChange={setSelected}
      isClearable
      placeholder="Pick a flavor"
    >
      {OPTIONS.map((o) => (
        <SingleSelect.Option key={o.value} option={o} isDisabled={!o.inStock}>
          {`${o.label}${o.inStock ? "" : " ❌ Sold out"}`}
        </SingleSelect.Option>
      ))}
    </SingleSelect>
  );
};

export const DemoSingleSelectDisabled = () => {
  return (
    <SingleSelect
      label="Favorite Ice Cream"
      className="w-64"
      selected={null}
      options={[]}
      onChange={() => {}}
      isDisabled
    >
      {[]}
    </SingleSelect>
  );
};

export const DemoSelectInModal = () => {
  const { isOpen, onOpen, onClose } = useDisclosure();
  const [selected, setSelected] = useState<SingleSelectedOption>(null);
  const OPTIONS = [
    { label: "Butter Pecan", value: "butter-pecan" },
    { label: "Chocolate", value: "chocolate" },
    {
      label: "Chocolate Chip Cookie Dough",
      value: "chocolate-chip-cookie-dough",
    },
    { label: "Cookie Dough", value: "cookie-dough" },
    { label: "Cookies and Cream", value: "cookies-and-cream" },
    { label: "Mint Chocolate Chip", value: "mint-chocolate-chip" },
    { label: "Neapolitan", value: "neapolitan" },
    { label: "Moose Tracks", value: "moose-tracks" },
    { label: "Rocky Road", value: "rocky-road" },
    { label: "Strawberry", value: "strawberry" },
    { label: "Vanilla", value: "vanilla" },
  ];

  return (
    <>
      <Button onClick={onOpen}>Show Modal</Button>
      <Modal title="Select in a modal" isOpen={isOpen} onClose={onClose}>
        <Modal.Body>
          <SingleSelect
            label="Favorite Ice Cream"
            className="w-56"
            selected={selected}
            options={OPTIONS}
            onChange={setSelected}
            isClearable
            placeholder="Favorite ice cream?"
          >
            {OPTIONS.map((o) => (
              <SingleSelect.Option key={o.value} option={o} />
            ))}
          </SingleSelect>
        </Modal.Body>
      </Modal>
    </>
  );
};
