/**
 * Copyright Aquera Inc 2023
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */

import {
  LitElement,
  html,
  CSSResultArray,
  TemplateResult,
  PropertyValues,
} from 'lit-element';
import { customElement,property, state } from 'lit/decorators.js';
import { styles } from './nile-popover.css';
import NileElement from '../internal/nile-element';
import { watch } from '../internal/watch';
import { CSSResultGroup } from 'lit';
import { PopoverPortalManager } from './portal-manager';
import { VisibilityManager } from '../utilities/visibility-manager.js';


/**
 * Nile icon component.
 *
 * @tag nile-popover
 *
 */
@customElement('nile-popover')
export class NilePopover extends NileElement {
  /**
   * The styles for Popover
   * @remarks If you are extending this class you can extend the base styles with super. Eg `return [super(), myCustomStyles]`
   */
  public static get styles(): CSSResultGroup {
    return [styles];
  }

  /**
   * The preferred placement of the popover. Note that the actual placement may vary as needed to keep the tooltip
   * inside of the viewport.
   */
  @property() placement:
    | 'top'
    | 'top-start'
    | 'top-end'
    | 'right'
    | 'right-start'
    | 'right-end'
    | 'bottom'
    | 'bottom-start'
    | 'bottom-end'
    | 'left'
    | 'left-start'
    | 'left-end' = 'top';

  /** The distance in pixels from which to offset the popover away from its target. */
  @property({ type: Number }) distance = 18;

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

  @property({ type: Boolean, attribute: 'arrow' }) arrow = true;

  /** Gives the title to the popover */
  @property({ type: String }) title = '';

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

  @state() isShow:boolean = false;

  @property({ attribute: 'arrow-placement' }) arrowPlacement:
    | 'start'
    | 'end'
    | 'center'
    | 'anchor' = 'anchor';

  private portalManager: PopoverPortalManager | null = null;
  private visibilityManager?: VisibilityManager;

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

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


  /**
   * Enable this option to prevent the panel from being clipped when the component is placed inside a container with
   * `overflow: auto|scroll`. Hoisting uses a fixed positioning strategy that works in many, but not all, scenarios.
   */
  @property({ type: Boolean }) hoist = false;

  /**
   * Enable portal mode to render the popover content in a portal outside the component's DOM tree.
   * This provides better positioning control and prevents clipping issues in complex layouts.
   */
  @property({ type: Boolean }) portal = false;

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

  /* #endregion */

  /* #region Methods */

  /**
   * Render method
   * @slot This is a slot test
   */

  connectedCallback() {
    super.connectedCallback();
    this.emit('nile-init');
    document.addEventListener('click', this.handleDocumentClick);
    
    requestAnimationFrame(() => {
      if (this.portal && !this.portalManager) {
        this.portalManager = new PopoverPortalManager(this);
      }
    });
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.emit('nile-destroy');
    this.visibilityManager?.cleanup();
    document.removeEventListener('click', this.handleDocumentClick);
    
    if (this.portalManager) {
      this.portalManager.cleanupPortalAppend();
      this.portalManager = null;
    }
  }

  protected async firstUpdated(_changed: PropertyValues) {
    await this.updateComplete;
  
    const anchorSlot = this.renderRoot.querySelector('slot[name="anchor"]') as HTMLSlotElement | null;
    const anchorEl = anchorSlot?.assignedElements({ flatten: true })[0] as HTMLElement | undefined;
    
  
    this.visibilityManager = new VisibilityManager({
      host: this,
      target: anchorEl || null,
      enableVisibilityEffect: this.enableVisibilityEffect,
      enableTabClose: this.enableTabClose,
      isOpen: () => this.isShow,
      onAnchorOutOfView: () => {
        this.isShow = false;
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'anchor-out-of-view',
        });
      },
      onDocumentHidden: () => {
        this.isShow = false;
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'document-hidden',
        });
      },
      emit: (event, detail) => this.emit(`nile-${event}`, detail),
    });
  }

  
  protected updated(_changedProperties: PropertyValues): void {
    if (_changedProperties.has('open')) {
      this.isShow = this.open;
    }
    
    if (_changedProperties.has('portal')) {
      if (this.portal && !this.portalManager) {
        this.portalManager = new PopoverPortalManager(this);
      } else if (!this.portal && this.portalManager) {
        this.portalManager.cleanupPortalAppend();
        this.portalManager = null;
      }
    }
  }

  @watch('isShow')
  handleShowHide() {
    if (this.isShow) {
      this.emit('nile-show');
      this.visibilityManager?.setup();
      if (this.portal && this.portalManager) {
        this.portalManager.setupPortalAppend();
      } else if (this.portal && !this.portalManager) {
        this.portalManager = new PopoverPortalManager(this);
        this.portalManager.setupPortalAppend();
      }
    } else {
      this.emit('nile-hide');
      this.visibilityManager?.cleanup();
      if (this.portal && this.portalManager) {
        this.portalManager.cleanupPortalAppend();
      }
    }
  }

  public render(): TemplateResult {
    const shouldRenderContent = this.isShow && !(this.portal && this.portalManager);
    
    return html`
      <nile-popup
        active
        .arrow="${this.arrow && this.isShow && !this.portal}"
        distance="${this.distance}"
        placement="${this.placement}"
        @click=${(e: Event) => e.stopPropagation()}
        arrowPlacement="${this.arrowPlacement}"
        .flip="${this.flip}"
        shift
        strategy=${this.hoist ? 'fixed' : 'absolute'}
      >
        <slot
          slot="anchor"
          name="anchor"
          aria-describedby="tooltip"
          @click=${this.handleClick}
        ></slot>
        ${shouldRenderContent
          ? html` <div part="popover" class="popover__box">
              <slot name="title" class="popover__title">${this.title}</slot>
              <slot part="base"></slot>
              <slot name="action" class="popover__action"> </slot>
            </div>`
          : html``}
      </nile-popup>
    `;
  }

  private handleClick = () => {
    this.isShow = !this.isShow;
    const allPopovers = document.querySelectorAll('nile-popover');

    allPopovers.forEach(popover => {
      if (popover !== this) {
        popover.isShow = false;
      }
    });
  };

  private handleDocumentClick = () => {
    if (this.isShow && !this.preventOverlayClose) {
      this.isShow = false;
    }
  };

  public updatePortalContent(): void {
    if (this.portalManager) {
      this.portalManager.updatePortalAppendPosition();
    }
  }

  public forceReposition(): void {
    if (this.portalManager) {
      this.portalManager.forceReposition();
    }
  }

  public getCurrentPlacement(): string {
    if (this.portalManager) {
      return this.portalManager.getCurrentPlacement();
    }
    return this.placement;
  }

  public isPositioningOptimal(): boolean {
    if (this.portalManager) {
      return this.portalManager.isPositioningOptimal();
    }
    return true;
  }

  /* #endregion */
}

export default NilePopover;

declare global {
  interface HTMLElementTagNameMap {
    'nile-popover': NilePopover;
  }
}
