import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';

type ScrollbarVisibility = 'auto' | 'always' | 'never';
type ScrollDirection = 'vertical' | 'horizontal' | 'both';
type ScrollBehavior = 'smooth' | 'auto';

export interface ScrollVirtualConfig {
  itemHeight: number;
  bufferSize?: number;
  enabled?: boolean;
}

export interface ScrollPosition {
  top: number;
  left: number;
}

@Component({
  selector: 'ui-scroll-container',
  standalone: true,
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    <div 
      #scrollContainer
      class="ui-scroll-container"
      [class.ui-scroll-container--vertical]="direction === 'vertical'"
      [class.ui-scroll-container--horizontal]="direction === 'horizontal'"
      [class.ui-scroll-container--both]="direction === 'both'"
      [class.ui-scroll-container--scrollbar-auto]="scrollbarVisibility === 'auto'"
      [class.ui-scroll-container--scrollbar-always]="scrollbarVisibility === 'always'"
      [class.ui-scroll-container--scrollbar-never]="scrollbarVisibility === 'never'"
      [class.ui-scroll-container--virtual]="virtualScrollConfig?.enabled"
      [class.ui-scroll-container--smooth]="scrollBehavior === 'smooth'"
      [attr.role]="role"
      [attr.aria-label]="ariaLabel"
      [tabindex]="tabIndex"
      (scroll)="onScroll($event)"
      (keydown)="onKeyDown($event)">
      
      @if (virtualScrollConfig?.enabled && items && items.length > 0) {
        <!-- Virtual Scrolling Content -->
        <div class="ui-scroll-container__virtual-spacer" [style.height.px]="totalHeight">
          <div class="ui-scroll-container__virtual-content" [style.transform]="'translateY(' + offsetY + 'px)'">
            @for (item of visibleItems; track trackByFn ? trackByFn(item) : item) {
              <div 
                class="ui-scroll-container__virtual-item" 
                [style.height.px]="virtualScrollConfig?.itemHeight">
                <ng-content [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ item: item, index: getItemIndex(item) }"></ng-content>
              </div>
            }
          </div>
        </div>
      } @else {
        <!-- Regular Content -->
        <div class="ui-scroll-container__content">
          <ng-content></ng-content>
        </div>
      }
      
      @if (showScrollIndicators) {
        <div class="ui-scroll-container__indicators">
          @if (canScrollUp) {
            <div class="ui-scroll-container__indicator ui-scroll-container__indicator--up" aria-hidden="true"></div>
          }
          @if (canScrollDown) {
            <div class="ui-scroll-container__indicator ui-scroll-container__indicator--down" aria-hidden="true"></div>
          }
        </div>
      }
    </div>
  `,
  styleUrl: './scroll-container.component.scss'
})
export class ScrollContainerComponent implements AfterViewInit, OnDestroy {
  @ViewChild('scrollContainer', { static: true }) scrollContainer!: ElementRef<HTMLDivElement>;

  // Basic Configuration
  @Input() direction: ScrollDirection = 'vertical';
  @Input() scrollbarVisibility: ScrollbarVisibility = 'auto';
  @Input() scrollBehavior: ScrollBehavior = 'auto';
  @Input() showScrollIndicators = false;
  @Input() role = 'region';
  @Input() ariaLabel = 'Scrollable content';
  @Input() tabIndex = 0;

  // Virtual Scrolling
  @Input() virtualScrollConfig?: ScrollVirtualConfig;
  @Input() items?: any[];
  @Input() itemTemplate?: any;
  @Input() trackByFn?: (item: any) => any;

  // Scroll Position Restoration
  @Input() restoreScrollPosition = false;
  @Input() scrollPositionKey?: string;

  // Events
  @Output() scrolled = new EventEmitter<ScrollPosition>();
  @Output() scrollStart = new EventEmitter<void>();
  @Output() scrollEnd = new EventEmitter<void>();
  @Output() reachedTop = new EventEmitter<void>();
  @Output() reachedBottom = new EventEmitter<void>();

  // Virtual Scrolling State
  visibleItems: any[] = [];
  totalHeight = 0;
  offsetY = 0;
  startIndex = 0;
  endIndex = 0;

  // Scroll State
  canScrollUp = false;
  canScrollDown = false;
  private scrollTimeout?: number;
  private isScrolling = false;

  ngAfterViewInit(): void {
    if (this.virtualScrollConfig?.enabled && this.items) {
      this.setupVirtualScrolling();
    }

    if (this.restoreScrollPosition) {
      this.restorePosition();
    }

    this.updateScrollIndicators();
  }

  ngOnDestroy(): void {
    if (this.scrollTimeout) {
      clearTimeout(this.scrollTimeout);
    }

    if (this.restoreScrollPosition) {
      this.savePosition();
    }
  }

  onScroll(event: Event): void {
    const element = event.target as HTMLElement;
    const scrollPosition: ScrollPosition = {
      top: element.scrollTop,
      left: element.scrollLeft
    };

    if (!this.isScrolling) {
      this.isScrolling = true;
      this.scrollStart.emit();
    }

    this.scrolled.emit(scrollPosition);

    // Check if reached boundaries
    if (element.scrollTop === 0) {
      this.reachedTop.emit();
    }

    if (element.scrollTop + element.clientHeight >= element.scrollHeight - 1) {
      this.reachedBottom.emit();
    }

    // Update virtual scrolling
    if (this.virtualScrollConfig?.enabled) {
      this.updateVirtualScrolling(element.scrollTop);
    }

    this.updateScrollIndicators();

    // Debounce scroll end event
    if (this.scrollTimeout) {
      clearTimeout(this.scrollTimeout);
    }

    this.scrollTimeout = window.setTimeout(() => {
      this.isScrolling = false;
      this.scrollEnd.emit();
    }, 150);
  }

  onKeyDown(event: KeyboardEvent): void {
    const element = this.scrollContainer.nativeElement;
    let handled = false;

    switch (event.key) {
      case 'ArrowUp':
        this.scrollBy(0, -40);
        handled = true;
        break;
      case 'ArrowDown':
        this.scrollBy(0, 40);
        handled = true;
        break;
      case 'ArrowLeft':
        if (this.direction === 'horizontal' || this.direction === 'both') {
          this.scrollBy(-40, 0);
          handled = true;
        }
        break;
      case 'ArrowRight':
        if (this.direction === 'horizontal' || this.direction === 'both') {
          this.scrollBy(40, 0);
          handled = true;
        }
        break;
      case 'PageUp':
        this.scrollBy(0, -element.clientHeight * 0.8);
        handled = true;
        break;
      case 'PageDown':
        this.scrollBy(0, element.clientHeight * 0.8);
        handled = true;
        break;
      case 'Home':
        this.scrollTo({ top: 0, left: 0 });
        handled = true;
        break;
      case 'End':
        this.scrollTo({ top: element.scrollHeight, left: 0 });
        handled = true;
        break;
    }

    if (handled) {
      event.preventDefault();
    }
  }

  // Public API Methods
  scrollTo(position: Partial<ScrollPosition>): void {
    const element = this.scrollContainer.nativeElement;
    const options: ScrollToOptions = {
      behavior: this.scrollBehavior
    };

    if (position.top !== undefined) {
      options.top = position.top;
    }
    if (position.left !== undefined) {
      options.left = position.left;
    }

    element.scrollTo(options);
  }

  scrollBy(deltaX: number, deltaY: number): void {
    const element = this.scrollContainer.nativeElement;
    element.scrollBy({
      left: deltaX,
      top: deltaY,
      behavior: this.scrollBehavior
    });
  }

  scrollToTop(): void {
    this.scrollTo({ top: 0 });
  }

  scrollToBottom(): void {
    const element = this.scrollContainer.nativeElement;
    this.scrollTo({ top: element.scrollHeight });
  }

  getScrollPosition(): ScrollPosition {
    const element = this.scrollContainer.nativeElement;
    return {
      top: element.scrollTop,
      left: element.scrollLeft
    };
  }

  // Virtual Scrolling Implementation
  private setupVirtualScrolling(): void {
    if (!this.virtualScrollConfig || !this.items) return;

    const bufferSize = this.virtualScrollConfig.bufferSize || 10;
    const itemHeight = this.virtualScrollConfig.itemHeight;
    
    this.totalHeight = this.items.length * itemHeight;
    
    const containerHeight = this.scrollContainer.nativeElement.clientHeight;
    const visibleCount = Math.ceil(containerHeight / itemHeight) + bufferSize * 2;
    
    this.endIndex = Math.min(visibleCount, this.items.length);
    this.visibleItems = this.items.slice(this.startIndex, this.endIndex);
  }

  private updateVirtualScrolling(scrollTop: number): void {
    if (!this.virtualScrollConfig || !this.items) return;

    const itemHeight = this.virtualScrollConfig.itemHeight;
    const bufferSize = this.virtualScrollConfig.bufferSize || 10;
    const containerHeight = this.scrollContainer.nativeElement.clientHeight;
    
    this.startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
    const visibleCount = Math.ceil(containerHeight / itemHeight) + bufferSize * 2;
    this.endIndex = Math.min(this.startIndex + visibleCount, this.items.length);
    
    this.visibleItems = this.items.slice(this.startIndex, this.endIndex);
    this.offsetY = this.startIndex * itemHeight;
  }

  getItemIndex(item: any): number {
    if (!this.items) return -1;
    return this.items.indexOf(item);
  }

  // Scroll Position Restoration
  private savePosition(): void {
    if (!this.scrollPositionKey) return;
    
    const position = this.getScrollPosition();
    const key = `scroll-position-${this.scrollPositionKey}`;
    sessionStorage.setItem(key, JSON.stringify(position));
  }

  private restorePosition(): void {
    if (!this.scrollPositionKey) return;
    
    const key = `scroll-position-${this.scrollPositionKey}`;
    const saved = sessionStorage.getItem(key);
    
    if (saved) {
      try {
        const position = JSON.parse(saved) as ScrollPosition;
        setTimeout(() => this.scrollTo(position), 0);
      } catch (e) {
        console.warn('Failed to restore scroll position:', e);
      }
    }
  }

  // Scroll Indicators
  private updateScrollIndicators(): void {
    if (!this.showScrollIndicators) return;

    const element = this.scrollContainer.nativeElement;
    this.canScrollUp = element.scrollTop > 0;
    this.canScrollDown = element.scrollTop + element.clientHeight < element.scrollHeight - 1;
  }
}