import {
  LitElement,
  html,
  CSSResultArray,
  TemplateResult,
  nothing,
} from 'lit';
import { customElement, query, state, property } from 'lit/decorators.js';
import { styles } from './nile-auto-complete.css';
import NileElement from '../internal/nile-element';
import type { CSSResultGroup, PropertyValues } from 'lit';
import { NileDropdown } from '../nile-dropdown';
import { watch } from '../internal/watch';
import { AutoCompletePortalManager } from './portal-manager';
import { NileInput } from '../nile-input';

import { virtualize } from '@lit-labs/virtualizer/virtualize.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { VisibilityManager } from '../utilities/visibility-manager.js';


// Define the custom element 'nile-auto-complete'
@customElement('nile-auto-complete')
export class NileAutoComplete extends NileElement {

  static styles: CSSResultGroup = styles;
  private visibilityManager?: VisibilityManager;


  @query('nile-dropdown') dropdownElement: NileDropdown;

  @query('nile-input') inputElement: NileInput;

  // Define component properties

  @property({ type: Boolean, reflect: true }) disabled: boolean = false;

  @property({ type: Boolean }) isDropdownOpen: boolean = false;

  /** 
   * When true, the dropdown menu will be appended to the document body instead of the parent container.
   * This is useful when the parent has overflow: hidden, clip-path, or transform applied.
   */
  @property({ type: Boolean, reflect: true }) portal = false;

  private readonly portalManager = new AutoCompletePortalManager(this);

  @property({ type: Boolean }) enableVirtualScroll: boolean = false;

  @property({ type: Boolean }) openOnFocus: boolean = false;

  @property({ type: String }) value: string = '';

  @property({ type: String }) placeholder: string = 'Type here ..';

  @property({ type: Boolean }) noBorder: boolean = false;
  
  @property({ type: Boolean }) noOutline: boolean = false;

  @property({ type: Boolean }) noPadding: boolean = false;

  @property({ type: Boolean }) loading: boolean = false;

  @property({ attribute:false}) filterFunction: (item:string,searchedValue:string)=>boolean=(item:string,searchedValue:string)=>item.toLowerCase().includes(searchedValue.toLowerCase());

  @property({ attribute:false}) renderItemFunction: (item:any)=>string = (item:any)=>item;

  @property({ type: Array }) allMenuItems: any = [];

  @property({ type: Boolean, reflect: true }) enableVisibilityEffect = false;

  @property({ type: Boolean, reflect: true }) enableTabClose = false;

  @property({ type: Boolean, reflect: true, attribute: true }) noDropdownClose = false;

  @property({ type: String }) label = '';

  @state() menuItems: any = [];

