import { CommonModule } from '@angular/common';
import {
  AfterViewInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ContentChildren,
  ElementRef,
  EventEmitter,
  Input,
  OnDestroy,
  Output,
  QueryList,
  ViewChild
} from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
import { Utils } from '../../utils/utils.util';
import { SegmentedControlButtonComponent } from '../segmented-control-button/segmented-control-button.component';
import { SegmentedControlSize } from './segmented-control.model';

@Component({
  selector: 'nj-segmented-control',
  templateUrl: './segmented-control.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  imports: [SegmentedControlButtonComponent, CommonModule]
})
export class SegmentedControlComponent implements ControlValueAccessor, AfterViewInit, OnDestroy {
  private readonly segmentedControlClass = 'nj-segmented-control';

  /**
   * @ignore
   */
  private unsubscribe: Subject<void> = new Subject<void>();

  /**
   * @ignore
   */
  private _onChange = (_: any): void => {};

  /**
   * @ignore
   */
  private _onTouched = (): void => {};

  /**
   * @ignore
   */
  private _isDisabled: boolean;

  /**
   * @ignore
   */
  private _value: string;

  /**
   * @ignore
   */
  public segmentedButtons: SegmentedControlButtonComponent[];

  /**
   * Selected value
   */
  @Input()
  set value(value: string) {
    this._value = value;
    this.setActive(value);
  }

  get value(): string {
    return this._value;
  }

  /**
   * Selected aria label
   */
  @Input() ariaLabel: string;

  /**
   * Overrides disabled state on all buttons
   */
  @Input()
  set isDisabled(value: boolean) {
    this._isDisabled = value;
    this.setButtonsIsDisabled();
  }

  get isDisabled(): boolean {
    return this._isDisabled;
  }

  /**
   * Segmented Control size
   */
  @Input() size: SegmentedControlSize = 'md';

  /**
   * @ignore
   */
  @ContentChildren(SegmentedControlButtonComponent) segmentedButtonsList: QueryList<SegmentedControlButtonComponent>;

  /**
   * @ignore
   */
  @ViewChild('segmentedControl') segmentedControl: ElementRef<HTMLDivElement>;

  /**
   * Output that emits value change
   */
  @Output() valueChange: EventEmitter<string> = new EventEmitter<string>();

  constructor(private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    // A render is being done after view init so setTimeout allows us to delay any attribute modification
    // in the call stack, so it can be taken account on next render only
    setTimeout(() => {
      this.segmentedButtons = this.segmentedButtonsList?.toArray();
      this.setButtonsIsDisabled();
      this.initializeClickListener();
      if (this.value) {
        this.setActive(this.value);
      }
    });
  }

  private setButtonsIsDisabled() {
    if (!Utils.isUndefinedOrNull(this.isDisabled) && this.segmentedButtons) {
      for (const segmentedButton of this.segmentedButtons) {
        segmentedButton.setIsDisabled(this.isDisabled);
      }
    }
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

  /**
   * @ignore
   */
  private initializeClickListener() {
    if (Utils.isUndefinedOrNull(this.segmentedButtons)) {
      return;
    }
    this.segmentedButtons.forEach((button, value) => {
      button.buttonClick.pipe(takeUntil(this.unsubscribe)).subscribe((_) => {
        button.isSelected = true;
        this.writeValue(button.value);
      });
    });
  }

  private setActive(buttonValue: string) {
    if (!this.segmentedButtons) {
      return;
    }
    for (const button of this.segmentedButtons) {
      if (button?.value === buttonValue) {
        button.setIsSelected(true);
        const selectedBoundingRect = button.getClientBoundingRect();
        const segmentedControlBoundingRect = this.segmentedControl.nativeElement.getBoundingClientRect();
        const segmentedControlStyle = this.segmentedControl?.nativeElement?.style;
        segmentedControlStyle.setProperty('--nj-sc-btn-selected-width', `${selectedBoundingRect.width}px`);
        segmentedControlStyle.setProperty(
          '--nj-sc-btn-selected-right',
          `${segmentedControlBoundingRect.right - selectedBoundingRect.right}px`
        );
      } else {
        button.setIsSelected(false);
      }
    }
  }

  /**
   * @ignore
   */
  getSizeClass(): string {
    if (!this.size || this.size === 'md') {
      return;
    }
    return `${this.segmentedControlClass}--${this.size}`;
  }

  /**
   * @ignore
   */
  writeValue(value: any): void {
    if (value === this.value) {
      return;
    }
    this.setActive(value);
    this.value = value;
  }

  /**
   * @ignore
   */
  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  /**
   * @ignore
   */
  registerOnTouched(fn: any): void {
    this._onTouched = fn;
  }

  /**
   * @ignore
   */
  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
    this.cdr.markForCheck();
  }
}
