import { inject, Injectable } from '@angular/core';
import {
  CustomerService,
  LoyaltyCardService,
  LoyaltyBookingInfoDTO,
  KeyValueDTOOfStringAndString,
  KeyValueDTOOfStringAndInteger,
  LoyaltyBonResponse,
  AccountDetailsDTO,
} from '@generated/swagger/crm-api';
import {
  AddBookingInput,
  AddBookingSchema,
  CheckBonInput,
  CheckBonSchema,
  AddBonInput,
  AddBonSchema,
  Customer,
  FetchCustomerCardsInput,
  FetchCustomerCardsSchema,
  FetchCustomerInput,
  FetchCustomerSchema,
  AddCardInput,
  LockCardInput,
  UnlockCardInput,
  AddCardSchema,
  LockCardSchema,
  UnlockCardSchema,
} from '../schemas';
import {
  catchResponseArgsErrorPipe,
  ResponseArgs,
  ResponseArgsError,
  takeUntilAborted,
} from '@isa/common/data-access';
import { Cache, CacheTimeToLive } from '@isa/common/decorators';
import { firstValueFrom } from 'rxjs';
import { BonusCardInfo } from '../models';
import { logger } from '@isa/core/logging';

@Injectable({ providedIn: 'root' })
export class CrmSearchService {
  #customerService = inject(CustomerService);
  #loyaltyCardService = inject(LoyaltyCardService);
  #logger = logger(() => ({
    service: 'CrmSearchService',
  }));

  async fetchCustomer(
    params: FetchCustomerInput,
    abortSignal?: AbortSignal,
  ): Promise<ResponseArgs<Customer>> {
    this.#logger.info('Fetching customer from API');
    const { customerId, eagerLoading } = FetchCustomerSchema.parse(params);

    let req$ = this.#customerService
      .CustomerGetCustomer({ customerId, eagerLoading })
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    try {
      const res = await firstValueFrom(req$);
      this.#logger.debug('Successfully fetched customer');
      return res as ResponseArgs<Customer>;
    } catch (error) {
      this.#logger.error('Error fetching customer', error);
      return undefined as unknown as ResponseArgs<Customer>;
    }
  }

  async fetchCustomerCards(
    params: FetchCustomerCardsInput,
    abortSignal?: AbortSignal,
  ): Promise<ResponseArgs<BonusCardInfo[]>> {
    this.#logger.info('Fetching customer bonuscards from API');
    const { customerId } = FetchCustomerCardsSchema.parse(params);

    let req$ = this.#customerService
      .CustomerGetBonuscards(customerId)
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    try {
      const res = await firstValueFrom(req$);
      this.#logger.debug('Successfully fetched customer bonuscards');
      return res as ResponseArgs<BonusCardInfo[]>;
    } catch (error) {
      this.#logger.error('Error fetching customer cards', error);
      return [] as unknown as ResponseArgs<BonusCardInfo[]>;
    }
  }

  async fetchLoyaltyBookings(
    customerId: number,
    abortSignal?: AbortSignal,
  ): Promise<LoyaltyBookingInfoDTO[]> {
    this.#logger.info('Fetching loyalty bookings from API');

    let req$ = this.#loyaltyCardService
      .LoyaltyCardListCustomerBookings({ customerId })
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    try {
      const res = await firstValueFrom(req$);
      this.#logger.debug('Successfully fetched loyalty bookings');

      const transactions = res?.result?.data || [];

      return transactions;
    } catch (error) {
      this.#logger.error('Error fetching loyalty bookings', error);
      return [];
    }
  }

  async fetchBookingReasons(
    abortSignal?: AbortSignal,
  ): Promise<KeyValueDTOOfStringAndInteger[]> {
    this.#logger.info('Fetching booking reasons from API');

    let req$ = this.#loyaltyCardService
      .LoyaltyCardBookingReason()
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    try {
      const res = await firstValueFrom(req$);
      this.#logger.debug('Successfully fetched booking reasons');

      return res?.result || [];
    } catch (error) {
      this.#logger.error('Error fetching booking reasons', error);
      return [];
    }
  }

  async addCard(params: AddCardInput): Promise<AccountDetailsDTO | undefined> {
    this.#logger.info('Adding customer card');

    const parsed = AddCardSchema.parse(params);

    const req$ = this.#customerService
      .CustomerMergeLoyaltyAccounts({
        customerId: parsed.customerId,
        loyaltyCardValues: {
          cardCode: parsed.loyaltyCardValues.cardCode,
        },
      })
      .pipe(catchResponseArgsErrorPipe());

    const res = await firstValueFrom(req$);
    return res?.result;
  }

  async lockCard(params: LockCardInput): Promise<boolean | undefined> {
    this.#logger.info('Locking customer card');

    const parsed = LockCardSchema.parse(params);

    const req$ = this.#loyaltyCardService
      .LoyaltyCardLockCard({
        cardCode: parsed.cardCode,
      })
      .pipe(catchResponseArgsErrorPipe());

    const res = await firstValueFrom(req$);

    if (res.error) {
      const err = new ResponseArgsError(res);
      this.#logger.error('Lock card Failed', err);
      throw err;
    }

    return res?.result;
  }

  async unlockCard(params: UnlockCardInput): Promise<boolean | undefined> {
    this.#logger.info('Unlocking customer card');

    const parsed = UnlockCardSchema.parse(params);

    const req$ = this.#loyaltyCardService
      .LoyaltyCardUnlockCard({
        customerId: parsed.customerId,
        cardCode: parsed.cardCode,
      })
      .pipe(catchResponseArgsErrorPipe());

    const res = await firstValueFrom(req$);

    if (res.error) {
      const err = new ResponseArgsError(res);
      this.#logger.error('Unlock card Failed', err);
      throw err;
    }

    return res?.result;
  }

  @Cache({ ttl: CacheTimeToLive.oneHour })
  async fetchCurrentBookingPartnerStore(
    abortSignal?: AbortSignal,
  ): Promise<KeyValueDTOOfStringAndString | undefined> {
    this.#logger.info('Fetching current booking partner store from API');

    let req$ = this.#loyaltyCardService
      .LoyaltyCardCurrentBookingPartnerStore()
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    const res = await firstValueFrom(req$);
    this.#logger.debug('Successfully fetched current booking partner store');

    return res?.result;
  }

  async addBooking(
    params: AddBookingInput,
  ): Promise<LoyaltyBookingInfoDTO | undefined> {
    const parsed = AddBookingSchema.parse(params);

    const req$ = this.#loyaltyCardService
      .LoyaltyCardAddBooking({
        cardCode: parsed.cardCode,
        booking: {
          points: parsed.booking.points,
          reason: parsed.booking.reason,
          storeId: parsed.booking.storeId,
        },
      })
      .pipe(catchResponseArgsErrorPipe());
    const res = await firstValueFrom(req$);
    return res?.result;
  }

  /**
   * Check/validate a Bon number
   */
  async checkBon(
    params: CheckBonInput,
    abortSignal?: AbortSignal,
  ): Promise<ResponseArgs<LoyaltyBonResponse>> {
    this.#logger.info('Checking Bon from API');
    const { cardCode, bonNr, storeId } = CheckBonSchema.parse(params);

    let req$ = this.#loyaltyCardService
      .LoyaltyCardLoyaltyBonCheck({
        cardCode,
        payload: { bonNr, storeId },
      })
      .pipe(catchResponseArgsErrorPipe());

    if (abortSignal) {
      req$ = req$.pipe(takeUntilAborted(abortSignal));
    }

    try {
      const res = await firstValueFrom(req$);
      this.#logger.debug('Successfully checked Bon');

      if (res.error) {
        const err = new ResponseArgsError(res);
        this.#logger.error('Bon check failed', err);
        throw err;
      }

      return res as ResponseArgs<LoyaltyBonResponse>;
    } catch (error) {
      this.#logger.error('Error checking Bon', error);
      throw error;
    }
  }

  /**
   * Redeem/add a Bon for customer points
   */
  async addBon(params: AddBonInput): Promise<boolean> {
    this.#logger.info('Redeeming Bon from API');
    const { cardCode, bonNr, storeId } = AddBonSchema.parse(params);

    const req$ = this.#loyaltyCardService
      .LoyaltyCardLoyaltyBonAdd({
        cardCode,
        payload: { bonNr, storeId },
      })
      .pipe(catchResponseArgsErrorPipe());

    const res = await firstValueFrom(req$);
    this.#logger.debug('Successfully redeemed Bon');
    return res?.result ?? false;
  }
}
