import * as plugins from '../00plugins.js';
import { demoFunc } from './dees-contextmenu.demo.js';
import {
  customElement,
  html,
  DeesElement,
  property,
  type TemplateResult,
  cssManager,
  css,
  type CSSResult,
  unsafeCSS,
} from '@design.estate/dees-element';

import * as domtools from '@design.estate/dees-domtools';
import { DeesWindowLayer } from '../dees-windowlayer/dees-windowlayer.js';
import { zIndexLayers } from '../00zindex.js';
import '../dees-icon/dees-icon.js';
import { themeDefaultStyles } from '../00theme.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-contextmenu': DeesContextmenu;
  }
}

@customElement('dees-contextmenu')
export class DeesContextmenu extends DeesElement {
  // DEMO
  public static demo = demoFunc

  // STATIC
  // This will store all the accumulated menu items
  public static contextMenuDeactivated = false;
  public static accumulatedMenuItems: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean; submenu?: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean } | { divider: true })[] } | { divider: true })[] = [];

  // Add a global event listener for the right-click context menu
  public static initializeGlobalListener() {
    document.addEventListener('contextmenu', (event: MouseEvent) => {
      if (this.contextMenuDeactivated) {
        return;
      }
      event.preventDefault();

      // Clear previously accumulated items
      DeesContextmenu.accumulatedMenuItems = [];

      // Use composedPath to properly traverse shadow DOM boundaries
      const path = event.composedPath();
      
      // Traverse the composed path to accumulate menu items
      for (const element of path) {
        if ((element as any).getContextMenuItems) {
          const items = (element as any).getContextMenuItems();
          if (items && items.length > 0) {
            if (DeesContextmenu.accumulatedMenuItems.length > 0) {
              DeesContextmenu.accumulatedMenuItems.push({ divider: true });
            }
            DeesContextmenu.accumulatedMenuItems.push(...items);
          }
        }
      }

      // Open the context menu with the accumulated items
      DeesContextmenu.openContextMenuWithOptions(event, DeesContextmenu.accumulatedMenuItems);
    });
  }

  // allows opening of a contextmenu with options
  public static async openContextMenuWithOptions(eventArg: MouseEvent, menuItemsArg: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean; submenu?: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean } | { divider: true })[] } | { divider: true })[]) {
    if (this.contextMenuDeactivated) {
      return;
    }
    eventArg.preventDefault();
    eventArg.stopPropagation();
    const contextMenu = new DeesContextmenu();
    contextMenu.style.position = 'fixed';
    contextMenu.style.zIndex = String(zIndexLayers.overlay.contextMenu);
    contextMenu.style.opacity = '0';
    contextMenu.style.transform = 'scale(0.95) translateY(-10px)';
    contextMenu.menuItems = menuItemsArg;
    contextMenu.windowLayer = await DeesWindowLayer.createAndShow();
    contextMenu.windowLayer.addEventListener('click', async (event) => {
      // Check if click is on the context menu or its submenus
      const clickedElement = event.target as HTMLElement;
      const isContextMenu = clickedElement.closest('dees-contextmenu');
      if (!isContextMenu) {
        await contextMenu.destroy();
      }
    })
    document.body.append(contextMenu);
    
    // Get dimensions after adding to DOM
    await domtools.plugins.smartdelay.delayFor(0);
    const rect = contextMenu.getBoundingClientRect();
    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;
    
    // Calculate position
    let top = eventArg.clientY;
    let left = eventArg.clientX;
    
    // Adjust if menu would go off right edge
    if (left + rect.width > windowWidth) {
      left = windowWidth - rect.width - 10;
    }
    
    // Adjust if menu would go off bottom edge
    if (top + rect.height > windowHeight) {
      top = windowHeight - rect.height - 10;
    }
    
    // Ensure menu doesn't go off left or top edge
    if (left < 10) left = 10;
    if (top < 10) top = 10;
    
    contextMenu.style.top = `${top}px`;
    contextMenu.style.left = `${left}px`;
    contextMenu.style.transformOrigin = 'top left';
    
    // Animate in
    await domtools.plugins.smartdelay.delayFor(0);
    contextMenu.style.opacity = '1';
    contextMenu.style.transform = 'scale(1) translateY(0)';
  }

  // INSTANCE
  @property({
    type: Array,
  })
  accessor menuItems: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean; submenu?: (plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean } | { divider: true })[]; divider?: never } | { divider: true })[] = [];
  windowLayer: DeesWindowLayer;
  
  private submenu: DeesContextmenu | null = null;
  private submenuTimeout: any = null;
  private parentMenu: DeesContextmenu | null = null;
  private isDestroying: boolean = false;

  constructor() {
    super();
    this.tabIndex = 0;
  }

  /**
   * STATIC STYLES
   */
  public static styles = [
    themeDefaultStyles,
    cssManager.defaultStyles,
    css`
      /* TODO: Migrate hardcoded values to --dees-* CSS variables */
      :host {
        display: block;
        transition: opacity 0.2s, transform 0.2s;
        outline: none;
      }

      .mainbox {
        min-width: 200px;
        max-width: 280px;
        background: ${cssManager.bdTheme('#ffffff', '#000000')};
        border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
        border-radius: 4px;
        box-shadow: ${cssManager.bdTheme(
          '0 4px 12px rgba(0, 0, 0, 0.15)',
          '0 4px 12px rgba(0, 0, 0, 0.3)'
        )};
        user-select: none;
        padding: 4px 0;
        font-size: 12px;
        color: ${cssManager.bdTheme('#333', '#ccc')};
      }

      .menuitem {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 8px 12px;
        cursor: default;
        transition: background 0.1s;
        line-height: 1;
        position: relative;
      }

      .menuitem:hover {
        background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.08)')};
      }
      
      .menuitem.has-submenu::after {
        content: '›';
        position: absolute;
        right: 8px;
        font-size: 16px;
        opacity: 0.5;
      }

      .menuitem:active:not(.has-submenu) {
        background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.12)')};
      }
      
      .menuitem.disabled {
        opacity: 0.5;
        cursor: not-allowed;
        pointer-events: none;
      }

      .menuitem dees-icon {
        font-size: 14px;
        opacity: 0.7;
      }

      .menuitem-text {
        flex: 1;
      }

      .menuitem-shortcut {
        font-size: 11px;
        color: ${cssManager.bdTheme('#999', '#666')};
        margin-left: auto;
        opacity: 0.7;
      }

      .menu-divider {
        height: 1px;
        background: ${cssManager.bdTheme('#e0e0e0', '#202020')};
        margin: 4px 0;
      }
    `,
  ];

  public render(): TemplateResult {
    return html`
      <div class="mainbox">
        ${this.menuItems.map((menuItemArg) => {
          if ('divider' in menuItemArg && menuItemArg.divider) {
            return html`<div class="menu-divider"></div>`;
          }
          
          const menuItem = menuItemArg as plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean; submenu?: any };
          const hasSubmenu = menuItem.submenu && menuItem.submenu.length > 0;
          return html`
            <div 
              class="menuitem ${menuItem.disabled ? 'disabled' : ''} ${hasSubmenu ? 'has-submenu' : ''}" 
              @click=${() => !menuItem.disabled && !hasSubmenu && this.handleClick(menuItem)}
              @mouseenter=${() => this.handleMenuItemHover(menuItem, hasSubmenu)}
              @mouseleave=${() => this.handleMenuItemLeave()}
            >
              ${menuItem.iconName ? html`
                <dees-icon .icon="${menuItem.iconName}"></dees-icon>
              ` : ''}
              <span class="menuitem-text">${menuItem.name}</span>
              ${menuItem.shortcut && !hasSubmenu ? html`
                <span class="menuitem-shortcut">${menuItem.shortcut}</span>
              ` : ''}
            </div>
          `;
        })}
        ${this.menuItems.length === 0 ? html`
            <div class="menuitem" @click=${() => {
              DeesContextmenu.contextMenuDeactivated = true;
              this.destroy();
            }}>
              <dees-icon .icon="lucide:x"></dees-icon>
              <span class="menuitem-text">Allow native context</span>
            </div>
        ` : html``}
      </div>
    `;
  }

  public async firstUpdated() {
    // Focus on the menu for keyboard navigation
    this.focus();
    
    // Add keyboard event listeners
    this.addEventListener('keydown', this.handleKeydown);
  }
  
  private handleKeydown = (event: KeyboardEvent) => {
    const menuItems = Array.from(this.shadowRoot.querySelectorAll('.menuitem:not(.disabled)'));
    const currentIndex = menuItems.findIndex(item => item.matches(':hover'));
    
    switch (event.key) {
      case 'ArrowDown':
        event.preventDefault();
        const nextIndex = currentIndex + 1 < menuItems.length ? currentIndex + 1 : 0;
        (menuItems[nextIndex] as HTMLElement).dispatchEvent(new MouseEvent('mouseenter'));
        break;
        
      case 'ArrowUp':
        event.preventDefault();
        const prevIndex = currentIndex - 1 >= 0 ? currentIndex - 1 : menuItems.length - 1;
        (menuItems[prevIndex] as HTMLElement).dispatchEvent(new MouseEvent('mouseenter'));
        break;
        
      case 'Enter':
        event.preventDefault();
        if (currentIndex >= 0) {
          (menuItems[currentIndex] as HTMLElement).click();
        }
        break;
        
      case 'Escape':
        event.preventDefault();
        this.destroy();
        break;
    }
  }

  public async handleClick(menuItem: plugins.tsclass.website.IMenuItem & { shortcut?: string; disabled?: boolean }) {
    menuItem.action();
    
    // Close all menus in the chain (this menu and all parent menus)
    await this.destroyAll();
  }
  
  private async handleMenuItemHover(menuItem: plugins.tsclass.website.IMenuItem & { submenu?: any }, hasSubmenu: boolean) {
    // Clear any existing timeout
    if (this.submenuTimeout) {
      clearTimeout(this.submenuTimeout);
      this.submenuTimeout = null;
    }
    
    // Hide any existing submenu if hovering a different item
    if (this.submenu) {
      await this.hideSubmenu();
    }
    
    // Show submenu if this item has one
    if (hasSubmenu && menuItem.submenu) {
      this.submenuTimeout = setTimeout(() => {
        this.showSubmenu(menuItem);
      }, 200); // Small delay to prevent accidental triggers
    }
  }
  
  private handleMenuItemLeave() {
    // Add a delay before hiding to allow moving to submenu
    if (this.submenuTimeout) {
      clearTimeout(this.submenuTimeout);
    }
    
    this.submenuTimeout = setTimeout(() => {
      if (this.submenu && !this.submenu.matches(':hover')) {
        this.hideSubmenu();
      }
    }, 300);
  }
  
  private async showSubmenu(menuItem: plugins.tsclass.website.IMenuItem & { submenu?: any }) {
    if (!menuItem.submenu || menuItem.submenu.length === 0) return;
    
    // Find the menu item element
    const menuItems = Array.from(this.shadowRoot.querySelectorAll('.menuitem'));
    const menuItemElement = menuItems.find(el => el.querySelector('.menuitem-text')?.textContent === menuItem.name) as HTMLElement;
    if (!menuItemElement) return;
    
    // Create submenu
    this.submenu = new DeesContextmenu();
    this.submenu.menuItems = menuItem.submenu;
    this.submenu.parentMenu = this;
    this.submenu.style.position = 'fixed';
    this.submenu.style.zIndex = String(parseInt(this.style.zIndex) + 1);
    this.submenu.style.opacity = '0';
    this.submenu.style.transform = 'scale(0.95)';
    
    // Don't create a window layer for submenus
    document.body.append(this.submenu);
    
    // Position submenu
    await domtools.plugins.smartdelay.delayFor(0);
    const itemRect = menuItemElement.getBoundingClientRect();
    const menuRect = this.getBoundingClientRect();
    const submenuRect = this.submenu.getBoundingClientRect();
    const windowWidth = window.innerWidth;
    
    let left = menuRect.right - 4; // Slight overlap
    let top = itemRect.top;
    
    // Check if submenu would go off right edge
    if (left + submenuRect.width > windowWidth - 10) {
      // Show on left side instead
      left = menuRect.left - submenuRect.width + 4;
    }
    
    // Adjust vertical position if needed
    if (top + submenuRect.height > window.innerHeight - 10) {
      top = window.innerHeight - submenuRect.height - 10;
    }
    
    this.submenu.style.left = `${left}px`;
    this.submenu.style.top = `${top}px`;
    
    // Animate in
    await domtools.plugins.smartdelay.delayFor(0);
    this.submenu.style.opacity = '1';
    this.submenu.style.transform = 'scale(1)';
    
    // Handle submenu hover
    this.submenu.addEventListener('mouseenter', () => {
      if (this.submenuTimeout) {
        clearTimeout(this.submenuTimeout);
        this.submenuTimeout = null;
      }
    });
    
    this.submenu.addEventListener('mouseleave', () => {
      this.handleMenuItemLeave();
    });
  }
  
  private async hideSubmenu() {
    if (!this.submenu) return;
    
    await this.submenu.destroy();
    this.submenu = null;
  }

  public async destroy() {
    // Guard against double-destruction
    if (this.isDestroying) {
      return;
    }
    this.isDestroying = true;

    // Clear timeout
    if (this.submenuTimeout) {
      clearTimeout(this.submenuTimeout);
      this.submenuTimeout = null;
    }

    // Destroy submenu first
    if (this.submenu) {
      await this.submenu.destroy();
      this.submenu = null;
    }

    // Only destroy window layer if this is not a submenu
    // Don't await - let cleanup happen in background for instant visual feedback
    if (this.windowLayer && !this.parentMenu) {
      this.windowLayer.destroy();
    }

    this.style.opacity = '0';
    this.style.transform = 'scale(0.95) translateY(-10px)';
    await domtools.plugins.smartdelay.delayFor(100);

    if (this.parentElement) {
      this.parentElement.removeChild(this);
    }
  }
  
  /**
   * Destroys this menu and all parent menus in the chain
   */
  public async destroyAll() {
    // Find the root menu (top-level parent)
    let rootMenu: DeesContextmenu = this;
    while (rootMenu.parentMenu) {
      rootMenu = rootMenu.parentMenu;
    }

    // Destroy from the root - this will cascade through all submenus
    await rootMenu.destroy();
  }
}

DeesContextmenu.initializeGlobalListener();
