import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, forwardRef, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import { faClock, faTimes, faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';

export type TimePickerSize = 'sm' | 'md' | 'lg';
export type TimePickerVariant = 'outlined' | 'filled' | 'underlined';
export type TimePickerState = 'default' | 'error' | 'success' | 'warning';
export type TimePickerFormat = '12' | '24';

export interface TimeValue {
  hours: number;
  minutes: number;
  seconds?: number;
}

@Component({
  selector: 'ui-time-picker',
  standalone: true,
  imports: [CommonModule, FontAwesomeModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TimePickerComponent),
      multi: true
    }
  ],
  template: `
    <div class="ui-time-picker" [class]="getWrapperClasses()">
      <!-- Label -->
      @if (label) {
        <label 
          class="ui-time-picker__label"
          [for]="inputId"
          [class.ui-time-picker__label--required]="required"
        >
          {{ label }}
          @if (required) {
            <span class="ui-time-picker__required-indicator">*</span>
          }
        </label>
      }
      
      <!-- Input Container -->
      <div class="ui-time-picker__container" [class]="getContainerClasses()">
        <!-- Clock Icon -->
        <div class="ui-time-picker__prefix-icon">
          <fa-icon [icon]="faClock"></fa-icon>
        </div>
        
        <!-- Input Field -->
        <input
          [id]="inputId"
          class="ui-time-picker__field"
          type="text"
          [placeholder]="placeholder"
          [disabled]="disabled"
          [readonly]="true"
          [required]="required"
          [value]="formattedValue"
          [attr.aria-expanded]="isOpen()"
          [attr.aria-haspopup]="'dialog'"
          [attr.aria-describedby]="helperText ? inputId + '-helper' : null"
          (click)="toggleTimePicker()"
          (keydown)="onInputKeyDown($event)"
        />
        
        <!-- Clear Button -->
        @if (clearable && value && !disabled) {
          <button
            type="button"
            class="ui-time-picker__clear-btn"
            (click)="clearValue()"
            [attr.aria-label]="'Clear ' + (label || 'time')"
          >
            <fa-icon [icon]="faTimes"></fa-icon>
          </button>
        }
      </div>
      
      <!-- Time Picker Dropdown -->
      @if (isOpen()) {
        <div class="ui-time-picker__dropdown" role="dialog" aria-label="Time picker">
          <div class="ui-time-picker__time-display">
            <div class="ui-time-picker__time-sections">
              <!-- Hours -->
              <div class="ui-time-picker__time-section">
                <button
                  type="button"
                  class="ui-time-picker__time-btn ui-time-picker__time-btn--up"
                  (click)="incrementHours()"
                  [attr.aria-label]="'Increase hours'"
                >
                  <fa-icon [icon]="faChevronUp"></fa-icon>
                </button>
                
                <input
                  type="number"
                  class="ui-time-picker__time-input"
                  [value]="displayHours"
                  [min]="timeFormat === '24' ? 0 : 1"
                  [max]="timeFormat === '24' ? 23 : 12"
                  (input)="onHoursInput($event)"
                  (blur)="validateHours()"
                />
                
                <button
                  type="button"
                  class="ui-time-picker__time-btn ui-time-picker__time-btn--down"
                  (click)="decrementHours()"
                  [attr.aria-label]="'Decrease hours'"
                >
                  <fa-icon [icon]="faChevronDown"></fa-icon>
                </button>
                
                <div class="ui-time-picker__time-label">Hours</div>
              </div>
              
              <!-- Separator -->
              <div class="ui-time-picker__separator">:</div>
              
              <!-- Minutes -->
              <div class="ui-time-picker__time-section">
                <button
                  type="button"
                  class="ui-time-picker__time-btn ui-time-picker__time-btn--up"
                  (click)="incrementMinutes()"
                  [attr.aria-label]="'Increase minutes'"
                >
                  <fa-icon [icon]="faChevronUp"></fa-icon>
                </button>
                
                <input
                  type="number"
                  class="ui-time-picker__time-input"
                  [value]="displayMinutes"
                  min="0"
                  max="59"
                  (input)="onMinutesInput($event)"
                  (blur)="validateMinutes()"
                />
                
                <button
                  type="button"
                  class="ui-time-picker__time-btn ui-time-picker__time-btn--down"
                  (click)="decrementMinutes()"
                  [attr.aria-label]="'Decrease minutes'"
                >
                  <fa-icon [icon]="faChevronDown"></fa-icon>
                </button>
                
                <div class="ui-time-picker__time-label">Minutes</div>
              </div>
              
              <!-- Seconds (if enabled) -->
              @if (showSeconds) {
                <div class="ui-time-picker__separator">:</div>
                
                <div class="ui-time-picker__time-section">
                  <button
                    type="button"
                    class="ui-time-picker__time-btn ui-time-picker__time-btn--up"
                    (click)="incrementSeconds()"
                    [attr.aria-label]="'Increase seconds'"
                  >
                    <fa-icon [icon]="faChevronUp"></fa-icon>
                  </button>
                  
                  <input
                    type="number"
                    class="ui-time-picker__time-input"
                    [value]="displaySeconds"
                    min="0"
                    max="59"
                    (input)="onSecondsInput($event)"
                    (blur)="validateSeconds()"
                  />
                  
                  <button
                    type="button"
                    class="ui-time-picker__time-btn ui-time-picker__time-btn--down"
                    (click)="decrementSeconds()"
                    [attr.aria-label]="'Decrease seconds'"
                  >
                    <fa-icon [icon]="faChevronDown"></fa-icon>
                  </button>
                  
                  <div class="ui-time-picker__time-label">Seconds</div>
                </div>
              }
              
              <!-- AM/PM (if 12-hour format) -->
              @if (timeFormat === '12') {
                <div class="ui-time-picker__ampm-section">
                  <button
                    type="button"
                    class="ui-time-picker__ampm-btn"
                    [class.ui-time-picker__ampm-btn--active]="ampm() === 'AM'"
                    (click)="setAMPM('AM')"
                  >
                    AM
                  </button>
                  
                  <button
                    type="button"
                    class="ui-time-picker__ampm-btn"
                    [class.ui-time-picker__ampm-btn--active]="ampm() === 'PM'"
                    (click)="setAMPM('PM')"
                  >
                    PM
                  </button>
                </div>
              }
            </div>
          </div>
          
          <!-- Preset Times (if enabled) -->
          @if (presetTimes.length > 0) {
            <div class="ui-time-picker__presets">
              <div class="ui-time-picker__presets-title">Quick Select</div>
              <div class="ui-time-picker__presets-list">
                @for (preset of presetTimes; track preset.label) {
                  <button
                    type="button"
                    class="ui-time-picker__preset-btn"
                    (click)="selectPreset(preset.value)"
                  >
                    {{ preset.label }}
                  </button>
                }
              </div>
            </div>
          }
          
          <!-- Actions -->
          <div class="ui-time-picker__actions">
            <button
              type="button"
              class="ui-time-picker__action-btn ui-time-picker__action-btn--secondary"
              (click)="closeTimePicker()"
            >
              Cancel
            </button>
            
            <button
              type="button"
              class="ui-time-picker__action-btn ui-time-picker__action-btn--primary"
              (click)="confirmTime()"
            >
              OK
            </button>
          </div>
        </div>
      }
      
      <!-- Helper Text / Error Message -->
      @if (helperText || errorMessage) {
        <div 
          class="ui-time-picker__helper-text" 
          [class]="getHelperTextClasses()"
          [id]="inputId + '-helper'"
        >
          {{ state === 'error' ? errorMessage : helperText }}
        </div>
      }
    </div>
  `,
  styleUrl: './time-picker.component.scss'
})
export class TimePickerComponent implements ControlValueAccessor {
  @Input() label: string = '';
  @Input() placeholder: string = 'Select time';
  @Input() size: TimePickerSize = 'md';
  @Input() variant: TimePickerVariant = 'outlined';
  @Input() state: TimePickerState = 'default';
  @Input() disabled = false;
  @Input() required = false;
  @Input() clearable = true;
  @Input() helperText: string = '';
  @Input() errorMessage: string = '';
  @Input() timeFormat: TimePickerFormat = '12';
  @Input() showSeconds = false;
  @Input() minuteStep = 1;
  @Input() secondStep = 1;
  @Input() presetTimes: Array<{label: string, value: TimeValue}> = [];
  @Input() inputId: string = `time-picker-${Math.random().toString(36).substr(2, 9)}`;

