import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, OnInit, OnDestroy, HostListener, inject, ElementRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { signal, computed } from '@angular/core';

type OverlayPosition = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
type OverlaySize = 'auto' | 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
type OverlayVariant = 'default' | 'elevated' | 'floating' | 'modal' | 'popover';

export interface OverlayContainerConfig {
  position?: OverlayPosition;
  size?: OverlaySize;
  variant?: OverlayVariant;
  visible?: boolean;
  closable?: boolean;
  backdropClosable?: boolean;
  escapeClosable?: boolean;
  showBackdrop?: boolean;
  backdropBlur?: boolean;
  preventBodyScroll?: boolean;
  focusTrap?: boolean;
  autoFocus?: boolean;
  restoreFocus?: boolean;
  zIndex?: number;
  offsetX?: number;
  offsetY?: number;
}

@Component({
  selector: 'ui-overlay-container',
  standalone: true,
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    @if (isVisible()) {
      <div 
        class="ui-overlay-container"
        [class.ui-overlay-container--{{position}}]="position"
        [class.ui-overlay-container--{{size}}]="size"
        [class.ui-overlay-container--{{variant}}]="variant"
        [class.ui-overlay-container--visible]="isVisible()"
        [class.ui-overlay-container--entering]="isEntering()"
        [class.ui-overlay-container--leaving]="isLeaving()"
        [class.ui-overlay-container--backdrop-blur]="showBackdrop && backdropBlur"
        [style.--overlay-z-index]="zIndex"
        [style.--overlay-offset-x.px]="offsetX"
        [style.--overlay-offset-y.px]="offsetY"
        [attr.role]="variant === 'modal' ? 'dialog' : 'presentation'"
        [attr.aria-modal]="variant === 'modal' ? 'true' : null"
        [attr.aria-hidden]="!isVisible()"
        (keydown)="handleKeydown($event)"
        #overlayContainer
      >
        <!-- Backdrop -->
        @if (showBackdrop) {
          <div 
            class="ui-overlay-container__backdrop"
            [class.ui-overlay-container__backdrop--visible]="isVisible()"
            [class.ui-overlay-container__backdrop--entering]="isEntering()"
            [class.ui-overlay-container__backdrop--leaving]="isLeaving()"
            [class.ui-overlay-container__backdrop--blur]="backdropBlur"
            [attr.aria-hidden]="true"
            (click)="handleBackdropClick($event)"
          ></div>
        }

        <!-- Overlay Content -->
        <div 
          class="ui-overlay-container__content"
          [class.ui-overlay-container__content--visible]="isVisible()"
          [class.ui-overlay-container__content--entering]="isEntering()"
          [class.ui-overlay-container__content--leaving]="isLeaving()"
          (click)="$event.stopPropagation()"
          #overlayContent
        >
          <ng-content></ng-content>
        </div>
      </div>
    }
  `,
  styleUrl: './overlay-container.component.scss'
})
export class OverlayContainerComponent implements OnInit, OnDestroy {
  // Dependencies
  private elementRef = inject(ElementRef);

  // Inputs
  @Input() position: OverlayPosition = 'center';
  @Input() size: OverlaySize = 'auto';
  @Input() variant: OverlayVariant = 'default';
  @Input() closable = true;
  @Input() backdropClosable = true;
  @Input() escapeClosable = true;
  @Input() showBackdrop = true;
  @Input() backdropBlur = false;
  @Input() preventBodyScroll = true;
  @Input() focusTrap = false;
  @Input() autoFocus = false;
  @Input() restoreFocus = true;
  @Input() zIndex = 1000;
  @Input() offsetX = 0;
  @Input() offsetY = 0;

  // State Inputs
  @Input() set visible(value: boolean) {
    if (value !== this._visible()) {
      this._visible.set(value);
      if (value) {
        this.show();
      } else {
        this.hide();
      }
    }
  }

  get visible(): boolean {
    return this._visible();
  }

  // Outputs
  @Output() visibleChange = new EventEmitter<boolean>();
  @Output() shown = new EventEmitter<void>();
  @Output() hidden = new EventEmitter<void>();
  @Output() backdropClicked = new EventEmitter<MouseEvent>();
  @Output() escapePressed = new EventEmitter<KeyboardEvent>();

  // Internal state signals
  private _visible = signal(false);
  private _entering = signal(false);
  private _leaving = signal(false);

  // Computed signals
  readonly isVisible = computed(() => this._visible() || this._entering() || this._leaving());
  readonly isEntering = computed(() => this._entering());
  readonly isLeaving = computed(() => this._leaving());

  // Internal properties
  private originalBodyOverflow = '';
  private originalBodyPaddingRight = '';
  private focusableElements: HTMLElement[] = [];
  private previousActiveElement: Element | null = null;
  private currentFocusIndex = 0;
  private animationDuration = 200; // ms

  ngOnInit(): void {
    if (this.visible) {
      this.show();
    }

    // Set appropriate z-index based on variant
    if (!this.zIndex || this.zIndex === 1000) {
      this.setVariantZIndex();
    }
  }

  ngOnDestroy(): void {
    this.restoreBodyScroll();
    this.restoreFocusIfNeeded();
  }

  /**
   * Shows the overlay with animation
   */
  show(): void {
    if (this._visible()) return;

    this.preventBodyScrollIfEnabled();
    this.storePreviousFocusIfNeeded();
    
    this._visible.set(true);
    this._entering.set(true);
    this.visibleChange.emit(true);

    // Animation timing
    setTimeout(() => {
      this._entering.set(false);
      this.setupFocusTrapIfEnabled();
      this.autoFocusIfEnabled();
      this.shown.emit();
    }, this.animationDuration);
  }

  /**
   * Hides the overlay with animation
   */
  hide(): void {
    if (!this._visible()) return;

    this._leaving.set(true);

    // Animation timing
    setTimeout(() => {
      this._visible.set(false);
      this._leaving.set(false);
      this.restoreBodyScroll();
      this.restoreFocusIfNeeded();
      this.visibleChange.emit(false);
      this.hidden.emit();
    }, this.animationDuration);
  }

  /**
   * Closes the overlay (alias for hide)
   */
  close(): void {
    if (this.closable) {
      this.hide();
    }
  }

  /**
   * Toggles overlay visibility
   */
  toggle(): void {
    if (this._visible()) {
      this.hide();
    } else {
      this.show();
    }
  }

  /**
   * Handles backdrop click
   */
  handleBackdropClick(event: MouseEvent): void {
    if (this.backdropClosable) {
      this.close();
      this.backdropClicked.emit(event);
    }
  }

  /**
   * Handles keyboard events
   */
  handleKeydown(event: KeyboardEvent): void {
    if (event.key === 'Escape' && this.escapeClosable) {
      event.preventDefault();
      this.close();
      this.escapePressed.emit(event);
      return;
    }

    // Focus trap handling
    if (this.focusTrap && event.key === 'Tab') {
      this.handleTabKeyForFocusTrap(event);
    }
  }

  /**
   * Global keyboard listener for ESC key
   */
  @HostListener('document:keydown', ['$event'])
  onDocumentKeydown(event: KeyboardEvent): void {
    if (this._visible() && event.key === 'Escape' && this.escapeClosable) {
      event.preventDefault();
      this.close();
      this.escapePressed.emit(event);
    }
  }

  /**
   * Sets z-index based on variant
   */
  private setVariantZIndex(): void {
    switch (this.variant) {
      case 'modal':
        this.zIndex = 1050; // Higher than backdrop
        break;
      case 'popover':
        this.zIndex = 1030;
        break;
      case 'elevated':
        this.zIndex = 1020;
        break;
      case 'floating':
        this.zIndex = 1010;
        break;
      default:
        this.zIndex = 1000;
        break;
    }
  }

  /**
   * Prevents body scroll when overlay is open
   */
  private preventBodyScrollIfEnabled(): void {
    if (!this.preventBodyScroll) return;

    const body = document.body;
    const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;

    this.originalBodyOverflow = body.style.overflow;
    this.originalBodyPaddingRight = body.style.paddingRight;

    body.style.overflow = 'hidden';
    if (scrollbarWidth > 0) {
      body.style.paddingRight = `${scrollbarWidth}px`;
    }
  }

  /**
   * Restores body scroll
   */
  private restoreBodyScroll(): void {
    if (!this.preventBodyScroll) return;

    const body = document.body;
    body.style.overflow = this.originalBodyOverflow;
    body.style.paddingRight = this.originalBodyPaddingRight;
  }

  /**
   * Stores the currently focused element
   */
  private storePreviousFocusIfNeeded(): void {
    if (this.restoreFocus) {
      this.previousActiveElement = document.activeElement;
    }
  }

  /**
   * Restores focus to the previously focused element
   */
  private restoreFocusIfNeeded(): void {
    if (this.restoreFocus && this.previousActiveElement && 'focus' in this.previousActiveElement) {
      (this.previousActiveElement as HTMLElement).focus();
    }
  }

  /**
   * Auto focuses the first focusable element if enabled
   */
  private autoFocusIfEnabled(): void {
    if (!this.autoFocus) return;

    setTimeout(() => {
      this.updateFocusableElements();
      if (this.focusableElements.length > 0) {
        this.focusableElements[0].focus();
        this.currentFocusIndex = 0;
      }
    }, 100);
  }

  /**
   * Sets up focus trap within overlay
   */
  private setupFocusTrapIfEnabled(): void {
    if (!this.focusTrap) return;

    setTimeout(() => {
      this.updateFocusableElements();
      if (this.focusableElements.length > 0) {
        this.focusableElements[0].focus();
        this.currentFocusIndex = 0;
      }
    }, 100);
  }

  /**
   * Updates the list of focusable elements
   */
  private updateFocusableElements(): void {
    const overlayElement = this.elementRef.nativeElement.querySelector('.ui-overlay-container__content');
    if (!overlayElement) return;

    const focusableSelectors = [
      'button:not([disabled])',
      'input:not([disabled])',
      'textarea:not([disabled])',
      'select:not([disabled])',
      'a[href]',
      '[tabindex]:not([tabindex="-1"])',
      '[contenteditable="true"]'
    ].join(', ');

    this.focusableElements = Array.from(
      overlayElement.querySelectorAll(focusableSelectors)
    ) as HTMLElement[];
  }

  /**
   * Handles Tab key for focus trapping
   */
  private handleTabKeyForFocusTrap(event: KeyboardEvent): void {
    if (this.focusableElements.length === 0) return;

    const isShiftTab = event.shiftKey;
    
    if (isShiftTab) {
      // Shift + Tab - move to previous focusable element
      this.currentFocusIndex = this.currentFocusIndex <= 0 
        ? this.focusableElements.length - 1 
        : this.currentFocusIndex - 1;
    } else {
      // Tab - move to next focusable element
      this.currentFocusIndex = this.currentFocusIndex >= this.focusableElements.length - 1 
        ? 0 
        : this.currentFocusIndex + 1;
    }

    event.preventDefault();
    this.focusableElements[this.currentFocusIndex].focus();
  }

  /**
   * Public API method to apply configuration
   */
  configure(config: OverlayContainerConfig): void {
    Object.assign(this, config);
  }
}