import { Injectable, inject, resource, signal, computed } from '@angular/core';
import { logger } from '@isa/core/logging';
import { CrmSearchService } from '../services/crm-search.service';
import { BonusCardInfo } from '../models';

/**
 * Resource for loading customer bonus cards (Kundenkarten) using Angular's Resource API.
 *
 * Provides reactive, automatic loading of all bonus cards for a given customer ID.
 * Uses Angular's `resource()` function for declarative data fetching with built-in
 * loading states, error handling, and automatic race condition prevention.
 *
 * **Features:**
 * - Reactive loading triggered by parameter changes
 * - Automatic request cancellation on parameter updates (race condition prevention)
 * - Built-in loading, error, and status states
 * - Customer ID can be changed dynamically via `params()` method
 * - Lazy loading: only fetches when customerId is provided
 *
 * **Lifecycle:**
 * - **Injectable:** Component-level only (not providedIn: 'root')
 * - **Scope:** Provide in component `providers` array for isolated state
 * - **Data Flow:** params → loader → resource.value → UI
 *
 * @example
 * ```typescript
 * @Component({
 *   providers: [CustomerBonusCardsResource],
 * })
 * export class CustomerCardsComponent {
 *   readonly #bonusCardsResource = inject(CustomerBonusCardsResource);
 *
 *   // Reactive signals exposed by resource
 *   readonly cards = this.#bonusCardsResource.resource.value;
 *   readonly isLoading = this.#bonusCardsResource.resource.isLoading;
 *   readonly error = this.#bonusCardsResource.resource.error;
 *
 *   // Trigger load by updating params
 *   loadCards(customerId: number): void {
 *     this.#bonusCardsResource.params({ customerId });
 *   }
 * }
 * ```
 */
@Injectable()
export class CustomerBonusCardsResource {
  readonly #crmSearchService = inject(CrmSearchService);
  readonly #logger = logger(() => ({ context: 'CustomerBonusCardsResource' }));

  readonly #customerId = signal<number | undefined>(undefined);

  /**
   * Current customer ID being loaded.
   *
   * Read-only computed signal that exposes the current customer ID parameter.
   * Returns `undefined` if no customer ID has been set via `params()`.
   *
   * @returns Customer ID or undefined if not set
   */
  readonly customerId = computed(() => this.#customerId());

  /**
   * Angular Resource API instance that manages bonus card loading.
   *
   * Automatically loads bonus cards when `customerId` parameter changes.
   * Provides reactive signals for data, loading state, errors, and status.
   *
   * **Exposed Signals:**
   * - `value()` - Array of bonus cards or undefined (when no data loaded)
   * - `isLoading()` - Boolean indicating if a request is in progress
   * - `error()` - Error object if load failed, undefined otherwise
   * - `status()` - Current lifecycle status: 'idle' | 'loading' | 'resolved' | 'error'
   *
   * **Behavior:**
   * - Lazy: Only loads when customerId is provided (not undefined)
   * - Reactive: Automatically reloads when params change
   * - Safe: Cancels previous requests to prevent race conditions
   * - Cacheable: Returns undefined as default value before first load
   *
   * @example
   * ```typescript
   * // Access reactive signals
   * const cards = this.resource.value();       // BonusCardInfo[] | undefined
   * const loading = this.resource.isLoading(); // boolean
   * const error = this.resource.error();       // Error | undefined
   * const status = this.resource.status();     // ResourceStatus
   * ```
   */
  readonly resource = resource({
    params: computed(() => ({ customerId: this.#customerId() })),
    loader: async ({
      params,
      abortSignal,
    }): Promise<BonusCardInfo[] | undefined> => {
      const { customerId } = params;

      if (!customerId) {
        this.#logger.debug('No customerId provided, skipping load');
        return undefined;
      }

      this.#logger.debug('Loading bonus cards', () => ({ customerId }));

      const response = await this.#crmSearchService.fetchCustomerCards(
        { customerId },
        abortSignal,
      );

      this.#logger.debug('Bonus cards loaded', () => ({
        customerId,
        count: response?.result?.length ?? 0,
      }));

      return response?.result;
    },
    defaultValue: undefined,
  });

  constructor() {
    (window as any)['__customerBonusCardsResource'] = this;
  }

  /**
   * Updates resource parameters to trigger a reactive reload of bonus cards.
   *
   * Setting a new `customerId` will automatically cancel any in-flight request
   * and start a new load with the updated parameter. The resource's loader
   * function will be called with the new parameters.
   *
   * **Behavior:**
   * - Passing `customerId` triggers a load for that customer
   * - Passing `undefined` clears the data and returns resource to idle state
   * - Automatically cancels previous requests (race condition prevention)
   * - Updates are reactive: UI updates automatically via resource signals
   *
   * @param params - Parameters for loading bonus cards
   * @param params.customerId - Customer ID to load cards for, or undefined to clear
   *
   * @example
   * ```typescript
   * // Load cards for customer 12345
   * bonusCardsResource.params({ customerId: 12345 });
   *
   * // Clear data and reset to idle
   * bonusCardsResource.params({ customerId: undefined });
   * ```
   */
  params(params: { customerId?: number }): void {
    this.#logger.debug('Updating params', () => params);
    this.#customerId.set(params.customerId);
  }

  /**
   * Adds a random test card to the current card list.
   *
   * **For testing purposes only.** Creates a new card with random code
   * and copies customer details from the first existing card.
   *
   * **Testing the Card Stack Display:**
   * This method is useful for testing the card carousel stack behavior,
   * particularly verifying that newly added cards appear correctly in the stack
   * rather than mispositioned on the right side.
   *
   * **How to Test:**
   * 1. Open browser DevTools console (F12)
   * 2. Navigate to a customer with bonus cards
   * 3. Execute: `window.__customerBonusCardsResource.addRandomCardForTesting()`
   * 4. Verify the new card appears properly in the stack (not on the right)
   * 5. Repeat to test with multiple new cards
   *
   * **Expected Behavior:**
   * - New card should appear in correct stack position based on isPrimary/isActive
   * - Card should animate smoothly into position
   * - Stack should recalculate positions using ResizeObserver
   * - No visual glitches or misalignment
   *
   * @example
   * ```typescript
   * // From browser console (development only):
   * window.__customerBonusCardsResource.addRandomCardForTesting();
   *
   * // From component code:
   * this.#bonusCardsResource.addRandomCardForTesting();
   * ```
   */
  addRandomCardForTesting(): void {
    const currentCards = this.resource.value() ?? [];
    const newCard: BonusCardInfo = {
      code: `TEST-${Math.floor(Math.random() * 100000)}`,
      format: 'CARDCODE',
      isActive: true,
      isPrimary: false,
      totalPoints: currentCards[0].totalPoints,
      firstName: currentCards[0].firstName,
      lastName: currentCards[0].lastName,
    };
    this.#logger.debug('Adding random test card', () => ({ newCard }));
    this.resource.set([...currentCards, newCard]);
  }
}
