import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
} from '@angular/core';
import { ReturnReceiptListItemComponent } from './return-receipt-list-item/return-receipt-list-item.component';
import { Receipt, Return } from '@isa/remission/data-access';
import {
  provideFilter,
  withQueryParamsSync,
  withQuerySettings,
  FilterService,
  FilterControlsPanelComponent,
} from '@isa/shared/filter';
import { RouterLink } from '@angular/router';
import { compareAsc, compareDesc, subDays } from 'date-fns';
import { RETURN_RECEIPT_QUERY_SETTINGS } from './remission-return-receipt-list.query-settings';
import { RemissionReturnReceiptListCardComponent } from './return-receipt-list-card/return-receipt-list-card.component';
import {
  completedRemissionReturnsResource,
  incompletedRemissionReturnsResource,
} from './resources';

/**
 * Component for displaying a list of remission return receipts.
 * It shows both completed and incomplete receipts, with options to filter and sort.
 *
 * @component
 * @selector remi-remission-return-receipt-list
 * @standalone
 *
 * @example
 * <remi-remission-return-receipt-list></remi-remission-return-receipt-list>
 */
@Component({
  selector: 'remi-remission-return-receipt-list',
  templateUrl: './remission-return-receipt-list.component.html',
  styleUrls: ['./remission-return-receipt-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  imports: [
    RemissionReturnReceiptListCardComponent,
    FilterControlsPanelComponent,
    ReturnReceiptListItemComponent,
    RouterLink,
  ],
  providers: [
    provideFilter(
      withQuerySettings(RETURN_RECEIPT_QUERY_SETTINGS),
      withQueryParamsSync(),
    ),
  ],
})
export class RemissionReturnReceiptListComponent {
  /** Filter service for managing filter state and operations */
  #filter = inject(FilterService);

  /**
   * Computed signal that retrieves the currently selected order date for sorting.
   * This is used to determine how the return receipts should be ordered.
   * @returns {string | undefined} The selected order date
   */
  orderDateBy = computed(() => this.#filter.orderBy().find((o) => o.selected));

  /** Resource for fetching completed remission return receipts */
  completedRemissionReturnsResource = completedRemissionReturnsResource();

  /** Resource for fetching incomplete remission return receipts */
  incompletedRemissionReturnsResource = incompletedRemissionReturnsResource();

  /** Computed signal that retrieves the value of the completed remission returns resource */
  completedRemissionReturnsResourceValue = computed(() => {
    return this.completedRemissionReturnsResource.value() || [];
  });

  /** Computed signal that retrieves the value of the incomplete remission returns resource */
  incompletedRemissionReturnsResourceValue = computed(() => {
    return this.incompletedRemissionReturnsResource.value() || [];
  });

  /**
   * Computed signal that combines completed and incomplete remission returns,
   * filtering out any receipts that do not have associated data.
   * It also orders the returns based on the selected order date.
   * @returns {Array<[Return, Receipt]>} Array of tuples containing Return and Receipt objects
   */
  returns = computed(() => {
    let completed = this.completedRemissionReturnsResourceValue();
    let incompleted = this.incompletedRemissionReturnsResourceValue();
    const orderBy = this.orderDateBy();

    if (orderBy) {
      const compareFn = (a: string | undefined, b: string | undefined) => {
        if (a === undefined) return 1;
        if (b === undefined) return -1;
        return (orderBy.dir === 'desc' ? compareDesc : compareAsc)(a, b);
      };

      const orderByField = orderBy.by as 'created' | 'completed';
      completed = orderByKey(completed, orderByField, compareFn);
      incompleted = orderByKey(incompleted, orderByField, compareFn);
    }

    return [...incompleted, ...completed].flatMap((ret) =>
      ret.receipts
        .filter((rec) => rec.data != null)
        .map((rec) => [ret, rec.data] as [Return, Receipt]),
    );
  });

  /**
   * Reloads the completed and incomplete remission returns resources.
   * This is typically called when the user performs an action that requires
   * refreshing the list of returns, such as after adding or deleting a return.
   */
  reloadList() {
    this.completedRemissionReturnsResource.reload();
    this.incompletedRemissionReturnsResource.reload();
  }
}

/**
 * Helper function to order an array of objects by a specific key.
 * Uses a custom comparison function to sort the items.
 *
 * @param items - Array of items to be sorted
 * @param by - Key to sort by
 * @param compareFn - Comparison function for sorting
 * @returns Sorted array of items
 */
function orderByKey<T, K extends keyof T>(
  items: T[],
  by: K,
  compareFn: T[K] extends infer U ? (a: U, b: U) => number : never,
): T[] {
  return [...items].sort((a, b) => compareFn(a[by], b[by]) ?? 0);
}
