import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, forwardRef, signal, computed, ViewChild, ElementRef, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

export type RangeSliderSize = 'sm' | 'md' | 'lg';
export type RangeSliderVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'danger';
export type RangeSliderOrientation = 'horizontal' | 'vertical';

export interface RangeSliderTickMark {
  value: number;
  label?: string;
  major?: boolean;
}

@Component({
  selector: 'ui-range-slider',
  standalone: true,
  imports: [CommonModule, FormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RangeSliderComponent),
      multi: true
    }
  ],
  template: `
    <div 
      class="ui-range-slider"
      [class]="containerClasses()"
      [attr.aria-label]="ariaLabel || label || 'Range slider'"
      [attr.aria-disabled]="disabled">
      
      @if (label || showValue) {
        <div class="ui-range-slider__header">
          @if (label) {
            <label 
              class="ui-range-slider__label"
              [class.ui-range-slider__label--disabled]="disabled"
              [for]="sliderId">
              {{ label }}
              @if (required) {
                <span class="ui-range-slider__required">*</span>
              }
            </label>
          }
          
          @if (showValue) {
            <span class="ui-range-slider__value-display">
              {{ formatValue(currentValue()) }}{{ valueUnit }}
            </span>
          }
        </div>
      }
      
      <div class="ui-range-slider__container" #container>
        <!-- Track background -->
        <div class="ui-range-slider__track">
          <!-- Fill/Progress -->
          <div 
            class="ui-range-slider__fill"
            [style.width]="fillWidth() + '%'"
            [style.left]="'0%'">
          </div>
        </div>
        
        <!-- Hidden native input for accessibility and form integration -->
        <input
          #sliderInput
          type="range"
          class="ui-range-slider__input"
          [id]="sliderId"
          [min]="min"
          [max]="max"
          [step]="step"
          [value]="currentValue()"
          [disabled]="disabled"
          [required]="required"
          [attr.aria-label]="ariaLabel || label || 'Range slider'"
          [attr.aria-valuetext]="ariaValueText"
          [attr.aria-orientation]="orientation"
          (input)="onInputChange($event)"
          (focus)="onFocus($event)"
          (blur)="onBlur($event)"
          (keydown)="onKeyDown($event)" />
        
        <!-- Visual thumb -->
        <div 
          class="ui-range-slider__thumb"
          [class.ui-range-slider__thumb--focused]="isFocused()"
          [class.ui-range-slider__thumb--dragging]="isDragging()"
          [style.left]="thumbPosition() + '%'"
          (mousedown)="onThumbMouseDown($event)"
          (touchstart)="onThumbTouchStart($event)">
        </div>
      </div>
      
      @if (ticks && ticks.length > 0) {
        <!-- Tick marks -->
        <div class="ui-range-slider__ticks">
          @for (tick of ticks; track tick.value) {
            <div 
              class="ui-range-slider__tick"
              [class.ui-range-slider__tick--major]="tick.major"
              [style.left]="getTickPosition(tick.value) + '%'">
            </div>
          }
        </div>
        
        <!-- Tick labels -->
        @if (showTickLabels) {
          <div class="ui-range-slider__tick-labels">
            @for (tick of ticks; track tick.value) {
              @if (tick.label) {
                <div 
                  class="ui-range-slider__tick-label"
                  [style.left]="getTickPosition(tick.value) + '%'">
                  {{ tick.label }}
                </div>
              }
            }
          </div>
        }
      }
      
      @if (helperText && !disabled) {
        <div 
          class="ui-range-slider__helper-text"
          [class.ui-range-slider__helper-text--error]="hasError"
          [id]="sliderId + '-helper'">
          {{ helperText }}
        </div>
      }
    </div>
  `,
  styleUrl: './range-slider.component.scss'
})
export class RangeSliderComponent implements ControlValueAccessor {
  @ViewChild('sliderInput', { static: true }) sliderInput!: ElementRef<HTMLInputElement>;
  @ViewChild('container', { static: true }) container!: ElementRef<HTMLDivElement>;

  // Core inputs
  @Input() min = 0;
  @Input() max = 100;
  @Input() step = 1;
  @Input() set value(val: number) {
    if (val !== null && val !== undefined) {
      this._value.set(this.normalizeValue(val));
    }
  }
  get value(): number {
    return this.currentValue();
  }
  @Input() size: RangeSliderSize = 'md';
  @Input() variant: RangeSliderVariant = 'primary';
  @Input() orientation: RangeSliderOrientation = 'horizontal';
  
  // Behavior inputs
  @Input() disabled = false;
  @Input() required = false;
  @Input() readonly = false;
  
  // Display options
  @Input() label = '';
  @Input() showValue = false;
  @Input() valueUnit = '';
  @Input() showTickLabels = false;
  @Input() ticks: RangeSliderTickMark[] = [];
  
  // Validation and help
  @Input() helperText = '';
  @Input() hasError = false;
  @Input() ariaLabel = '';
  @Input() ariaValueText = '';
  @Input() sliderId = `range-slider-${Math.random().toString(36).substr(2, 9)}`;

  // Outputs
  @Output() valueChange = new EventEmitter<number>();
  @Output() sliderFocus = new EventEmitter<FocusEvent>();
  @Output() sliderBlur = new EventEmitter<FocusEvent>();
  @Output() slideStart = new EventEmitter<number>();
  @Output() slideEnd = new EventEmitter<number>();