  @Output() timeChange = new EventEmitter<TimeValue | null>();
  @Output() pickerOpen = new EventEmitter<void>();
  @Output() pickerClose = new EventEmitter<void>();

  value: TimeValue | null = null;
  isOpen = signal(false);
  ampm = signal<'AM' | 'PM'>('AM');

  // Internal state for the picker
  private tempValue: TimeValue = { hours: 12, minutes: 0, seconds: 0 };

  readonly faClock = faClock;
  readonly faTimes = faTimes;
  readonly faChevronUp = faChevronUp;
  readonly faChevronDown = faChevronDown;

  private onChange = (value: TimeValue | null) => {};
  private onTouched = () => {};

  writeValue(value: TimeValue | null): void {
    this.value = value;
    if (value) {
      this.tempValue = { ...value };
      if (this.timeFormat === '12') {
        this.updateAMPM();
      }
    }
  }

  registerOnChange(fn: (value: TimeValue | null) => void): void {
    this.onChange = fn;
  }

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

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

  get formattedValue(): string {
    if (!this.value) return '';
    return this.formatTime(this.value);
  }

  get displayHours(): string {
    if (this.timeFormat === '12') {
      const hours = this.tempValue.hours;
      const displayHour = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
      return displayHour.toString().padStart(2, '0');
    }
    return this.tempValue.hours.toString().padStart(2, '0');
  }

