import { customElement, property } from 'lit/decorators.js';
import { CSSResultArray, PropertyValues } from 'lit';
import { styles } from './nile-lite-tooltip.css';
import NileElement from '../internal/nile-element';
import tippy, {
  Instance,
  Props,
  followCursor,
  roundArrow,
  createSingleton,
} from 'tippy.js';
import { parseFollowCursor, parseDuration,  } from './utils';
import { VisibilityManager } from '../utilities/visibility-manager.js';

/**
 * Nile lite-tooltip component.
 *
 * Supports wrapper, sibling (for), and singleton modes.
 * Aligns with Tippy.js props and Nile design system.
 *
 * @tag nile-lite-tooltip
 */
@customElement('nile-lite-tooltip')
export class NileliteTooltip extends NileElement {
  public static get styles(): CSSResultArray {
    return [styles];
  }

  protected createRenderRoot() {
    return this; // lite DOM
  }

  // ───────────── Props ─────────────
  /** ID of the target element (for sibling mode) */
  @property({ type: String, attribute: 'for' }) for: string | null = null;

  /** Tooltip content text or HTML */
  @property({ type: String, reflect: true }) content = '';

  /** Tooltip size (applies CSS class) */
  @property({ type: String, reflect: true }) size: 'small' | 'large' = 'small';

  /** Animation duration for show/hide (ms). Can be a single value or [show, hide]. */
  @property({ type: String, reflect: true }) duration:
    | string
    | number
    | [number, number] = 200;

  /** Interactive mode */
  @property({ type: Boolean, reflect: true }) interactive = false;

  /** Placement of the tooltip */
  @property({ type: String })
  placement:
    | 'top'
    | 'top-start'
    | 'top-end'
    | 'right'
    | 'right-start'
    | 'right-end'
    | 'bottom'
    | 'bottom-start'
    | 'bottom-end'
    | 'left'
    | 'left-start'
    | 'left-end'
    | 'auto'
    | 'auto-start'
    | 'auto-end' = 'top';

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

  @property({ type: Number }) distance = 8;

  @property({ type: Number }) skidding = 0;

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

  @property({ type: String }) trigger: Props['trigger'] = 'mouseenter focus';

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

  @property({ type: String, reflect: true, attribute: true })
  followCursor:
    | boolean
    | 'initial'
    | 'horizontal'
    | 'vertical'
    | 'true'
    | 'false' = false;

  @property({ type: String, reflect: true, attribute: true })
  arrow: 'default' | 'round' | 'large' | 'small' | 'wide' | 'narrow' | 'none' =
    'default';

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

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


  /** Not added in Doc */

  @property({ type: String, reflect: true }) delay:
    | number
    | [number, number] = 0;

  @property({ type: String, reflect: true }) maxWidth: string | number = 'auto';

  @property({ type: Number }) zIndex = 9999;

  @property({ type: [Boolean, String], reflect: true }) hideOnClick:
    | boolean
    | 'toggle' = true;

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

  @property({ type: Number }) interactiveBorder = 2;

  private visibilityManager?: VisibilityManager;

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

  @property({ type: String, reflect: true }) width?: string;
  @property({ type: String, reflect: true }) height?: string;


  private tooltipInstances?: Instance[];
  private singleInstance?: Instance;
  private singletonInstance?: Instance;
  private targetEl?: HTMLElement | null;
  private generatedId?: string;
  private prevDescribedby?: string | null;

  constructor() {
    super();
  }

