import { DateValue } from './types';
import { ControlValueAccessor } from '@angular/forms';
import { Directive, inject, input, model } from '@angular/core';
import { UI_DATEPICKER_DEFAULT_MAX, UI_DATEPICKER_DEFAULT_MIN } from './tokens';

/**
 * Abstract base class for datepicker components.
 * Implements the ControlValueAccessor interface for Angular forms.
 * Provides common functionality for parsing and setting datepicker values.
 *
 * @template TValue - The type of the datepicker value.
 */
@Directive()
export abstract class DatepickerBase<TValue> implements ControlValueAccessor {
  /**
   * Callback function invoked when the control's value changes.
   */
  onChange?: (value: TValue | undefined) => void;

  /**
   * Callback function invoked when the control is touched.
   */
  onTouched?: () => void;

  /**
   * Internal model representing the current datepicker value.
   */
  value = model<TValue | undefined>();

  /**
   * The minimum allowed date for the datepicker.
   * Injected using the UI_DATEPICKER_DEFAULT_MIN token.
   */
  min = input<DateValue | undefined>(inject(UI_DATEPICKER_DEFAULT_MIN));

  /**
   * The maximum allowed date for the datepicker.
   * Injected using the UI_DATEPICKER_DEFAULT_MAX token.
   */
  max = input<DateValue | undefined>(inject(UI_DATEPICKER_DEFAULT_MAX));

  /**
   * Parses an unknown value into the datepicker's value type.
   * Must be implemented by concrete subclasses.
   *
   * @param value - The value to be parsed.
   * @returns The parsed value of type TValue, or undefined if parsing fails.
   */
  abstract parseValue(value: unknown): TValue | undefined;

  /**
   * Sets the datepicker value after parsing the input.
   * If parsing fails, logs an error to the console.
   *
   * @param value - The new value to set.
   */
  setValue(value?: TValue) {
    try {
      const parsedValue = this.parseValue(value);
      this.value.set(parsedValue);
      this.onChange?.(parsedValue);
      this.onTouched?.();
    } catch (error) {
      console.error('Error parsing value:', error);
    }
  }

  /**
   * Writes a new value to the datepicker, as required by the ControlValueAccessor interface.
   * If parsing fails, logs an error to the console.
   *
   * @param obj - The new value to write.
   */
  writeValue(obj: unknown) {
    try {
      const parsedValue = this.parseValue(obj);
      this.value.set(parsedValue);
    } catch (error) {
      console.error('Error parsing value:', error);
    }
  }

  /**
   * Registers a callback function that is called when the value changes.
   *
   * @param fn - The callback function to register.
   */
  registerOnChange(fn: typeof this.onChange): void {
    this.onChange = fn;
  }

  /**
   * Registers a callback function that is called when the control is touched.
   *
   * @param fn - The callback function to register.
   */
  registerOnTouched(fn: typeof this.onTouched): void {
    this.onTouched = fn;
  }
}
