// ============ 类型定义 ============

export interface PostTocItem {
  id: string;
  level: number;
  title: string;
  element: HTMLElement;
  tagName: string;
}

// ============ 工具函数 ============

const generateId = (counter: number): string =>
  `post-toc-${counter}-${Date.now()}`;

const ensureElementId = (element: HTMLElement, fallbackId: string): string => {
  if (!element.id) element.id = fallbackId;
  return element.id;
};

const findTopVisibleEntry = (entries: IntersectionObserverEntry[]): IntersectionObserverEntry | null => {
  const visible = entries.filter(e => e.isIntersecting);
  if (visible.length === 0) return null;

  return visible.reduce((prev, curr) =>
    curr.boundingClientRect.top < prev.boundingClientRect.top ? curr : prev
  );
};

const createScrollObserver = (onActiveChange: (id: string) => void): IntersectionObserver =>
  new IntersectionObserver(
    (entries) => {
      const topEntry = findTopVisibleEntry(entries);
      if (topEntry) onActiveChange(topEntry.target.id);
    },
    { rootMargin: '-80px 0px -70% 0px', threshold: [0, 0.5, 1] }
  );

// ============ 事件常量 ============

export const TOC_EVENTS = {
  CLEAR: 'post-toc:clear',
  ADD: 'post-toc:add',
  HIGHLIGHT: 'post-toc:init-highlight'
} as const;

// ============ Alpine 组件 ============

export const postToc = () => ({
  items: [] as PostTocItem[],
  activeId: '',
  observer: null as IntersectionObserver | null,

  // 私有属性
  _isInitialized: false,
  _counter: 0,
  _minLevel: 6,

  init() {
    if (this._isInitialized) return;

    document.addEventListener(TOC_EVENTS.CLEAR, () => this.clear());

    document.addEventListener(TOC_EVENTS.ADD, (e) => {
      const { level, title, element, tagName } = (e as CustomEvent).detail;

      this.addItem(level, title, element, tagName);
    });

    document.addEventListener(TOC_EVENTS.HIGHLIGHT, () => this.initScrollHighlight());

    this._isInitialized = true;
  },

  addItem(level: number, title: string, element: HTMLElement, tagName: string) {
    this._counter++;

    const id = ensureElementId(element, generateId(this._counter));

    if (level < this._minLevel) this._minLevel = level;

    this.items = [...this.items, { id, level, title, element, tagName }];
  },

  clear() {
    this.items = [];
    this.activeId = '';
    this._counter = 0;
    this._minLevel = 6;
    this.observer?.disconnect();
  },

  initScrollHighlight() {
    this.observer?.disconnect();
    this.observer = createScrollObserver((id) => { this.activeId = id; });
    this.items.forEach(item => this.observer?.observe(item.element));
  },

  scrollTo(id: string) {
    const element = document.getElementById(id);
    if (!element) return;

    element.scrollIntoView({ behavior: 'smooth', block: 'start' });
    this.activeId = id;
  },

  getIndent(level: number): string {
    return `${(level - this._minLevel) * 16}px`;
  }
});
