import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, ContentChildren, QueryList, AfterContentInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Subject, takeUntil } from 'rxjs';

export type AccordionSize = 'sm' | 'md' | 'lg';
export type AccordionVariant = 'elevated' | 'outlined' | 'filled';

export interface AccordionItem {
  id: string;
  title: string;
  content?: string;
  disabled?: boolean;
  expanded?: boolean;
}

export interface AccordionExpandEvent {
  item: AccordionItem;
  index: number;
  expanded: boolean;
}

@Component({
  selector: 'ui-accordion-item',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="ui-accordion__item"
         [class.ui-accordion__item--expanded]="expanded"
         [class.ui-accordion__item--disabled]="disabled">
      
      <button
        type="button"
        class="ui-accordion__header"
        [disabled]="disabled"
        [attr.aria-expanded]="expanded"
        [attr.aria-controls]="contentId"
        [attr.id]="headerId"
        (click)="handleToggle()"
        (keydown)="handleKeydown($event)">
        
        <div class="ui-accordion__header-content">
          <ng-content select="[slot='header']"></ng-content>
          @if (!hasHeaderContent) {
            <span>{{ title }}</span>
          }
        </div>
        
        <div class="ui-accordion__icon" aria-hidden="true">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
            <path d="M4.5 6l3.5 3.5L11.5 6"/>
          </svg>
        </div>
      </button>
      
      <div
        class="ui-accordion__content"
        [class.ui-accordion__content--expanded]="expanded"
        [class.ui-accordion__content--collapsed]="!expanded"
        [attr.id]="contentId"
        [attr.aria-labelledby]="headerId"
        role="region">
        
        @if (expanded || !collapseContent) {
          <div class="ui-accordion__content-inner">
            <ng-content></ng-content>
            @if (!hasContent && content) {
              <p>{{ content }}</p>
            }
          </div>
        }
      </div>
    </div>
  `
})
export class AccordionItemComponent {
  @Input() id!: string;
  @Input() title!: string;
  @Input() content?: string;
  @Input() disabled = false;
  @Input() expanded = false;
  @Input() collapseContent = true; // Whether to remove content from DOM when collapsed
  @Input() hasHeaderContent = false;
  @Input() hasContent = false;

  @Output() expandedChange = new EventEmitter<boolean>();
  @Output() itemToggle = new EventEmitter<AccordionExpandEvent>();

  get headerId(): string {
    return `accordion-header-${this.id}`;
  }

  get contentId(): string {
    return `accordion-content-${this.id}`;
  }

  handleToggle(): void {
    if (!this.disabled) {
      this.expanded = !this.expanded;
      this.expandedChange.emit(this.expanded);
      this.itemToggle.emit({
        item: {
          id: this.id,
          title: this.title,
          content: this.content,
          disabled: this.disabled,
          expanded: this.expanded
        },
        index: 0, // Will be set by parent accordion
        expanded: this.expanded
      });
    }
  }

  handleKeydown(event: KeyboardEvent): void {
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault();
      this.handleToggle();
    }
  }
}

@Component({
  selector: 'ui-accordion',
  standalone: true,
  imports: [CommonModule, AccordionItemComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    <div 
      class="ui-accordion"
      [class]="getComponentClasses()"
      [attr.aria-multiselectable]="multiple"
      role="group">
      
      @if (items && items.length > 0) {
        @for (item of items; track item.id; let i = $index) {
          <ui-accordion-item
            [id]="item.id"
            [title]="item.title"
            [content]="item.content"
            [disabled]="item.disabled || disabled"
            [expanded]="item.expanded || false"
            [collapseContent]="collapseContent"
            (itemToggle)="handleItemToggle($event, i)">
          </ui-accordion-item>
        }
      } @else {
        <ng-content></ng-content>
      }
    </div>
  `,
  styleUrl: './accordion.component.scss'
})
export class AccordionComponent implements AfterContentInit, OnDestroy {
  @Input() size: AccordionSize = 'md';
  @Input() variant: AccordionVariant = 'elevated';
  @Input() disabled = false;
  @Input() multiple = false; // Allow multiple items to be expanded simultaneously
  @Input() collapseContent = true; // Whether to remove content from DOM when collapsed
  @Input() items: AccordionItem[] = []; // Programmatic items
  @Input() expandedItems: string[] = []; // IDs of initially expanded items

  @Output() itemToggle = new EventEmitter<AccordionExpandEvent>();
  @Output() expandedItemsChange = new EventEmitter<string[]>();

