import { inject, Injectable } from '@angular/core';
import { SupplierService } from '@generated/swagger/inventory-api';
import { Supplier } from '../models';
import { firstValueFrom } from 'rxjs';
import { injectStorage, MemoryStorageProvider } from '@isa/core/storage';
import { SUPPLIER_STORAGE_KEY } from '../constants';
import { RemissionStockService } from './remission-stock.service';
import { DataAccessError, takeUntilAborted } from '@isa/common/data-access';
import { logger } from '@isa/core/logging';

/**
 * Service for managing remission suppliers.
 * Handles fetching and caching supplier data for the assigned stock.
 * 
 * @class RemissionSupplierService
 * @injectable
 * 
 * @example
 * // Inject the service
 * constructor(private supplierService: RemissionSupplierService) {}
 * 
 * // Fetch suppliers
 * const suppliers = await this.supplierService.fetchSuppliers();
 */
@Injectable({ providedIn: 'root' })
export class RemissionSupplierService {
  /** Private instance of the supplier service from inventory API */
  #supplierService = inject(SupplierService);
  /** Private instance of the remission stock service */
  #stockService = inject(RemissionStockService);
  /** Private memory storage for caching suppliers */
  #memoryStorage = injectStorage(MemoryStorageProvider);
  /** Private logger instance */
  #logger = logger(() => ({ service: 'RemissionSupplierService' }));

  /**
   * Fetches all suppliers for the currently assigned stock.
   * Results are cached in memory storage to reduce API calls.
   * 
   * @async
   * @param {AbortSignal} [abortSignal] - Optional signal to abort the request
   * @returns {Promise<Supplier[]>} Array of suppliers for the assigned stock
   * @throws {DataAccessError} When no assigned stock is found
   * @throws {Error} When the API request fails
   * 
   * @example
   * const controller = new AbortController();
   * try {
   *   const suppliers = await service.fetchSuppliers(controller.signal);
   *   // Process suppliers...
   * } catch (error) {
   *   // Handle error...
   * }
   * 
   * @todo Add schema validation for cached data
   */
  async fetchSuppliers(abortSignal?: AbortSignal): Promise<Supplier[]> {
    this.#logger.debug('Fetching suppliers');
    
    const cached = await this.#memoryStorage.get(SUPPLIER_STORAGE_KEY); // TODO: Schema Validierung erstellen

    if (cached) {
      this.#logger.debug('Returning cached suppliers', () => ({
        supplierCount: (cached as Supplier[]).length
      }));
      return cached as Supplier[];
    }

    this.#logger.info('Fetching assigned stock for suppliers');
    const assignedStock =
      await this.#stockService.fetchAssignedStock(abortSignal);

    if (!assignedStock) {
      const error = new DataAccessError(
        'NO_ASSIGNED_STOCK_FOUND',
        'No assigned stock found',
        null,
      );
      this.#logger.error('No assigned stock found', error);
      throw error;
    }

    this.#logger.info('Fetching suppliers from API', () => ({
      stockId: assignedStock.id
    }));
    
    let req$ = this.#supplierService.SupplierGetSuppliers({
      stockId: assignedStock.id,
    });

    if (abortSignal) {
      this.#logger.debug('Request configured with abort signal');
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    const res = await firstValueFrom(req$);

    if (res.error || !res.result) {
      const error = new Error(res.message || 'Failed to fetch Suppliers');
      this.#logger.error('Failed to fetch suppliers', error);
      throw error;
    }

    this.#logger.debug('Successfully fetched suppliers', () => ({
      supplierCount: res.result?.length || 0
    }));
    
    this.#memoryStorage.set(SUPPLIER_STORAGE_KEY, res.result);

    return res.result as Supplier[];
  }
}
