import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, OnInit, OnDestroy, inject, ElementRef, HostListener } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faXmark } from '@fortawesome/free-solid-svg-icons';
import { signal, computed } from '@angular/core';

type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
type ModalVariant = 'default' | 'danger' | 'warning' | 'success';
type FooterAlignment = 'start' | 'center' | 'end' | 'between';

export interface ModalConfig {
  size?: ModalSize;
  variant?: ModalVariant;
  closable?: boolean;
  backdropClosable?: boolean;
  escapeClosable?: boolean;
  showHeader?: boolean;
  showFooter?: boolean;
  headerBorder?: boolean;
  footerBorder?: boolean;
  bodyPadding?: boolean;
  bodyScrollable?: boolean;
  footerAlignment?: FooterAlignment;
  preventBodyScroll?: boolean;
  focusTrap?: boolean;
}

@Component({
  selector: 'ui-modal',
  standalone: true,
  imports: [CommonModule, FontAwesomeModule],
  changeDetection: ChangeDetectionStrategy.Default,
  encapsulation: ViewEncapsulation.None,
  template: `
    @if (isVisible()) {
      <div 
        class="ui-modal"
        [class]="getComponentClasses()"
        role="dialog"
        [attr.aria-modal]="true"
        [attr.aria-labelledby]="titleId"
        [attr.aria-describedby]="contentId"
        (keydown)="handleKeydown($event)"
      >
        <!-- Modal Backdrop -->
        <div 
          class="ui-modal__backdrop"
          [class.ui-modal__backdrop--visible]="isVisible()"
          [class.ui-modal__backdrop--entering]="isEntering()"
          [class.ui-modal__backdrop--leaving]="isLeaving()"
          (click)="handleBackdropClick($event)"
        >
          <!-- Modal Container -->
          <div 
            class="ui-modal__container"
            [class.ui-modal__container--visible]="isVisible()"
            [class.ui-modal__container--entering]="isEntering()"
            [class.ui-modal__container--leaving]="isLeaving()"
            (click)="$event.stopPropagation()"
            #modalContainer
          >
            <!-- Modal Header -->
            @if (showHeader) {
              <div 
                class="ui-modal__header"
                [class.ui-modal__header--no-border]="!headerBorder"
              >
                <h2 
                  class="ui-modal__title" 
                  [id]="titleId"
                >
                  @if (title) {
                    {{ title }}
                  } @else {
                    <ng-content select="[slot='title']"></ng-content>
                  }
                </h2>
                
                @if (closable) {
                  <button 
                    type="button"
                    class="ui-modal__close-button"
                    (click)="close()"
                    [attr.aria-label]="closeAriaLabel"
                    #closeButton
                  >
                    <fa-icon [icon]="faXmark"></fa-icon>
                  </button>
                }
              </div>
            }

            <!-- Modal Body -->
            <div 
              class="ui-modal__body"
              [class.ui-modal__body--no-padding]="!bodyPadding"
              [class.ui-modal__body--scrollable]="bodyScrollable"
              [id]="contentId"
            >
              @if (loading) {
                <div class="ui-modal__loader" role="status" aria-label="Loading"></div>
              } @else {
                <div class="ui-modal__content">
                  <ng-content></ng-content>
                </div>
              }
            </div>

            <!-- Modal Footer -->
            @if (showFooter) {
              <div 
                class="ui-modal__footer"
                [class.ui-modal__footer--no-border]="!footerBorder"
                [class.ui-modal__footer--start]="footerAlignment === 'start'"
                [class.ui-modal__footer--center]="footerAlignment === 'center'"
                [class.ui-modal__footer--between]="footerAlignment === 'between'"
              >
                <ng-content select="[slot='footer']"></ng-content>
              </div>
            }
          </div>
        </div>
      </div>
    }
  `,
  styleUrl: './modal.component.scss'
})
export class ModalComponent implements OnInit, OnDestroy {
  // Dependencies
  private elementRef = inject(ElementRef);

  // Icons
  readonly faXmark = faXmark;

  // Inputs
  @Input() size: ModalSize = 'md';
  @Input() variant: ModalVariant = 'default';
  @Input() title = '';
  @Input() loading = false;
  @Input() closable = true;
  @Input() backdropClosable = true;
  @Input() escapeClosable = true;
  @Input() showHeader = true;
  @Input() showFooter = false;
  @Input() headerBorder = true;
  @Input() footerBorder = true;
  @Input() bodyPadding = true;
  @Input() bodyScrollable = true;
  @Input() footerAlignment: FooterAlignment = 'end';
  @Input() preventBodyScroll = true;
  @Input() focusTrap = true;
  @Input() closeAriaLabel = 'Close modal';

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

  get open(): boolean {
    return this._open();
  }

  // Outputs
  @Output() openChange = new EventEmitter<boolean>();
  @Output() opened = new EventEmitter<void>();
  @Output() closed = new EventEmitter<void>();
  @Output() backdropClicked = new EventEmitter<void>();
  @Output() escapePressed = new EventEmitter<void>();

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

  // Computed signals
  readonly isVisible = computed(() => this._open() || 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;

  // Unique IDs for accessibility
  readonly titleId = `modal-title-${Math.random().toString(36).substr(2, 9)}`;
  readonly contentId = `modal-content-${Math.random().toString(36).substr(2, 9)}`;

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

  getComponentClasses(): string {
    const classes = [
      'ui-modal',
      `ui-modal--${this.size}`,
      `ui-modal--${this.variant}`
    ];
    
    if (this.loading) {
      classes.push('ui-modal--loading');
    }
    
    return classes.join(' ');
  }

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

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

    this.preventBodyScrollIfEnabled();
    this.storePreviousFocus();
    
    this._open.set(true);
    this._entering.set(true);
    this.openChange.emit(true);

    // Animation timing
    setTimeout(() => {
      this._entering.set(false);
      this.setupFocusTrap();
      this.opened.emit();
    }, 300);
  }

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

    this._leaving.set(true);

    // Animation timing
    setTimeout(() => {
      this._open.set(false);
      this._leaving.set(false);
      this.restoreBodyScroll();
      this.restoreFocus();
      this.openChange.emit(false);
      this.closed.emit();
    }, 300);
  }

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

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

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

  /**
   * Handles keyboard events
   */
  handleKeydown(event: KeyboardEvent): void {
    if (event.key === 'Escape' && this.escapeClosable) {
      event.preventDefault();
      this.close();
      this.escapePressed.emit();
      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._open() && event.key === 'Escape' && this.escapeClosable) {
      event.preventDefault();
      this.close();
      this.escapePressed.emit();
    }
  }

  /**
   * Prevents body scroll when modal 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 storePreviousFocus(): void {
    this.previousActiveElement = document.activeElement;
  }

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

  /**
   * Sets up focus trap within modal
   */
  private setupFocusTrap(): 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 modalElement = this.elementRef.nativeElement.querySelector('.ui-modal__container');
    if (!modalElement) 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(
      modalElement.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: ModalConfig): void {
    Object.assign(this, config);
  }
}