  // Internal state
  private _value = signal(0);
  currentValue = this._value.asReadonly();
  
  private _isFocused = signal(false);
  isFocused = this._isFocused.asReadonly();
  
  private _isDragging = signal(false);
  isDragging = this._isDragging.asReadonly();

  // Computed properties
  fillWidth = computed(() => {
    const range = this.max - this.min;
    const progress = (this.currentValue() - this.min) / range;
    return Math.max(0, Math.min(100, progress * 100));
  });

  thumbPosition = computed(() => {
    return this.fillWidth();
  });

  containerClasses = computed(() => {
    const classes = [
      `ui-range-slider--${this.size}`,
      `ui-range-slider--${this.variant}`,
      `ui-range-slider--${this.orientation}`
    ];

    if (this.disabled) classes.push('ui-range-slider--disabled');
    if (this.hasError) classes.push('ui-range-slider--error');
    if (this.readonly) classes.push('ui-range-slider--readonly');

    return classes.join(' ');
  });

  // ControlValueAccessor implementation
  private onChange = (value: number) => {};
  private onTouched = () => {};

  writeValue(value: number): void {
    if (value !== null && value !== undefined) {
      this._value.set(this.normalizeValue(value));
    }
  }

  registerOnChange(fn: (value: number) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  // Event handlers
  onInputChange(event: Event): void {
    const target = event.target as HTMLInputElement;
    const newValue = this.normalizeValue(parseFloat(target.value));
    this.updateValue(newValue);
  }

  onFocus(event: FocusEvent): void {
    this._isFocused.set(true);
    this.sliderFocus.emit(event);
  }

  onBlur(event: FocusEvent): void {
    this._isFocused.set(false);
    this.onTouched();
    this.sliderBlur.emit(event);
  }

  onKeyDown(event: KeyboardEvent): void {
    if (this.disabled || this.readonly) return;

    let delta = 0;
    const largeStep = (this.max - this.min) * 0.1;

    switch (event.key) {
      case 'ArrowRight':
      case 'ArrowUp':
        delta = this.step;
        break;
      case 'ArrowLeft':
      case 'ArrowDown':
        delta = -this.step;
        break;
      case 'PageUp':
        delta = largeStep;
        break;
      case 'PageDown':
        delta = -largeStep;
        break;
      case 'Home':
        this.updateValue(this.min);
        event.preventDefault();
        return;
      case 'End':
        this.updateValue(this.max);
        event.preventDefault();
        return;
      default:
        return;
    }

    event.preventDefault();
    const newValue = this.normalizeValue(this.currentValue() + delta);
    this.updateValue(newValue);
  }

  onThumbMouseDown(event: MouseEvent): void {
    if (this.disabled || this.readonly) return;
    
    event.preventDefault();
    this._isDragging.set(true);
    this.slideStart.emit(this.currentValue());
    
    const handleMouseMove = (e: MouseEvent) => {
      this.updateValueFromPosition(e.clientX);
    };
    
    const handleMouseUp = () => {
      this._isDragging.set(false);
      this.slideEnd.emit(this.currentValue());
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
    
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
  }

  onThumbTouchStart(event: TouchEvent): void {
    if (this.disabled || this.readonly) return;
    
    event.preventDefault();
    this._isDragging.set(true);
    this.slideStart.emit(this.currentValue());
    
    const handleTouchMove = (e: TouchEvent) => {
      if (e.touches.length > 0) {
        this.updateValueFromPosition(e.touches[0].clientX);
      }
    };
    
    const handleTouchEnd = () => {
      this._isDragging.set(false);
      this.slideEnd.emit(this.currentValue());
      document.removeEventListener('touchmove', handleTouchMove);
      document.removeEventListener('touchend', handleTouchEnd);
    };
    
    document.addEventListener('touchmove', handleTouchMove, { passive: false });
    document.addEventListener('touchend', handleTouchEnd);
  }

  // Utility methods
  private updateValue(newValue: number): void {
    const normalizedValue = this.normalizeValue(newValue);
    if (normalizedValue !== this.currentValue()) {
      this._value.set(normalizedValue);
      this.onChange(normalizedValue);
      this.valueChange.emit(normalizedValue);
      
      // Sync with native input
      if (this.sliderInput) {
        this.sliderInput.nativeElement.value = normalizedValue.toString();
      }
    }
  }

  private updateValueFromPosition(clientX: number): void {
    if (!this.container) return;
    
    const rect = this.container.nativeElement.getBoundingClientRect();
    const percentage = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
    const newValue = this.min + (percentage * (this.max - this.min));
    this.updateValue(newValue);
  }

  private normalizeValue(value: number): number {
    // Round to nearest step
    const steppedValue = Math.round((value - this.min) / this.step) * this.step + this.min;
    // Clamp to min/max bounds
    return Math.max(this.min, Math.min(this.max, steppedValue));
  }

  formatValue(value: number): string {
    // Format the value for display (could be overridden for custom formatting)
    return value.toString();
  }

  getTickPosition(tickValue: number): number {
    const range = this.max - this.min;
    return ((tickValue - this.min) / range) * 100;
  }

  // Public API methods
  setValue(value: number): void {
    this.updateValue(value);
  }

  getValue(): number {
    return this.currentValue();
  }

  focus(): void {
    if (this.sliderInput) {
      this.sliderInput.nativeElement.focus();
    }
  }
}