  @ContentChildren(AccordionItemComponent) accordionItems!: QueryList<AccordionItemComponent>;

  private destroy$ = new Subject<void>();

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

  ngAfterContentInit(): void {
    // Set up keyboard navigation between accordion items
    this.setupKeyboardNavigation();
    
    // Initialize expanded states
    this.initializeExpandedStates();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  private setupKeyboardNavigation(): void {
    this.accordionItems.changes
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.accordionItems.forEach((item, index) => {
          // Update index for event emission
          const originalToggle = item.handleToggle.bind(item);
          item.handleToggle = () => {
            originalToggle();
            // Emit with correct index
            const event = {
              item: {
                id: item.id,
                title: item.title,
                content: item.content,
                disabled: item.disabled,
                expanded: item.expanded
              },
              index,
              expanded: item.expanded
            };
            this.handleItemToggle(event, index);
          };
        });
      });
  }

  private initializeExpandedStates(): void {
    if (this.items.length > 0) {
      // Initialize programmatic items
      this.items.forEach(item => {
        if (this.expandedItems.includes(item.id)) {
          item.expanded = true;
        }
      });
    }
  }

  handleItemToggle(event: AccordionExpandEvent, index: number): void {
    event.index = index;
    
    if (!this.multiple && event.expanded) {
      // Close other items if not in multiple mode
      this.closeOtherItems(event.item.id);
    }

    // Update expanded items list
    this.updateExpandedItems(event.item.id, event.expanded);
    
    // Emit events
    this.itemToggle.emit(event);
    this.expandedItemsChange.emit([...this.expandedItems]);
  }

  private closeOtherItems(exceptId: string): void {
    if (this.items.length > 0) {
      // Close other programmatic items
      this.items.forEach(item => {
        if (item.id !== exceptId && item.expanded) {
          item.expanded = false;
        }
      });
    }

    // Close other content-projected items
    this.accordionItems.forEach(item => {
      if (item.id !== exceptId && item.expanded) {
        item.expanded = false;
        item.expandedChange.emit(false);
      }
    });
  }

  private updateExpandedItems(itemId: string, expanded: boolean): void {
    if (expanded) {
      if (!this.expandedItems.includes(itemId)) {
        this.expandedItems.push(itemId);
      }
    } else {
      const index = this.expandedItems.indexOf(itemId);
      if (index > -1) {
        this.expandedItems.splice(index, 1);
      }
    }
  }

  // Public methods for programmatic control
  expandItem(itemId: string): void {
    const item = this.items.find(i => i.id === itemId);
    const accordionItem = this.accordionItems.find(i => i.id === itemId);
    
    if (item && !item.disabled) {
      if (!this.multiple) {
        this.closeAllItems();
      }
      item.expanded = true;
      this.updateExpandedItems(itemId, true);
    }
    
    if (accordionItem && !accordionItem.disabled) {
      if (!this.multiple) {
        this.closeAllItems();
      }
      accordionItem.expanded = true;
      accordionItem.expandedChange.emit(true);
      this.updateExpandedItems(itemId, true);
    }
  }

  collapseItem(itemId: string): void {
    const item = this.items.find(i => i.id === itemId);
    const accordionItem = this.accordionItems.find(i => i.id === itemId);
    
    if (item) {
      item.expanded = false;
      this.updateExpandedItems(itemId, false);
    }
    
    if (accordionItem) {
      accordionItem.expanded = false;
      accordionItem.expandedChange.emit(false);
      this.updateExpandedItems(itemId, false);
    }
  }

  expandAll(): void {
    if (this.multiple) {
      this.items.forEach(item => {
        if (!item.disabled) {
          item.expanded = true;
          this.updateExpandedItems(item.id, true);
        }
      });
      
      this.accordionItems.forEach(item => {
        if (!item.disabled) {
          item.expanded = true;
          item.expandedChange.emit(true);
          this.updateExpandedItems(item.id, true);
        }
      });
    }
  }

  collapseAll(): void {
    this.items.forEach(item => {
      item.expanded = false;
      this.updateExpandedItems(item.id, false);
    });
    
    this.accordionItems.forEach(item => {
      item.expanded = false;
      item.expandedChange.emit(false);
      this.updateExpandedItems(item.id, false);
    });
  }

  private closeAllItems(): void {
    this.items.forEach(item => {
      item.expanded = false;
    });
    
    this.accordionItems.forEach(item => {
      item.expanded = false;
      item.expandedChange.emit(false);
    });
    
    this.expandedItems = [];
  }
}