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

export interface TreeNode {
  id: string;
  label: string;
  icon?: string;
  children?: TreeNode[];
  expanded?: boolean;
  selected?: boolean;
  disabled?: boolean;
  data?: any;
}

type TreeViewSize = 'sm' | 'md' | 'lg';
type TreeViewVariant = 'primary' | 'secondary';

@Component({
  selector: 'ui-tree-node',
  standalone: true,
  imports: [CommonModule, TreeNodeComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    <div
      class="ui-tree-view__node"
      [class.ui-tree-view__node--selected]="node.selected"
      [class.ui-tree-view__node--disabled]="node.disabled"
      [style.paddingLeft.px]="level * 20"
      [attr.aria-selected]="node.selected"
      [attr.aria-expanded]="hasChildren ? node.expanded : null"
      [attr.aria-level]="level + 1"
      [attr.aria-disabled]="node.disabled"
      [tabindex]="node.disabled ? -1 : 0"
      role="treeitem"
      (click)="handleNodeClick()"
      (keydown)="handleKeydown($event)">
      
      @if (hasChildren) {
        <button
          class="ui-tree-view__expand-button"
          [class.ui-tree-view__expand-button--expanded]="node.expanded"
          [attr.aria-label]="node.expanded ? 'Collapse' : 'Expand'"
          [disabled]="node.disabled"
          (click)="handleToggleClick($event)"
          type="button">
          ▶
        </button>
      } @else {
        <div class="ui-tree-view__expand-button ui-tree-view__expand-button--leaf">
          •
        </div>
      }
      
      <div class="ui-tree-view__node-content">
        @if (showIcons && node.icon) {
          <span class="ui-tree-view__node-icon" [attr.aria-hidden]="true">
            {{ node.icon }}
          </span>
        }
        
        <span class="ui-tree-view__node-label">
          {{ node.label }}
        </span>
      </div>
    </div>
    
    @if (hasChildren && node.expanded) {
      <div class="ui-tree-view__children" 
           [class.ui-tree-view__children--hidden]="!node.expanded"
           role="group">
        @for (childNode of node.children; track childNode.id) {
          <ui-tree-node
            [node]="childNode"
            [level]="level + 1"
            [showIcons]="showIcons"
            [multiSelect]="multiSelect"
            (nodeToggled)="nodeToggled.emit($event)"
            (nodeSelected)="nodeSelected.emit($event)">
          </ui-tree-node>
        }
      </div>
    }
  `
})
export class TreeNodeComponent {
  @Input() node!: TreeNode;
  @Input() level = 0;
  @Input() showIcons = true;
  @Input() multiSelect = false;
  
  @Output() nodeToggled = new EventEmitter<{ node: TreeNode; expanded: boolean }>();
  @Output() nodeSelected = new EventEmitter<{ node: TreeNode; selected: boolean }>();
  
  get hasChildren(): boolean {
    return !!(this.node.children && this.node.children.length > 0);
  }
  
  handleNodeClick(): void {
    if (!this.node.disabled) {
      this.nodeSelected.emit({
        node: this.node,
        selected: !this.node.selected
      });
    }
  }
  
  handleToggleClick(event: Event): void {
    event.stopPropagation();
    if (!this.node.disabled && this.hasChildren) {
      this.nodeToggled.emit({
        node: this.node,
        expanded: !this.node.expanded
      });
    }
  }
  
  handleKeydown(event: KeyboardEvent): void {
    if (this.node.disabled) return;
    
    switch (event.key) {
      case 'Enter':
      case ' ':
        event.preventDefault();
        this.handleNodeClick();
        break;
      case 'ArrowRight':
        if (this.hasChildren && !this.node.expanded) {
          event.preventDefault();
          this.handleToggleClick(event);
        }
        break;
      case 'ArrowLeft':
        if (this.hasChildren && this.node.expanded) {
          event.preventDefault();
          this.handleToggleClick(event);
        }
        break;
    }
  }
}

@Component({
  selector: 'ui-tree-view',
  standalone: true,
  imports: [CommonModule, TreeNodeComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    <div 
      class="ui-tree-view"
      [class.ui-tree-view--sm]="size === 'sm'"
      [class.ui-tree-view--md]="size === 'md'"
      [class.ui-tree-view--lg]="size === 'lg'"
      [class.ui-tree-view--primary]="variant === 'primary'"
      [class.ui-tree-view--secondary]="variant === 'secondary'"
      [attr.aria-label]="ariaLabel"
      role="tree">
      
      @if (loading) {
        <div class="ui-tree-view__loading" role="status" aria-live="polite">
          Loading...
        </div>
      } @else if (!nodes || nodes.length === 0) {
        <div class="ui-tree-view__empty" role="status">
          {{ emptyMessage }}
        </div>
      } @else {
        @for (node of nodes; track node.id) {
          <div class="ui-tree-view-node-wrapper">
            @if (renderTreeNode) {
              <ng-container [ngTemplateOutlet]="renderTreeNode" 
                           [ngTemplateOutletContext]="{ 
                             $implicit: node, 
                             level: 0,
                             onToggle: handleToggle,
                             onSelect: handleSelect
                           }">
              </ng-container>
            } @else {
              <ui-tree-node
                [node]="node"
                [level]="0"
                [showIcons]="showIcons"
                [multiSelect]="multiSelect"
                (nodeToggled)="handleToggle($event)"
                (nodeSelected)="handleSelect($event)">
              </ui-tree-node>
            }
          </div>
        }
      }
    </div>
  `,
  styleUrl: './tree-view.component.scss'
})
export class TreeViewComponent {
  @Input() nodes: TreeNode[] = [];
  @Input() size: TreeViewSize = 'md';
  @Input() variant: TreeViewVariant = 'primary';
  @Input() loading = false;
  @Input() showIcons = true;
  @Input() multiSelect = false;
  @Input() expandAll = false;
  @Input() emptyMessage = 'No items to display';
  @Input() ariaLabel = 'Tree view';
  @Input() renderTreeNode?: any; // Custom template reference
  
  @Output() nodeToggled = new EventEmitter<{ node: TreeNode; expanded: boolean }>();
  @Output() nodeSelected = new EventEmitter<{ node: TreeNode; selected: boolean }>();
  @Output() nodesChanged = new EventEmitter<TreeNode[]>();
  
  handleToggle = (event: { node: TreeNode; expanded: boolean }): void => {
    event.node.expanded = event.expanded;
    this.nodeToggled.emit(event);
    this.nodesChanged.emit(this.nodes);
  };
  
  handleSelect = (event: { node: TreeNode; selected: boolean }): void => {
    if (!this.multiSelect) {
      // Single select - clear all other selections
      this.clearAllSelections(this.nodes);
    }
    
    event.node.selected = event.selected;
    this.nodeSelected.emit(event);
    this.nodesChanged.emit(this.nodes);
  };
  
  private clearAllSelections(nodes: TreeNode[]): void {
    for (const node of nodes) {
      node.selected = false;
      if (node.children) {
        this.clearAllSelections(node.children);
      }
    }
  }
  
  // Public API methods
  expandAllNodes(): void {
    this.setAllExpanded(this.nodes, true);
    this.nodesChanged.emit(this.nodes);
  }
  
  collapseAllNodes(): void {
    this.setAllExpanded(this.nodes, false);
    this.nodesChanged.emit(this.nodes);
  }
  
  getSelectedNodes(): TreeNode[] {
    return this.findSelectedNodes(this.nodes);
  }
  
  selectNode(nodeId: string): void {
    const node = this.findNodeById(this.nodes, nodeId);
    if (node && !node.disabled) {
      this.handleSelect({ node, selected: true });
    }
  }
  
  private setAllExpanded(nodes: TreeNode[], expanded: boolean): void {
    for (const node of nodes) {
      if (node.children && node.children.length > 0) {
        node.expanded = expanded;
        this.setAllExpanded(node.children, expanded);
      }
    }
  }
  
  private findSelectedNodes(nodes: TreeNode[]): TreeNode[] {
    const selected: TreeNode[] = [];
    for (const node of nodes) {
      if (node.selected) {
        selected.push(node);
      }
      if (node.children) {
        selected.push(...this.findSelectedNodes(node.children));
      }
    }
    return selected;
  }
  
  private findNodeById(nodes: TreeNode[], id: string): TreeNode | null {
    for (const node of nodes) {
      if (node.id === id) {
        return node;
      }
      if (node.children) {
        const found = this.findNodeById(node.children, id);
        if (found) {
          return found;
        }
      }
    }
    return null;
  }
}