import { Maybe } from '@/core';
import { ToastService } from '@/core/services/toast.service';
import { UikitFieldComponent, UikitLabelComponent } from '@/uikit';
import { formatWithCurrency } from '@/utils';
import { CommonModule } from '@angular/common';
import {
  ChangeDetectionStrategy,
  Component,
  ContentChild,
  DestroyRef,
  ElementRef,
  EventEmitter,
  inject,
  Input,
  Output,
  signal,
  TemplateRef,
  ViewChild,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { InputGroup } from 'primeng/inputgroup';
import { InputGroupAddon } from 'primeng/inputgroupaddon';
import { InputNumber, InputNumberModule } from 'primeng/inputnumber';
import { InputText } from 'primeng/inputtext';
import { Select } from 'primeng/select';

type TAmountPercentageInput = 'amount' | 'percentage';
interface IAmountPercentageInputSelect {
  label: string;
  value: TAmountPercentageInput;
}

@Component({
  selector: 'app-amount-percentage-input',
  templateUrl: './amount-percentage-input.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    UikitFieldComponent,
    InputGroup,
    InputGroupAddon,
    ReactiveFormsModule,
    InputText,
    Select,
    InputNumberModule,
    CommonModule,
    UikitLabelComponent,
  ],
})
export class AmountPercentageInputComponent {
  private readonly destroyRef = inject(DestroyRef);
  @Input() defaultType: TAmountPercentageInput = 'percentage';
  @Input({ required: true }) percentageControl!: FormControl<Maybe<any>>;
  @Input({ required: true }) amountControl!: FormControl<Maybe<any>>;
  @Input({ required: true }) name!: string;
  @Input({ required: true }) baseAmount!: number;
  @Input() label: string = '';
  @Input() placeholder?: string;
  @Input() required = false;
  @Input() disabled = false;
  @Input() size?: 'small' | 'large';
  @Input() autocomplete?: string = 'off';
  @Input() showErrors = true;
  @Input() hint?: string;
  @Input() isLtrInput = false;
  @Input() minAmount?: number = 0;
  @Input() maxAmount?: number;
  @Input() minPercentage?: number = 0;
  @Input() maxPercentage?: number = 100;
  @Output() valueChange = new EventEmitter<string | number>();
  @Output() onTypeChange = new EventEmitter<TAmountPercentageInput>();
  @Output() blur = new EventEmitter<void>();

  @ContentChild('labelSuffix', { static: false }) labelSuffix!: TemplateRef<any> | null;
  @ViewChild('amountInput') amountInput?: InputNumber;
  @ViewChild('percentageInput') percentageInput?: ElementRef<HTMLInputElement>;

  private readonly toastService = inject(ToastService);

  readonly typeSelectOptions: IAmountPercentageInputSelect[] = [
    {
      label: 'قیمت',
      value: 'amount',
    },
    {
      label: 'درصد',
      value: 'percentage',
    },
  ];

  selectedType = new FormControl({
    value: this.defaultType,
    disabled: false,
  });

  preparedLabel = signal(this.label);
  private lastValidAmount = 0;
  private lastValidPercentage = 0;

  private parseValue(value: Maybe<any>) {
    const normalized = `${value ?? ''}`.replace(/,/g, '');
    const parsed = parseFloat(normalized);
    return Number.isFinite(parsed) ? parsed : 0;
  }

  private getRangeLabel(value: number, isPercentageType: boolean) {
    return isPercentageType ? `${value}%` : formatWithCurrency(value);
  }

  private isInRange(value: number, min = 0, max = Number.MAX_SAFE_INTEGER) {
    return value >= min && value <= max;
  }

  private showOutOfRangeToast(
    value: number,
    isPercentageType: boolean,
    min = 0,
    max = Number.MAX_SAFE_INTEGER,
  ) {
    if (value < min) {
      this.toastService.warn({
        text: `مقدار ${this.label} باید بیشتر از ${this.getRangeLabel(min, isPercentageType)} باشد.`,
      });
    }
    if (value > max) {
      this.toastService.warn({
        text: `مقدار ${this.label} باید کمتر از ${this.getRangeLabel(max, isPercentageType)} باشد.`,
      });
    }
  }

