import { 
  Component, 
  Input, 
  Output, 
  EventEmitter,
  OnInit,
  OnDestroy,
  ChangeDetectionStrategy,
  signal, 
  computed,
  TemplateRef,
  ContentChild,
  ViewChild,
  ElementRef
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { Subject, timer } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export type LoadingState = 'idle' | 'loading' | 'success' | 'error';
export type LoadingVariant = 'spinner' | 'skeleton' | 'pulse' | 'shimmer';
export type LoadingSize = 'sm' | 'md' | 'lg' | 'xl';

export interface ErrorState {
  message: string;
  code?: string;
  details?: any;
  retryable?: boolean;
}

export interface LoadingConfig {
  variant: LoadingVariant;
  size: LoadingSize;
  message?: string;
  showProgress?: boolean;
  progress?: number;
  timeout?: number;
}

@Component({
  selector: 'ui-loading-state-container',
  standalone: true,
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div 
      class="loading-state-container"
      [class]="containerClasses()"
      [attr.data-state]="state"
      [attr.data-variant]="config().variant"
      [attr.data-size]="config().size"
      [attr.role]="state === 'loading' ? 'status' : null"
      [attr.aria-live]="state === 'loading' ? 'polite' : null"
      [attr.aria-busy]="state === 'loading'">
      
      <!-- Loading State -->
      @if (state === 'loading') {
        <div class="loading-overlay" [class.transparent]="transparentOverlay">
          <div class="loading-content">
            
            @switch (config().variant) {
              @case ('spinner') {
                <div class="spinner-container">
                  <div class="spinner"></div>
                  @if (config().message) {
                    <p class="loading-message">{{ config().message }}</p>
                  }
                  @if (config().showProgress && config().progress !== undefined) {
                    <div class="progress-bar">
                      <div class="progress-fill" 
                           [style.width.%]="config().progress">
                      </div>
                    </div>
                    <span class="progress-text">{{ config().progress }}%</span>
                  }
                </div>
              }
              
              @case ('skeleton') {
                @if (skeletonTemplate) {
                  <ng-container 
                    [ngTemplateOutlet]="skeletonTemplate">
                  </ng-container>
                } @else {
                  <div class="skeleton-container">
                    @for (item of skeletonItems(); track $index) {
                      <div class="skeleton-item" 
                           [style.height]="item.height"
                           [style.width]="item.width">
                      </div>
                    }
                  </div>
                }
              }
              
              @case ('pulse') {
                <div class="pulse-container">
                  <div class="pulse-dot"></div>
                  <div class="pulse-dot"></div>
                  <div class="pulse-dot"></div>
                </div>
                @if (config().message) {
                  <p class="loading-message">{{ config().message }}</p>
                }
              }
              
              @case ('shimmer') {
                <div class="shimmer-container">
                  <div class="shimmer-block large"></div>
                  <div class="shimmer-block medium"></div>
                  <div class="shimmer-block small"></div>
                </div>
              }
            }
            
            @if (showCancelButton && state === 'loading') {
              <button 
                class="cancel-button"
                (click)="cancelLoading()"
                type="button"
                [attr.aria-label]="cancelButtonText">
                {{ cancelButtonText }}
              </button>
            }
          </div>
        </div>
      }
      
      <!-- Error State -->
      @if (state === 'error') {
        <div class="error-overlay">
          <div class="error-content">
            
            @if (errorTemplate) {
              <ng-container 
                [ngTemplateOutlet]="errorTemplate"
                [ngTemplateOutletContext]="{ error: error() }">
              </ng-container>
            } @else {
              <div class="error-icon" aria-hidden="true">⚠</div>
              <h3 class="error-title">{{ errorTitle }}</h3>
              <p class="error-message">{{ error()?.message || defaultErrorMessage }}</p>
              
              @if (error()?.details && showErrorDetails) {
                <details class="error-details">
                  <summary>Technical Details</summary>
                  <pre>{{ error()?.details | json }}</pre>
                </details>
              }
              
              <div class="error-actions">
                @if (error()?.retryable !== false && showRetryButton) {
                  <button 
                    class="retry-button primary"
                    (click)="handleRetry()"
                    type="button">
                    {{ retryButtonText }}
                  </button>
                }
                
                @if (showReportButton) {
                  <button 
                    class="report-button secondary"
                    (click)="handleReportError()"
                    type="button">
                    {{ reportButtonText }}
                  </button>
                }
              </div>
            }
          </div>
        </div>
      }
      
      <!-- Success Content or Regular Content -->
      <div 
        class="main-content"
        [class.hidden]="state === 'loading' && !showContentWhileLoading"
        [attr.aria-hidden]="(state === 'loading' && !showContentWhileLoading) || state === 'error' ? 'true' : null">
        
        @if (state === 'success' && successTemplate && showSuccessState) {
          <div class="success-state">
            <ng-container [ngTemplateOutlet]="successTemplate"></ng-container>
          </div>
        } @else {
          <ng-content></ng-content>
        }
      </div>
    </div>
  `,
  styles: [`
    .loading-state-container {
      position: relative;
      width: 100%;
      height: 100%;
    }
    
    .loading-overlay {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(255, 255, 255, 0.8);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1000;
    }
    
    .spinner {
      width: 40px;
      height: 40px;
      border: 3px solid #f3f3f3;
      border-top: 3px solid #007bff;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }
    
    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }
    
    .error-overlay {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(255, 255, 255, 0.95);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1000;
    }
    
    .main-content {
      width: 100%;
      height: 100%;
    }
  `]
})
export class LoadingStateContainerComponent implements OnInit, OnDestroy {
  @ViewChild('container') containerElement!: ElementRef<HTMLDivElement>;
  
  @ContentChild('skeleton') skeletonTemplate?: TemplateRef<any>;
  @ContentChild('error') errorTemplate?: TemplateRef<{ error: ErrorState | null }>;
  @ContentChild('success') successTemplate?: TemplateRef<any>;
  
  // State management
  @Input() state: LoadingState = 'idle';
  @Input() error = signal<ErrorState | null>(null);
  @Input() config = signal<LoadingConfig>({
    variant: 'spinner',
    size: 'md',
    showProgress: false
  });
  
  // Loading behavior
  @Input() showContentWhileLoading = false;
  @Input() transparentOverlay = false;
  @Input() minimumLoadingTime = 500; // ms
  @Input() automaticTimeout = 0; // ms, 0 = no timeout
  
  // Loading cancellation
  @Input() showCancelButton = false;
  @Input() cancelButtonText = 'Cancel';
  
  // Error handling
  @Input() errorTitle = 'Something went wrong';
  @Input() defaultErrorMessage = 'An unexpected error occurred. Please try again.';
  @Input() showRetryButton = true;
  @Input() retryButtonText = 'Try Again';
  @Input() showReportButton = false;
  @Input() reportButtonText = 'Report Issue';
  @Input() showErrorDetails = false;
  
  // Success state
  @Input() showSuccessState = false;
  @Input() successStateDuration = 2000; // ms
  
  // Skeleton loading
  @Input() skeletonItems = signal([
    { width: '100%', height: '24px' },
    { width: '80%', height: '20px' },
    { width: '60%', height: '20px' }
  ]);
  
  // Events
  @Output() cancel = new EventEmitter<void>();
  @Output() retryClicked = new EventEmitter<void>();
  @Output() reportErrorClicked = new EventEmitter<ErrorState>();
  @Output() timeout = new EventEmitter<void>();
  @Output() stateChange = new EventEmitter<LoadingState>();
  
  private destroy$ = new Subject<void>();
  private loadingStartTime = 0;
  private timeoutTimer?: any;
  private successTimer?: any;
  
  // Computed properties
  protected containerClasses = computed(() => {
    const classes = ['loading-state-container'];
    
    if (this.transparentOverlay) classes.push('transparent-overlay');
    if (this.showContentWhileLoading) classes.push('show-content-while-loading');
    
    return classes.join(' ');
  });

  ngOnInit(): void {
    this.setupStateWatching();
  }

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

  /**
   * Sets the loading state with optional configuration
   */
  setLoadingState(config?: Partial<LoadingConfig>): void {
    if (config) {
      this.config.update(current => ({ ...current, ...config }));
    }
    
    this.loadingStartTime = Date.now();
    this.setState('loading');
    
    // Set up automatic timeout if configured
    if (this.automaticTimeout > 0) {
      this.timeoutTimer = setTimeout(() => {
        this.handleTimeout();
      }, this.automaticTimeout);
    }
  }

  /**
   * Sets the error state
   */
  setErrorState(error: ErrorState): void {
    this.error.set(error);
    this.setState('error');
  }

  /**
   * Sets the success state
   */
  setSuccessState(): void {
    this.setState('success');
    
    if (this.showSuccessState && this.successStateDuration > 0) {
      this.successTimer = setTimeout(() => {
        this.setState('idle');
      }, this.successStateDuration);
    }
  }

  /**
   * Sets the idle state
   */
  setIdleState(): void {
    this.setState('idle');
  }

  /**
   * Cancels the current loading operation
   */
  cancelLoading(): void {
    if (this.state !== 'loading') return;
    
    this.cleanup();
    this.setState('idle');
    this.cancel.emit();
  }

  /**
   * Retries the failed operation
   */
  handleRetry(): void {
    if (this.state !== 'error') return;
    
    this.error.set(null);
    this.retryClicked.emit();
  }

  /**
   * Reports the current error
   */
  handleReportError(): void {
    const currentError = this.error();
    if (currentError) {
      this.reportErrorClicked.emit(currentError);
    }
  }

  /**
   * Updates the loading progress (for progress variant)
   */
  updateProgress(progress: number): void {
    this.config.update(config => ({
      ...config,
      progress: Math.max(0, Math.min(100, progress))
    }));
  }

  /**
   * Updates the loading message
   */
  updateMessage(message: string): void {
    this.config.update(config => ({ ...config, message }));
  }

  private setState(newState: LoadingState): void {
    const previousState = this.state;
    this.state = newState;
    
    // Ensure minimum loading time
    if (previousState === 'loading' && newState !== 'loading') {
      const elapsedTime = Date.now() - this.loadingStartTime;
      const remainingTime = this.minimumLoadingTime - elapsedTime;
      
      if (remainingTime > 0) {
        setTimeout(() => {
          this.finalizeStateChange(newState);
        }, remainingTime);
        return;
      }
    }
    
    this.finalizeStateChange(newState);
  }

  private finalizeStateChange(state: LoadingState): void {
    this.state = state;
    this.stateChange.emit(state);
    this.cleanup();
  }

  private handleTimeout(): void {
    this.setState('error');
    this.error.set({
      message: 'The operation timed out. Please try again.',
      code: 'TIMEOUT',
      retryable: true
    });
    this.timeout.emit();
  }

  private setupStateWatching(): void {
    // Watch for external state changes
    timer(0, 100)
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        // This allows for reactive updates if state is changed externally
      });
  }

  private cleanup(): void {
    if (this.timeoutTimer) {
      clearTimeout(this.timeoutTimer);
      this.timeoutTimer = undefined;
    }
    
    if (this.successTimer) {
      clearTimeout(this.successTimer);
      this.successTimer = undefined;
    }
  }
}