/** * AmountInput.vue - Specialized amount input with increment/decrement
controls * * Extracted from GiftedDialog.vue to handle numeric amount input *
with increment/decrement buttons and validation. * * @author Matthew Raymer */
<template>
  <div class="flex flex-grow">
    <button
      class="rounded-s border border-e-0 border-slate-400 bg-slate-200 px-4 py-2"
      :disabled="isAtMinimum"
      type="button"
      @click.prevent="decrement"
    >
      <font-awesome icon="chevron-left" />
    </button>

    <input
      :id="inputId"
      v-model="displayValue"
      type="number"
      :min="min"
      :max="max"
      :step="step"
      class="flex-1 border border-e-0 border-slate-400 px-2 py-2 text-center w-[1px]"
      @input="handleInput"
      @blur="handleBlur"
    />

    <button
      class="rounded-e border border-slate-400 bg-slate-200 px-4 py-2"
      :disabled="isAtMaximum"
      type="button"
      @click.prevent="increment"
    >
      <font-awesome icon="chevron-right" />
    </button>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-facing-decorator";
import { logger } from "@/utils/logger";

/**
 * AmountInput - Numeric input with increment/decrement controls
 *
 * Features:
 * - Increment/decrement buttons with validation
 * - Configurable min/max values and step size
 * - Input validation and formatting
 * - Disabled state handling for boundary values
 * - Function props for parent control over validation and updates
 * - Maintains v-model compatibility through onUpdateValue function prop
 */
@Component({ name: "AmountInput" })
export default class AmountInput extends Vue {
  /** Current numeric value */
  @Prop({ required: true })
  value!: number;

  /** Minimum allowed value */
  @Prop({ default: 0 })
  min!: number;

  /** Maximum allowed value */
  @Prop({ default: Number.MAX_SAFE_INTEGER })
  max!: number;

  /** Step size for increment/decrement */
  @Prop({ default: 1 })
  step!: number;

  /** Input element ID for accessibility */
  @Prop({ default: "amount-input" })
  inputId!: string;

  /**
   * Function prop for handling value updates
   * Called when the value changes, allowing parent to control validation and update behavior
   */
  @Prop({ type: Function, default: () => {} })
  onUpdateValue!: (value: number) => void | Promise<void>;

  /** Internal display value for input field */
  private displayValue: string = "0";

  /**
   * Initialize display value from prop
   */
  mounted(): void {
    logger.debug("[AmountInput] mounted()", {
      value: this.value,
      min: this.min,
      max: this.max,
      step: this.step,
    });
    this.displayValue = this.value.toString();
    logger.debug("[AmountInput] mounted() - displayValue set", {
      displayValue: this.displayValue,
    });
  }

  /**
   * Watch for external value changes
   */
  @Watch("value")
  onValueChange(newValue: number): void {
    this.displayValue = newValue.toString();
  }

  /**
   * Check if current value is at minimum
   */
  get isAtMinimum(): boolean {
    const result = this.value <= this.min;
    logger.debug("[AmountInput] isAtMinimum", {
      value: this.value,
      min: this.min,
      result,
    });
    return result;
  }

  /**
   * Check if current value is at maximum
   */
  get isAtMaximum(): boolean {
    const result = this.value >= this.max;
    logger.debug("[AmountInput] isAtMaximum", {
      value: this.value,
      max: this.max,
      result,
    });
    return result;
  }

  /**
   * Increment the value by step size
   */
  increment(): void {
    logger.debug("[AmountInput] increment() called", {
      currentValue: this.value,
      step: this.step,
    });
    const newValue = Math.min(this.value + this.step, this.max);
    logger.debug("[AmountInput] increment() calculated newValue", {
      newValue,
    });
    this.updateValue(newValue);
  }

  /**
   * Decrement the value by step size
   */
  decrement(): void {
    logger.debug("[AmountInput] decrement() called", {
      currentValue: this.value,
      step: this.step,
    });
    const newValue = Math.max(this.value - this.step, this.min);
    logger.debug("[AmountInput] decrement() calculated newValue", {
      newValue,
    });
    this.updateValue(newValue);
  }

  /**
   * Handle direct input changes
   */
  handleInput(): void {
    const numericValue = parseFloat(this.displayValue);
    if (!isNaN(numericValue)) {
      const clampedValue = Math.max(this.min, Math.min(numericValue, this.max));
      this.updateValue(clampedValue);
    }
  }

  /**
   * Handle input blur - ensure display value matches actual value
   */
  handleBlur(): void {
    this.displayValue = this.value.toString();
  }

  /**
   * Update the value and call parent function prop
   * Allows parent to control validation and update behavior
   */
  private updateValue(newValue: number): void {
    logger.debug("[AmountInput] updateValue() called", {
      oldValue: this.value,
      newValue,
    });
    if (newValue !== this.value) {
      logger.debug(
        "[AmountInput] updateValue() - values different, updating and calling parent handler",
      );
      this.displayValue = newValue.toString();
      this.onUpdateValue(newValue);
    } else {
      logger.debug(
        "[AmountInput] updateValue() - values same, skipping update",
      );
    }
  }
}
</script>

<style scoped>
/* Remove spinner arrows from number input */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

input[type="number"] {
  -moz-appearance: textfield;
}

/* Disabled button styles */
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>