  get displayMinutes(): string {
    return this.tempValue.minutes.toString().padStart(2, '0');
  }

  get displaySeconds(): string {
    return (this.tempValue.seconds || 0).toString().padStart(2, '0');
  }

  getWrapperClasses(): string {
    const classes = [
      `ui-time-picker--${this.size}`,
      `ui-time-picker--${this.variant}`,
      `ui-time-picker--${this.state}`
    ];

    if (this.disabled) classes.push('ui-time-picker--disabled');
    if (this.isOpen()) classes.push('ui-time-picker--open');

    return classes.join(' ');
  }

  getContainerClasses(): string {
    const classes: string[] = [];
    if (this.clearable && this.value) classes.push('ui-time-picker__container--has-clear');
    return classes.join(' ');
  }

  getHelperTextClasses(): string {
    return `ui-time-picker__helper-text--${this.state}`;
  }

  toggleTimePicker(): void {
    if (this.disabled) return;
    
    if (this.isOpen()) {
      this.closeTimePicker();
    } else {
      this.openTimePicker();
    }
  }

  openTimePicker(): void {
    this.isOpen.set(true);
    // Initialize temp value with current value or default
    if (this.value) {
      this.tempValue = { ...this.value };
    } else {
      const now = new Date();
      this.tempValue = {
        hours: now.getHours(),
        minutes: now.getMinutes(),
        seconds: this.showSeconds ? now.getSeconds() : 0
      };
    }
    
    if (this.timeFormat === '12') {
      this.updateAMPM();
    }
    
    this.pickerOpen.emit();
  }

  closeTimePicker(): void {
    this.isOpen.set(false);
    this.onTouched();
    this.pickerClose.emit();
  }

  confirmTime(): void {
    this.value = { ...this.tempValue };
    this.onChange(this.value);
    this.timeChange.emit(this.value);
    this.closeTimePicker();
  }

  clearValue(): void {
    this.value = null;
    this.onChange(null);
    this.timeChange.emit(null);
  }

  incrementHours(): void {
    const maxHours = this.timeFormat === '24' ? 23 : 12;
    const minHours = this.timeFormat === '24' ? 0 : 1;
    
    if (this.tempValue.hours >= maxHours) {
      this.tempValue.hours = minHours;
    } else {
      this.tempValue.hours++;
    }
    
    if (this.timeFormat === '12') {
      this.updateAMPM();
    }
  }

  decrementHours(): void {
    const maxHours = this.timeFormat === '24' ? 23 : 12;
    const minHours = this.timeFormat === '24' ? 0 : 1;
    
    if (this.tempValue.hours <= minHours) {
      this.tempValue.hours = maxHours;
    } else {
      this.tempValue.hours--;
    }
    
    if (this.timeFormat === '12') {
      this.updateAMPM();
    }
  }

  incrementMinutes(): void {
    if (this.tempValue.minutes >= 59) {
      this.tempValue.minutes = 0;
    } else {
      this.tempValue.minutes = Math.min(59, this.tempValue.minutes + this.minuteStep);
    }
  }

