import { Injectable, NgZone, OnDestroy } from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';

export interface KeyboardShortcut {
  key: string;
  ctrlKey?: boolean;
  shiftKey?: boolean;
  altKey?: boolean;
  metaKey?: boolean;
  preventDefault?: boolean;
  stopPropagation?: boolean;
  description?: string;
  handler: (event: KeyboardEvent) => void;
}

export interface KeyboardShortcutRegistration {
  id: string;
  shortcut: KeyboardShortcut;
  element?: Element;
  priority?: number;
}

@Injectable({
  providedIn: 'root'
})
export class KeyboardManagerService implements OnDestroy {
  private destroy$ = new Subject<void>();
  private shortcuts = new Map<string, KeyboardShortcutRegistration>();
  private globalShortcuts: KeyboardShortcutRegistration[] = [];
  private elementShortcuts = new Map<Element, KeyboardShortcutRegistration[]>();

  constructor(private ngZone: NgZone) {
    this.setupGlobalListener();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
    this.shortcuts.clear();
    this.globalShortcuts.length = 0;
    this.elementShortcuts.clear();
  }

  /**
   * Register a global keyboard shortcut
   */
  registerGlobalShortcut(
    id: string,
    shortcut: KeyboardShortcut,
    priority = 0
  ): void {
    const registration: KeyboardShortcutRegistration = {
      id,
      shortcut,
      priority
    };

    this.shortcuts.set(id, registration);
    this.globalShortcuts.push(registration);
    
    // Sort by priority (higher priority first)
    this.globalShortcuts.sort((a, b) => (b.priority || 0) - (a.priority || 0));
  }

  /**
   * Register a keyboard shortcut for a specific element
   */
  registerElementShortcut(
    id: string,
    element: Element,
    shortcut: KeyboardShortcut,
    priority = 0
  ): void {
    const registration: KeyboardShortcutRegistration = {
      id,
      shortcut,
      element,
      priority
    };

    this.shortcuts.set(id, registration);
    
    if (!this.elementShortcuts.has(element)) {
      this.elementShortcuts.set(element, []);
      this.setupElementListener(element);
    }
    
    const elementShortcuts = this.elementShortcuts.get(element)!;
    elementShortcuts.push(registration);
    
    // Sort by priority (higher priority first)
    elementShortcuts.sort((a, b) => (b.priority || 0) - (a.priority || 0));
  }

  /**
   * Unregister a keyboard shortcut
   */
  unregisterShortcut(id: string): void {
    const registration = this.shortcuts.get(id);
    if (!registration) return;

    if (registration.element) {
      // Remove from element shortcuts
      const elementShortcuts = this.elementShortcuts.get(registration.element);
      if (elementShortcuts) {
        const index = elementShortcuts.findIndex(s => s.id === id);
        if (index >= 0) {
          elementShortcuts.splice(index, 1);
        }
        
        // Clean up if no shortcuts left for this element
        if (elementShortcuts.length === 0) {
          this.elementShortcuts.delete(registration.element);
        }
      }
    } else {
      // Remove from global shortcuts
      const index = this.globalShortcuts.findIndex(s => s.id === id);
      if (index >= 0) {
        this.globalShortcuts.splice(index, 1);
      }
    }

    this.shortcuts.delete(id);
  }

  /**
   * Get all registered shortcuts
   */
  getShortcuts(): KeyboardShortcutRegistration[] {
    return Array.from(this.shortcuts.values());
  }

  /**
   * Get shortcuts for a specific element
   */
  getElementShortcuts(element: Element): KeyboardShortcutRegistration[] {
    return this.elementShortcuts.get(element) || [];
  }

  /**
   * Get global shortcuts
   */
  getGlobalShortcuts(): KeyboardShortcutRegistration[] {
    return [...this.globalShortcuts];
  }

  /**
   * Unregister all keyboard shortcuts
   */
  unregisterAll(): void {
    this.shortcuts.clear();
    this.globalShortcuts.length = 0;
    this.elementShortcuts.clear();
  }

  /**
   * Check if a keyboard event matches a shortcut
   */
  private matchesShortcut(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {
    if (event.key.toLowerCase() !== shortcut.key.toLowerCase()) {
      return false;
    }

    return (
      !!event.ctrlKey === !!shortcut.ctrlKey &&
      !!event.shiftKey === !!shortcut.shiftKey &&
      !!event.altKey === !!shortcut.altKey &&
      !!event.metaKey === !!shortcut.metaKey
    );
  }

  /**
   * Handle keyboard event for shortcuts
   */
  private handleKeyboardEvent(
    event: KeyboardEvent,
    shortcuts: KeyboardShortcutRegistration[]
  ): boolean {
    for (const registration of shortcuts) {
      if (this.matchesShortcut(event, registration.shortcut)) {
        if (registration.shortcut.preventDefault) {
          event.preventDefault();
        }
        
        if (registration.shortcut.stopPropagation) {
          event.stopPropagation();
        }

        this.ngZone.run(() => {
          registration.shortcut.handler(event);
        });

        return true; // Shortcut handled
      }
    }
    
    return false; // No shortcut matched
  }

  /**
   * Set up global keyboard event listener
   */
  private setupGlobalListener(): void {
    if (typeof document === 'undefined') return;

    this.ngZone.runOutsideAngular(() => {
      fromEvent<KeyboardEvent>(document, 'keydown').pipe(
        takeUntil(this.destroy$),
        filter(event => this.globalShortcuts.length > 0)
      ).subscribe(event => {
        this.handleKeyboardEvent(event, this.globalShortcuts);
      });
    });
  }

  /**
   * Set up keyboard event listener for a specific element
   */
  private setupElementListener(element: Element): void {
    this.ngZone.runOutsideAngular(() => {
      fromEvent<KeyboardEvent>(element, 'keydown').pipe(
        takeUntil(this.destroy$)
      ).subscribe(event => {
        const shortcuts = this.elementShortcuts.get(element) || [];
        this.handleKeyboardEvent(event, shortcuts);
      });
    });
  }

  /**
   * Create a shortcut description for display purposes
   */
  static getShortcutDescription(shortcut: KeyboardShortcut): string {
    if (shortcut.description) {
      return shortcut.description;
    }

    const parts: string[] = [];
    
    if (shortcut.ctrlKey) parts.push('Ctrl');
    if (shortcut.altKey) parts.push('Alt');
    if (shortcut.shiftKey) parts.push('Shift');
    if (shortcut.metaKey) parts.push('Meta');
    
    parts.push(shortcut.key.toUpperCase());
    
    return parts.join(' + ');
  }
}