import { signal, WritableSignal } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime, filter, take } from 'rxjs/operators';

/**
 * Creates a debounced submit handler to prevent double-click submissions.
 * Returns a function that will only trigger once within the debounce period.
 *
 * Usage:
 * ```typescript
 * private submitDebounce = createSubmitDebounce(300);
 *
 * onSubmit(): void {
 *   this.submitDebounce(() => {
 *     // actual submit logic
 *   });
 * }
 * ```
 */
export function createSubmitDebounce(debounceMs = 300): (callback: () => void) => void {
  const subject = new Subject<() => void>();
  let isProcessing = false;

  subject.pipe(
    filter(() => !isProcessing),
    debounceTime(debounceMs)
  ).subscribe((callback) => {
    isProcessing = true;
    try {
      callback();
    } finally {
      // Reset after a short delay to allow for proper state cleanup
      setTimeout(() => {
        isProcessing = false;
      }, debounceMs);
    }
  });

  return (callback: () => void) => {
    subject.next(callback);
  };
}

/**
 * Creates a submitting signal with automatic reset capability.
 * Useful for managing form submission state.
 */
export function createSubmittingState(): {
  submitting: WritableSignal<boolean>;
  startSubmit: () => void;
  endSubmit: () => void;
  withSubmit: <T>(promise: Promise<T>) => Promise<T>;
} {
  const submitting = signal(false);

  return {
    submitting,
    startSubmit: () => submitting.set(true),
    endSubmit: () => submitting.set(false),
    withSubmit: async <T>(promise: Promise<T>): Promise<T> => {
      submitting.set(true);
      try {
        return await promise;
      } finally {
        submitting.set(false);
      }
    },
  };
}

/**
 * Tracks whether a form has been modified since loading.
 * Useful for "unsaved changes" warnings.
 */
export function createDirtyTracker(): {
  isDirty: WritableSignal<boolean>;
  markDirty: () => void;
  markClean: () => void;
} {
  const isDirty = signal(false);

  return {
    isDirty,
    markDirty: () => isDirty.set(true),
    markClean: () => isDirty.set(false),
  };
}

/**
 * Rate limits function calls. Only the last call within the window will execute.
 */
export function debounce<T extends (...args: unknown[]) => unknown>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timeoutId: ReturnType<typeof setTimeout> | null = null;

  return (...args: Parameters<T>) => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId);
    }

    timeoutId = setTimeout(() => {
      fn(...args);
      timeoutId = null;
    }, delay);
  };
}

/**
 * Throttles function calls. Ensures function is called at most once per interval.
 */
export function throttle<T extends (...args: unknown[]) => unknown>(
  fn: T,
  interval: number
): (...args: Parameters<T>) => void {
  let lastTime = 0;
  let timeoutId: ReturnType<typeof setTimeout> | null = null;

  return (...args: Parameters<T>) => {
    const now = Date.now();
    const remaining = interval - (now - lastTime);

    if (remaining <= 0) {
      if (timeoutId !== null) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }
      lastTime = now;
      fn(...args);
    } else if (timeoutId === null) {
      timeoutId = setTimeout(() => {
        lastTime = Date.now();
        timeoutId = null;
        fn(...args);
      }, remaining);
    }
  };
}

/**
 * Prevents the callback from being called more than once.
 * Useful for one-time submit operations.
 */
export function once<T extends (...args: unknown[]) => unknown>(fn: T): T {
  let called = false;
  let result: ReturnType<T>;

  return ((...args: Parameters<T>) => {
    if (!called) {
      called = true;
      result = fn(...args) as ReturnType<T>;
    }
    return result;
  }) as T;
}