  decrementMinutes(): void {
    if (this.tempValue.minutes <= 0) {
      this.tempValue.minutes = 59;
    } else {
      this.tempValue.minutes = Math.max(0, this.tempValue.minutes - this.minuteStep);
    }
  }

  incrementSeconds(): void {
    if (!this.showSeconds) return;
    
    if ((this.tempValue.seconds || 0) >= 59) {
      this.tempValue.seconds = 0;
    } else {
      this.tempValue.seconds = Math.min(59, (this.tempValue.seconds || 0) + this.secondStep);
    }
  }

  decrementSeconds(): void {
    if (!this.showSeconds) return;
    
    if ((this.tempValue.seconds || 0) <= 0) {
      this.tempValue.seconds = 59;
    } else {
      this.tempValue.seconds = Math.max(0, (this.tempValue.seconds || 0) - this.secondStep);
    }
  }

  onHoursInput(event: Event): void {
    const target = event.target as HTMLInputElement;
    const hours = parseInt(target.value, 10);
    
    if (!isNaN(hours)) {
      this.tempValue.hours = hours;
    }
  }

  onMinutesInput(event: Event): void {
    const target = event.target as HTMLInputElement;
    const minutes = parseInt(target.value, 10);
    
    if (!isNaN(minutes)) {
      this.tempValue.minutes = minutes;
    }
  }

  onSecondsInput(event: Event): void {
    if (!this.showSeconds) return;
    
    const target = event.target as HTMLInputElement;
    const seconds = parseInt(target.value, 10);
    
    if (!isNaN(seconds)) {
      this.tempValue.seconds = seconds;
    }
  }

  validateHours(): void {
    const maxHours = this.timeFormat === '24' ? 23 : 12;
    const minHours = this.timeFormat === '24' ? 0 : 1;
    
    this.tempValue.hours = Math.max(minHours, Math.min(maxHours, this.tempValue.hours));
  }

  validateMinutes(): void {
    this.tempValue.minutes = Math.max(0, Math.min(59, this.tempValue.minutes));
  }

  validateSeconds(): void {
    if (!this.showSeconds) return;
    this.tempValue.seconds = Math.max(0, Math.min(59, this.tempValue.seconds || 0));
  }

  setAMPM(period: 'AM' | 'PM'): void {
    if (this.timeFormat !== '12') return;
    
    this.ampm.set(period);
    
    // Convert temp hours to 24-hour format for internal storage
    const currentHours = this.tempValue.hours;
    
    if (period === 'AM') {
      if (currentHours === 12) {
        this.tempValue.hours = 0;
      } else if (currentHours > 12) {
        this.tempValue.hours = currentHours - 12;
      }
    } else {
      if (currentHours < 12) {
        this.tempValue.hours = currentHours + 12;
      }
    }
  }

  selectPreset(timeValue: TimeValue): void {
    this.tempValue = { ...timeValue };
    if (this.timeFormat === '12') {
      this.updateAMPM();
    }
    this.confirmTime();
  }

  onInputKeyDown(event: KeyboardEvent): void {
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault();
      this.toggleTimePicker();
    } else if (event.key === 'Escape' && this.isOpen()) {
      event.preventDefault();
      this.closeTimePicker();
    }
  }

  private formatTime(time: TimeValue): string {
    if (this.timeFormat === '12') {
      const hours = time.hours;
      const displayHour = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
      const period = hours >= 12 ? 'PM' : 'AM';
      const minutes = time.minutes.toString().padStart(2, '0');
      
      if (this.showSeconds && time.seconds !== undefined) {
        const seconds = time.seconds.toString().padStart(2, '0');
        return `${displayHour}:${minutes}:${seconds} ${period}`;
      }
      
      return `${displayHour}:${minutes} ${period}`;
    } else {
      const hours = time.hours.toString().padStart(2, '0');
      const minutes = time.minutes.toString().padStart(2, '0');
      
      if (this.showSeconds && time.seconds !== undefined) {
        const seconds = time.seconds.toString().padStart(2, '0');
        return `${hours}:${minutes}:${seconds}`;
      }
      
      return `${hours}:${minutes}`;
    }
  }

  private updateAMPM(): void {
    if (this.timeFormat !== '12') return;
    
    const period = this.tempValue.hours >= 12 ? 'PM' : 'AM';
    this.ampm.set(period);
  }
}