  private resetAmountInputView(value: number) {
    const inputEl = this.amountInput?.input?.nativeElement;
    if (!inputEl) return;
    inputEl.value = value ? value.toLocaleString('en-US') : '0';
  }

  private resetPercentageInputView(value: number) {
    const inputEl = this.percentageInput?.nativeElement;
    if (!inputEl) return;
    inputEl.value = `${value}`;
  }

  private getAmountRange() {
    const min = this.minAmount ?? 0;
    const maxFromInput = this.maxAmount;
    const max = maxFromInput ?? (this.baseAmount > 0 ? this.baseAmount : Number.MAX_SAFE_INTEGER);
    return { min, max };
  }

  private getPercentageRange() {
    const min = this.minPercentage ?? 0;
    const max = this.maxPercentage ?? 100;
    return { min, max };
  }

  private syncFromPercentage(rawValue: Maybe<any>) {
    const { min, max } = this.getPercentageRange();
    const percentageValue = this.parseValue(rawValue);
    if (!this.isInRange(percentageValue, min, max)) {
      this.showOutOfRangeToast(percentageValue, true, min, max);
      this.percentageControl.setValue(this.lastValidPercentage);
      this.resetPercentageInputView(this.lastValidPercentage);
      return;
    }

    this.lastValidPercentage = percentageValue;
    const amountValue = (this.baseAmount * percentageValue) / 100;
    this.lastValidAmount = Number.isFinite(amountValue) ? amountValue : 0;

    this.percentageControl.setValue(this.lastValidPercentage, { emitEvent: false });
    this.amountControl.setValue(this.lastValidAmount, { emitEvent: false });
    this.preparedLabel.set(this.prepareLabel(true));
  }

  private syncFromAmount(rawValue: Maybe<any>) {
    const { min, max } = this.getAmountRange();
    const amountValue = this.parseValue(rawValue);
    if (!this.isInRange(amountValue, min, max)) {
      this.showOutOfRangeToast(amountValue, false, min, max);
      this.amountControl.setValue(this.lastValidAmount);
      this.resetAmountInputView(this.lastValidAmount);
      return;
    }

    this.lastValidAmount = amountValue;
    const percentageValue = this.baseAmount
      ? ((amountValue / this.baseAmount) * 100).toFixed(2)
      : '0';
    this.lastValidPercentage = this.parseValue(percentageValue);

    this.amountControl.setValue(this.lastValidAmount, { emitEvent: false });
    this.percentageControl.setValue(percentageValue, { emitEvent: false });
    this.preparedLabel.set(this.prepareLabel(false));
  }

  private prepareLabel(isPercentageType: boolean) {
    if (isPercentageType) {
      return `${this.label} (معادل ${formatWithCurrency(this.amountControl.value || 0)})`;
    }
    return `${this.label} (معادل ${this.percentageControl.value || 0} درصد)`;
  }

  ngOnInit() {
    const isPercentageType = this.amountControl.value ? false : true;

    this.selectedType.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
      this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
    });

    this.percentageControl.valueChanges
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe((value) => {
        if (this.selectedType.value === 'percentage') {
          this.syncFromPercentage(value);
        }
      });

    this.amountControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
      if (this.selectedType.value === 'amount') {
        this.syncFromAmount(value);
      }
    });

    this.lastValidAmount = this.parseValue(this.amountControl.value);
    this.lastValidPercentage = this.parseValue(this.percentageControl.value);
    if (isPercentageType) {
      this.syncFromPercentage(this.percentageControl.value);
    } else {
      this.syncFromAmount(this.amountControl.value);
    }
    this.selectedType.setValue(isPercentageType ? 'percentage' : 'amount');
  }

  ngOnChanges() {
    const isPercentageType = this.amountControl.value ? false : true;
    if (isPercentageType) {
      this.syncFromPercentage(this.percentageControl.value);
    } else {
      this.syncFromAmount(this.amountControl.value);
    }

    this.preparedLabel.set(this.prepareLabel(isPercentageType));
  }
}