  protected firstUpdated(): void {
    this.attachTooltip();
    const targetEl =
      (this.for && document.getElementById(this.for)) ||
      (this.firstElementChild as HTMLElement | null);
  
    this.visibilityManager = new VisibilityManager({
      host: this,
      target: targetEl,
      enableVisibilityEffect: this.enableVisibilityEffect,
      enableTabClose: this.enableTabClose,
      isOpen: () => this.open,
      onAnchorOutOfView: () => {
        this.open = false;
        this.hideAllTooltips();
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'anchor-out-of-view',
        });
      },
      onDocumentHidden: () => {
        this.open = false;
        this.hideAllTooltips();
        this.emit('nile-visibility-change', {
          visible: false,
          reason: 'document-hidden',
        });
      },
      emit: (event, detail) => this.emit(`nile-${event}`, detail),
    });
  }
  

  public refresh() {
    this.attachTooltip();
  }
  
  private hasValidContent(): boolean {
    const content =
      this.content ??
      this.getAttribute('data-tippy-content') ??
      '';
  
    return typeof content === 'string' && content.trim().length > 0;
  }
  

  private attachTooltip(): void {
    this.destroyTooltips();
    if (this.disabled ||(!this.enableRecursiveMode && !this.singleton && !this.hasValidContent())) {return;}
    

    const options: Partial<Props> = {
      content:
        this.content || this.getAttribute('data-tippy-content') || undefined,
      placement: this.placement,
      trigger: this.trigger,
      offset: [this.skidding, this.distance],
      theme: 'lite',
      animation: 'fade',
      interactive: this.interactive,
      arrow: roundArrow,
      duration: parseDuration(this.duration),
      allowHTML: this.allowHTML,
      delay: this.delay,
      maxWidth: this.maxWidth,
      zIndex: this.zIndex,
      hideOnClick: false,
      inertia: this.inertia,
      interactiveBorder: this.interactiveBorder,
      appendTo: document.body,
      followCursor: parseFollowCursor(this.followCursor),
      plugins: parseFollowCursor(this.followCursor) ? [followCursor] : [],
      onShow: instance => {
        this.open = true;
        const content = instance.popper.querySelector(
          '.tippy-content'
        ) as HTMLElement | null;
        if (content) {
          if (this.width) content.style.width = this.width;
          if (this.height) {
            content.style.height = this.height;
            content.style.overflow = 'auto';
          }
        }

        this.dispatchEvent(
          new CustomEvent('nile-show', {
            detail: { instance, target: instance.reference },
          })
        );
        this.dispatchEvent(
          new CustomEvent('nile-toggle', {
            detail: { open: true, instance, target: instance.reference },
          })
        );
        return undefined;
      },
      onHide: instance => {
        this.open = false;
        this.dispatchEvent(
          new CustomEvent('nile-hide', {
            detail: { instance, target: instance.reference },
          })
        );
        this.dispatchEvent(
          new CustomEvent('nile-toggle', {
            detail: { open: false, instance, target: instance.reference },
          })
        );
        return undefined;
      },
    };
    if (this.for) {
      if (this.singleton && !this.for.startsWith('#') && !document.getElementById(this.for)) {
        const targetEls = Array.from(document.querySelectorAll(`.${this.for}`));
        if (targetEls.length > 0) {
          this.tooltipInstances = targetEls.map(el => {
            const instance = tippy(el as HTMLElement, {
              ...options,
              content: el.getAttribute('content') || this.content,
            });
            instance.popper.querySelector('.tippy-box')?.classList.add(this.size);
            return instance;
          });
    
          this.singletonInstance = createSingleton(this.tooltipInstances, {
            delay: [75, 0],
            arrow: roundArrow,
            moveTransition: 'transform 0.15s ease-out',
          });
    
          if (this.open) queueMicrotask(() => this.singletonInstance?.show());
          return;
        }
      }
    
      const containerEl = document.getElementById(this.for);
      if (this.singleton && containerEl) {
        const childEls = Array.from(containerEl.querySelectorAll('[content]'));
        if (childEls.length > 0) {
          this.tooltipInstances = childEls.map(el => {
            const instance = tippy(el as HTMLElement, {
              ...options,
              content: el.getAttribute('content') || this.content,
            });
            instance.popper.querySelector('.tippy-box')?.classList.add(this.size);
            return instance;
          });
    
          this.singletonInstance = createSingleton(this.tooltipInstances, {
            delay: [75, 0],
            arrow: roundArrow,
            moveTransition: 'transform 0.15s ease-out',
          });
    
          if (this.open) queueMicrotask(() => this.singletonInstance?.show());
          return;
        }
      }
    
      this.targetEl = document.getElementById(this.for);
      if (!this.targetEl) return;

      if (!this.id) {
        this.generatedId = `nile-tooltip-${Math.random()
          .toString(36)
          .slice(2, 9)}`;
        this.id = this.generatedId;
      }

      this.prevDescribedby = this.targetEl.getAttribute('aria-describedby');
      const describedby = this.prevDescribedby
        ? `${this.prevDescribedby} ${this.id}`
        : this.id;
      this.targetEl.setAttribute('aria-describedby', describedby);

      this.singleInstance = tippy(this.targetEl, options);
      if (this.size)
        this.singleInstance.popper
          .querySelector('.tippy-box')
          ?.classList.add(this.size);
      if (this.open) {
        queueMicrotask(() => this.singleInstance?.show());
      }
      return;
    }
  

if (this.enableRecursiveMode) {
  const children = Array.from( this.querySelectorAll('[content]')).filter(el => {
    const value = el.getAttribute('content');
    return value !== null && value.trim().length > 0;
  });
  
  if (children.length > 0) {
    this.tooltipInstances = children.map(child => {
      const el = child as HTMLElement;
      const localContent = el.getAttribute('content') || this.content;
      const instance = tippy(el, { ...options, content: localContent });
      instance.popper.querySelector('.tippy-box')?.classList.add(this.size);
      return instance;
    });

    if (this.singleton && this.tooltipInstances.length > 1) {
      this.singletonInstance = createSingleton(this.tooltipInstances, {
        delay: [75, 0],
        arrow: roundArrow,
        moveTransition: 'transform 0.15s ease-out',
      });
    }

    if (this.open) {
      if (this.singletonInstance) this.singletonInstance.show();
      else this.tooltipInstances.forEach(t => t.show());
    }
  }

} else {
  const firstChild = this.firstElementChild as HTMLElement | null;

  if (firstChild) {
    const localContent = firstChild.getAttribute('content') || this.content;
    const instance = tippy(firstChild, { ...options, content: localContent });
    instance.popper.querySelector('.tippy-box')?.classList.add(this.size);

    this.tooltipInstances = [instance];

    if (this.open) {
      instance.show();
    }
  }
}  
  }
  

  private destroyTooltips(): void {
    this.tooltipInstances?.forEach(t => t.destroy());
    this.singleInstance?.destroy();
    this.singletonInstance?.destroy();

    this.tooltipInstances = undefined;
    this.singleInstance = undefined;
    this.singletonInstance = undefined;

    if (this.targetEl && this.id) {
      const current = this.targetEl
        .getAttribute('aria-describedby')
        ?.split(' ')
        .filter(id => id !== this.id)
        .join(' ')
        .trim();
      if (current) {
        this.targetEl.setAttribute('aria-describedby', current);
      } else {
        this.targetEl.removeAttribute('aria-describedby');
      }
    }
  }

  private hideAllTooltips(): void {
    this.singleInstance?.hide();
    this.singletonInstance?.hide();
    this.tooltipInstances?.forEach(t => t.hide());
  }

  
  disconnectedCallback(): void {
    super.disconnectedCallback();
    this.visibilityManager?.cleanup();
    this.destroyTooltips();
  }

  updated(changed: PropertyValues): void {
    super.updated(changed);

    if (
      [
        'for',
        'content',
        'placement',
        'distance',
        'skidding',
        'trigger',
        'disabled',
        'hoist',
        'size',
        'arrow',
        'singleton',
      ].some(p => changed.has(p))
    ) {
      this.attachTooltip();
    }

    if (changed.has('open')) {
      if (this.open) {
        this.visibilityManager?.setup();
        this.singleInstance?.show();
        this.singletonInstance?.show();
        this.tooltipInstances?.forEach(t => t.show());
      } else {
        this.visibilityManager?.cleanup();
        this.singleInstance?.hide();
        this.singletonInstance?.hide();
        this.tooltipInstances?.forEach(t => t.hide());
      }
    }
  }
}

export default NileliteTooltip;

declare global {
  interface HTMLElementTagNameMap {
    'nile-lite-tooltip': NileliteTooltip;
  }
}
