'use client';

import { getClassNames } from '@websolutespa/bom-core';
import { ComponentPropsWithRef, forwardRef, ReactNode, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { UIComponentWithRef, UIStyledComponentProps } from '../../components/types';
import { getCssResponsive } from '../../components/utils';
import { FormControlCss, FormFocusCss } from '../styles';
import { highligthText } from '../utils/utils';
import { AutocompleteContext, IAutocomplete, IAutocompleteItem } from './autocomplete-context';
import { AutocompleteDropdown } from './autocomplete-dropdown';

type Props = ComponentPropsWithRef<'input'> & {
  initialValue?: IAutocompleteItem;
  before?: ReactNode;
  after?: ReactNode;
  source: (query: string) => Promise<IAutocompleteItem[]>;
  onAutocomplete?: (value: IAutocompleteItem) => void;
  onDropdownVisibleChange?: (visible: boolean) => void;
};

export type AutocompleteProps = UIStyledComponentProps;

export type AutocompleteInputProps = UIStyledComponentProps;

export type AutocompleteComponent<C extends React.ElementType = 'input'> = UIComponentWithRef<C, Props>;

const StyledAutocomplete = styled.div<AutocompleteProps>`
  ${FormControlCss}
  display: flex;
  align-items: center;
  width: 100%;
  cursor: pointer;

  &>input {
    flex-grow: 1;
    appearance: none;
    min-width: 0;

    &::placeholder {
      color: var(--form-color-placeholder);
      opacity: 0.3;
    }
  }

  &>:first-child:not(input) {
    margin: 0 0 0 0.5em;
  }

  &>:last-child:not(input):not(.button) {
    margin: 0 0.5em 0 0;
  }

  &>.button {
    line-height: inherit;
    font-size: inherit;
    padding-top: var(--form-control-padding);
    padding-bottom: var(--form-control-padding);
    margin: -2px;
  }

  &>:last-child.button {
    border-radius: 0 var(--border-radius) var(--border-radius) 0;
  }

  &>:first-child.button {
    border-radius: var(--border-radius) 0 0 var(--border-radius);
  }

  &>:last-child.button {
    border-radius: 0 var(--border-radius) var(--border-radius) 0;
  }

  ${props => getCssResponsive(props)}
`;

const StyledAutocompleteInput = styled.div<AutocompleteInputProps>`
  display: block;
  appearance: none;
  padding: var(--form-control-padding);
  font-size: var(--form-control-font-size);
  font-weight: var(--form-control-font-weight);
  line-height: var(--form-control-line-height);
  color: inherit;
  background-color: transparent;
  border: none;
  outline: none;

  &:not(:first-child) {
    padding-left: calc(var(--form-control-padding) * 0.5);
  }

  ${props => getCssResponsive(props)}
`;

const StyledAutocompleteItem = styled.div`
  display: block;
  width: 100%;
  padding: var(--form-control-padding);
  font-size: var(--form-control-font-size);
  font-weight: var(--form-control-font-weight);
  line-height: var(--form-control-line-height);
  color: inherit;
  background-color: transparent;
  border-color: var(--color-neutral-200);
  transition: border 150ms ease-in 0s, outline-color 150ms ease-in 0s, color 200ms ease-out 0s;
  cursor: pointer;

  &:not(:last-child) {
    border-bottom: 1px solid var(--color-neutral-200);
  }

  ${FormFocusCss}
`;

export const Autocomplete: AutocompleteComponent = forwardRef(({
  className,
  initialValue,
  before,
  after,
  source,
  onFocus,
  onBlur,
  onDropdownVisibleChange = () => { },
  onAutocomplete,
  id,
  name,
  ...props
}, ref) => {

  const innerRef = useRef<HTMLInputElement>(null);

  const dropdownRef = useRef<HTMLDivElement>(null);

  useImperativeHandle(ref, () => innerRef.current as HTMLInputElement);

  const [items, setItems] = useState<IAutocompleteItem[]>([]);

  const [visible, setVisible] = useState<boolean>(false);

  const [value, setValue] = useState<IAutocompleteItem | null>(initialValue ? initialValue.id : null);

  const [focus, setFocus] = useState<boolean>(false);

  const updateVisible = (next: boolean) => {
    onDropdownVisibleChange(next);
    setVisible(next);
  };

  const setInnerValue = (value = '') => {
    if (innerRef.current) {
      innerRef.current.value = value;
    }
  };

  const onFocus_ = (event: React.FocusEvent<HTMLInputElement>) => {
    onFocus && onFocus(event);
    setFocus(true);
    setInnerValue('');
    console.log('Autocomplete.onFocus_');
  };

  const onBlur_ = (event: React.FocusEvent<HTMLInputElement>) => {
    onBlur && onBlur(event);
    setFocus(false);
    updateVisible(false);
    setItems([]);
    setInnerValue(value ? value.name : '');
    console.log('Autocomplete.onBlur_');
  };

  const onChange_ = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const query = event.target.value.toLowerCase();
    setInnerValue(query);
    const items = await source(query);
    setItems(items);
    console.log('Autocomplete.onChange_');
  };

  const onAutocomplete_ = (item: IAutocompleteItem) => {
    setValue(item);
    if (item) {
      setInnerValue(item.name);
    }
    onAutocomplete && onAutocomplete(item);
    console.log('Autocomplete.onAutocomplete_');
  };

  const context: IAutocomplete = useMemo(() => {
    const updateValue = (item: IAutocompleteItem) => {
      setInnerValue(item.name);
      onAutocomplete && onAutocomplete(item);
      updateVisible(false);
    };
    return {
      visible,
      value,
      ref: innerRef,
      updateValue,
      updateVisible,
    };
  }, [visible, value, updateVisible, onAutocomplete]);

  useEffect(() => {
    if (initialValue) {
      setInnerValue(initialValue.name);
    }
  }, [initialValue]);

  const classNames = getClassNames('autocomplete', {
    disabled: props.disabled,
    readonly: props.readOnly,
    hidden: props.hidden,
  }, className);

  return (
    <>
      <AutocompleteContext.Provider value={context}>
        <StyledAutocomplete
          className={classNames}
          data-focus-visible={focus || undefined}
          {...props}
        >
          {before}
          <StyledAutocompleteInput
            ref={innerRef}
            as="input"
            type="text"
            id={id}
            name={name}
            onFocus={onFocus_}
            onBlur={onBlur_}
            onChange={onChange_}
            {...props}
          />
          {after}
        </StyledAutocomplete>
        {items && (
          <AutocompleteDropdown ref={dropdownRef} visible={items.length > 0}>
            {items.map((item, i) => (
              <StyledAutocompleteItem key={i} onClick={() => onAutocomplete_(item)} dangerouslySetInnerHTML={{ __html: highligthText(item.name, innerRef.current?.value) }}></StyledAutocompleteItem>
            ))}
          </AutocompleteDropdown>
        )}
      </AutocompleteContext.Provider>
    </>
  );
});

Autocomplete.displayName = 'Autocomplete';
