"use client";

import type { Route } from "next";
import type { MouseEvent, ReactNode } from "react";

import clsx from "clsx";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { createContext, useContext, useState } from "react";

import ChevronDownSVG from "@/assets/icons/chevron-down.svg";
import ChevronUpSVG from "@/assets/icons/chevron-up.svg";
import CloseSVG from "@/assets/icons/close.svg";

import Image from "next/image";
import styles from "./Select.module.scss";

interface BaseProps {
  label?: string;
  placeholder?: string;
  noClear?: boolean;
  error?: string | undefined;
  className?: string | undefined;
  children: ReactNode;
}

interface ControlledProps {
  value: string | undefined;
  onChange: (value: string | undefined) => void;
  name?: never;
}

interface UncontrolledProps {
  name: string;
  value?: never;
  onChange?: never;
}

type SelectProps = BaseProps & (ControlledProps | UncontrolledProps);

const SelectContext = createContext<{
  register: (value: string, image?: string, label?: string) => void;
  onSelect: (value: string) => void;
} | null>(null);

export const useSelectContext = () => {
  const contextValue = useContext(SelectContext);

  if (!contextValue)
    throw new Error(
      "SelectContext has been used outside of its corresponsing provider",
    );

  return contextValue;
};

interface Option {
  value: string;
  image?: string | undefined;
  label?: string | undefined;
}

export function Select({
  label,
  name,
  value,
  placeholder,
  noClear,
  onChange,
  error,
  className,
  children,
}: SelectProps) {
  const router = useRouter();
  const pathname = usePathname();
  const searchParamsReadonly = useSearchParams();

  const searchParams = new URLSearchParams(searchParamsReadonly);

  const [isOpen, setIsOpen] = useState(false);
  const [options, setOptions] = useState<Option[]>([]);

  const isControlled = onChange !== undefined;

  const selectedOption = options.find(
    (option) =>
      option.value === (isControlled ? value : searchParams.get(name)),
  );
  const ButtonIcon = isOpen
    ? ChevronUpSVG
    : selectedOption
      ? CloseSVG
      : ChevronDownSVG;

  function handleToggleIsOpen() {
    setIsOpen((isOpen) => !isOpen);
  }

  function handleSelect(newValue: string) {
    setIsOpen(false);

    if (isControlled) {
      onChange(newValue);
    } else {
      searchParams.set(name, newValue);
    }

    router.replace(`${pathname}?${searchParams}` as Route);
  }

  function handleReset(event: MouseEvent<SVGElement>) {
    event.stopPropagation();

    if (isControlled) {
      onChange(undefined);
    } else {
      searchParams.delete(name);
    }

    router.replace(`${pathname}?${searchParams}` as Route);
  }

  function handleRegisterOption(value: string, image?: string, label?: string) {
    setOptions((options) => [...options, { value, image, label }]);
  }

  return (
    <SelectContext.Provider
      value={{ register: handleRegisterOption, onSelect: handleSelect }}
    >
      <div
        className={clsx(
          styles["select"],
          isOpen && styles["select--open"],
          className,
        )}
      >
        {label && <span className={styles["select__label"]}>{label}</span>}

        <div className={styles["select__body"]}>
          <button
            onClick={handleToggleIsOpen}
            className={styles["select__button"]}
          >
            <div className={styles["select__button-body"]}>
              {selectedOption?.image && (
                <div className={styles["select__button-image-wrapper"]}>
                  <Image
                    src={selectedOption.image}
                    alt=""
                    fill
                    className={styles["select__button-image"]}
                  />
                </div>
              )}
              <span>
                {selectedOption
                  ? selectedOption.label
                  : (placeholder ?? "Выберите опцию")}
              </span>
            </div>

            {!noClear && (
              <ButtonIcon
                onClick={selectedOption && handleReset}
                className={styles["select__button-icon"]}
              />
            )}
          </button>

          <ul className={styles["select__option-list"]}>{children}</ul>
        </div>

        {error && <p className={styles["select__error-message"]}>{error}</p>}
      </div>
    </SelectContext.Provider>
  );
}