  protected async firstUpdated(_changed: PropertyValues) {
    await this.updateComplete;

    this.visibilityManager = new VisibilityManager({
      host: this,
      target: this.inputElement.input,                
      enableVisibilityEffect: this.enableVisibilityEffect,
      enableTabClose: this.enableTabClose,
      isOpen: () => this.isDropdownOpen,
      onAnchorOutOfView: () => {
        this.isDropdownOpen = false;
        this.dropdownElement?.hide();
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'anchor-out-of-view',
        });
      },
      onDocumentHidden: () => {
        this.isDropdownOpen = false;
        this.dropdownElement?.hide();
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'document-hidden',
        });
      },
      emit: (event, detail) => this.emit(`nile-${event}`, detail),
    });
  }
  connectedCallback() {
    super.connectedCallback();
    this.renderItemFunction=(item:any)=>item;
    this.handleDocumentFocusIn = this.handleDocumentFocusIn.bind(this);
    this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
    this.handleWindowResize = this.handleWindowResize.bind(this);
    this.handleWindowScroll = this.handleWindowScroll.bind(this);
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.removeOpenListeners();
    this.visibilityManager?.cleanup();
    this.portalManager.cleanupPortalAppend();
  }

  protected updated(changedProperties: PropertyValues): void {
    super.updated(changedProperties);
    if (changedProperties.has('allMenuItems')){
      this.menuItems = this.applyFilter(this.allMenuItems,this.filterFunction);
      this.setVirtualMenuWidth();
      if (this.portal && this.isDropdownOpen) {
        this.portalManager.updatePortalOptions();
      }
    }
    if (changedProperties.has('isDropdownOpen')) {
      this.menuItems = this.applyFilter(this.allMenuItems,this.filterFunction);
      this.handleDropdownOpenChange();
    }
    if (changedProperties.has('value')){
      this.menuItems = this.applyFilter(this.allMenuItems,this.filterFunction);
      if (this.portal && this.isDropdownOpen) {
        this.portalManager.updatePortalOptions();
    }
  }
    if (changedProperties.has('portal')) {
      this.handlePortalChange();
    }
  }

  @watch('portal', { waitUntilFirstUpdate: true })
  handlePortalChange(): void {
    if (this.isDropdownOpen) {
      if (this.portal) {
        this.portalManager.setupPortalAppend();
      } else {
        this.portalManager.cleanupPortalAppend();
      }
    }
  }

  private handleDropdownOpenChange(): void {
    if (this.isDropdownOpen) {
      this.addOpenListeners();
      this.visibilityManager?.setup();
      if (this.portal) {
        this.portalManager.setupPortalAppend();
      }
    } else {
      this.removeOpenListeners();
      this.visibilityManager?.cleanup();
      if (this.portal) {
        this.portalManager.cleanupPortalAppend();
      }
    }
  }

  private addOpenListeners(): void {
    document.addEventListener('focusin', this.handleDocumentFocusIn);
    document.addEventListener('mousedown', this.handleDocumentMouseDown);
    
    if (this.portal) {
      window.addEventListener('resize', this.handleWindowResize);
      window.addEventListener('scroll', this.handleWindowScroll, true);
    }
  }

  private removeOpenListeners(): void {
    document.removeEventListener('focusin', this.handleDocumentFocusIn);
    document.removeEventListener('mousedown', this.handleDocumentMouseDown);
    window.removeEventListener('resize', this.handleWindowResize);
    window.removeEventListener('scroll', this.handleWindowScroll, true);
  }

  private handleDocumentFocusIn(event: FocusEvent) {
    if (!this.isDropdownOpen) return;
    const path = event.composedPath();
    const hitSelf = path.includes(this);
    const hitDropdown = this.dropdownElement && path.includes(this.dropdownElement);
    const hitPortalAppend = this.portal && this.portalManager.portalContainerElement && path.includes(this.portalManager.portalContainerElement);

    if (!hitSelf && !hitDropdown && !hitPortalAppend) {
      this.isDropdownOpen = false;
      this.dropdownElement?.hide();
    }
  }

  private handleDocumentMouseDown(event: MouseEvent) {
    if (!this.isDropdownOpen) return;
  
    const path = event.composedPath();
    const hitSelf = path.includes(this);
    const hitDropdown = this.dropdownElement && path.includes(this.dropdownElement);
    const hitPortalAppend = this.portal && this.portalManager.portalContainerElement && path.includes(this.portalManager.portalContainerElement);
  
    if (!hitSelf && !hitDropdown && !hitPortalAppend) {
      this.isDropdownOpen = false;
      this.dropdownElement?.hide();
    }
  }

  private handleWindowResize = (): void => {
    this.portalManager.updatePortalAppendPosition();
  };

  private handleWindowScroll = (): void => {
    this.portalManager.updatePortalAppendPosition();
  };

  public render(): TemplateResult {
    const content=this.enableVirtualScroll?this.getVirtualizedContent():this.getContent();
    return html`
    ${this.label
  ? html`
      <label class="nile-auto-complete__label">${this.label}</label>
    `: nothing}
      <nile-dropdown ?disabled=${this.disabled} class="nile-dropdown--input" ?open=${this.isDropdownOpen} ?stay-open-on-select=${this.noDropdownClose} noOpenOnCLick exportparts="input, base">
        <nile-input class="nile-auto-complete--input"
          ?no-border=${this.noBorder}
          ?no-outline=${this.noOutline}
          ?no-padding=${this.noPadding}
          .disabled=${this.disabled}
          .value=${this.value}
          @nile-input=${this.handleSearch}
          @focus=${this.handleFocus}
          @click=${this.handleClick}
          slot="trigger"
          placeholder=${this.placeholder}
          exportparts="input, base"
        >
          ${this.loading?html`<nile-icon slot="suffix" color="var(--nile-colors-primary-600)" name="var(--nile-icon-button-loading-blue-animated, var(--ng-icon-button-loading-blue-animated))" method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))" library="system"></nile-icon>`:nothing}
        </nile-input>
        ${this.menuItems.length > 0 && !this.loading
          ? content
          : nothing}
      </nile-dropdown>
    `;
  }  
  
  getVirtualizedContent():TemplateResult{
    return html`
      <nile-menu class="virtualized__menu" @nile-select=${this.handleSelect} id="content-menu" exportparts="menu__items-wrapper:options__wrapper" style=${this.portal ? 'display: none;' : ''}>
        ${virtualize({
          items: this.menuItems,
          renderItem: (item:any):TemplateResult=>this.getItemRenderFunction(item),
					scroller:true
        })}
      </nile-menu>
    `
  }

  getContent():TemplateResult{
    return html`
      <nile-menu id="content-menu" @nile-select=${this.handleSelect} exportparts="menu__items-wrapper:options__wrapper" style=${this.portal ? 'display: none;' : ''}>
        ${this.menuItems.map((item: any)  => this.getItemRenderFunction(item))}
      </nile-menu>`
  }

  getItemRenderFunction(item: any): TemplateResult {
    const value = this.renderItemFunction(item);
    let strValue = "";
    if(value || typeof value === "number") {
        strValue = value.toString();
    }
    const hasTooltip = !!item.tooltip;
    const shouldShowTooltip =
      hasTooltip && (!item.tooltip.for || item.tooltip.for === 'menu');
  
    if (!shouldShowTooltip) {
      return html`
        <nile-menu-item value=${value}>${unsafeHTML(strValue)}</nile-menu-item>
      `;
    }
  
    let tooltipContent: string | null = null;
  
    const content = item.tooltip.content;
    if (content instanceof Promise) {
      tooltipContent = 'Loading...'; 
      content.then((resolved: string) => {
        item.tooltip.content = resolved;
        this.requestUpdate();  
      });
    } else {
      tooltipContent = content;
    }
  
    return html`
      <nile-menu-item value=${value}>
        <nile-lite-tooltip allowHTML .content=${tooltipContent ?? ""}>
          <span class="menu-item-text">${unsafeHTML(strValue)}</span>
        </nile-lite-tooltip>
      </nile-menu-item>
    `;
  }

  handleSelect(event: CustomEvent) {
    this.value = event.detail.value;
    this.emit('nile-complete', { value: event.detail.value });
    if (this.noDropdownClose) {
      this.isDropdownOpen = true;
      this.dropdownElement?.show();
    } else {
      this.isDropdownOpen = false;
      this.dropdownElement?.hide();
    }
  }

  private setVirtualMenuWidth() {
    const maxLengthOption = this.menuItems
      .reduce((acc: number, curr: any) => {
        const currLength = this.renderItemFunction(curr).length
        return acc > currLength ? acc : currLength
      }, 0)
    const defaultWith = 110;
    const pixelMultiplier = 9.5;
    const menuWidth = maxLengthOption * pixelMultiplier < defaultWith ? defaultWith : maxLengthOption * pixelMultiplier;
    this.style.setProperty("--virtual-scroll-container-width", menuWidth + "px");
  }

  private handleSearch(event: CustomEvent) {
    this.value = event.detail.value;

    // Filter menu items based on the search value
    this.menuItems = this.applyFilter(this.allMenuItems,this.filterFunction);

    this.isDropdownOpen = this.menuItems.length > 0;
    if (this.isDropdownOpen) {
      this.dropdownElement?.show();
      if (this.portal) {
        this.portalManager.updatePortalOptions();
      }
    }
  }

  public handleFocus() {
    if (!this.openOnFocus) {
      return;
    }

    if(this.portal) {
      this.inputElement?.focus();
    }

    // Delay opening the dropdown to allow focus to take effect
    setTimeout(() => {
      this.isDropdownOpen = true;
      this.dropdownElement?.show();
    }, 300);
  }

  private handleClick() {
    this.isDropdownOpen = true;
    this.dropdownElement?.show();
  }

  applyFilter<T>(list: T[], filterFn: (item: T,searchValue?:string) => boolean): T[] {
    if(typeof(list)!=='object') return []
    const res:T[]=[]
    list.forEach( el=> filterFn(el,this.value) && res.push(el) )
    return res
  }

}

export default NileAutoComplete;

declare global {
  interface HTMLElementTagNameMap {
    'nile-auto-complete': NileAutoComplete;
  }
}
