import { inject, Injectable } from '@angular/core';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import {
  AddToShoppingCartDTO,
  AvailabilityDTO,
  EntityDTOContainerOfDestinationDTO,
  ItemPayload,
  ItemsResult,
  ShoppingCartDTO,
  UpdateShoppingCartItemDTO,
} from '@generated/swagger/checkout-api';
import { Observable } from 'rxjs';
import { map, shareReplay, take } from 'rxjs/operators';
import { Branch, ItemData } from './purchase-options.types';
import { memorize } from '@utils/common';
import { AuthService } from '@core/auth';
import { ApplicationService } from '@core/application';
import { DomainOmsService } from '@domain/oms';
import {
  OrderTypeFeature,
  PurchaseOptionsFacade,
} from '@isa/checkout/data-access';

@Injectable({ providedIn: 'root' })
export class PurchaseOptionsService {
  #purchaseOptionsFacade = inject(PurchaseOptionsFacade);

  constructor(
    private _availabilityService: DomainAvailabilityService,
    private _checkoutService: DomainCheckoutService,
    private _omsService: DomainOmsService,
    private _auth: AuthService,
  ) {}

  getVats$() {
    return this._omsService.getVATs();
  }

  getCustomerFeatures(processId: number): Observable<Record<string, string>> {
    return this._checkoutService
      .getCustomerFeatures({ processId })
      .pipe(take(1), shareReplay(1));
  }

  @memorize()
  fetchDefaultBranch(): Observable<Branch> {
    return this.getBranch({
      branchNumber: this._auth.getClaimByKey('branch_no'),
    }).pipe(take(1), shareReplay(1));
  }

  fetchPickupAvailability(
    item: ItemData,
    quantity: number,
    branch: Branch,
  ): Observable<AvailabilityDTO> {
    return this._availabilityService
      .getPickUpAvailability({
        branch,
        quantity,
        item,
      })
      .pipe(map((res) => (Array.isArray(res) ? res[0] : undefined)));
  }

  fetchInStoreAvailability(
    item: ItemData,
    quantity: number,
    branch: Branch,
  ): Observable<AvailabilityDTO> {
    return this._availabilityService.getTakeAwayAvailability({
      item,
      quantity,
      branch,
    });
  }

  fetchDeliveryAvailability(
    item: ItemData,
    quantity: number,
  ): Observable<AvailabilityDTO> {
    return this._availabilityService.getDeliveryAvailability({
      item,
      quantity,
    });
  }

  fetchDigDeliveryAvailability(
    item: ItemData,
    quantity: number,
  ): Observable<AvailabilityDTO> {
    return this._availabilityService.getDigDeliveryAvailability({
      item,
      quantity,
    });
  }

  fetchB2bDeliveryAvailability(
    item: ItemData,
    quantity: number,
  ): Observable<AvailabilityDTO> {
    return this._availabilityService.getB2bDeliveryAvailability({
      item,
      quantity,
    });
  }

  fetchDownloadAvailability(item: ItemData): Observable<AvailabilityDTO> {
    return this._availabilityService.getDownloadAvailability({
      item,
    });
  }

  isAvailable(availability: AvailabilityDTO): boolean {
    return this._availabilityService.isAvailable({ availability });
  }

  fetchCanAdd(
    shoppingCartId: number,
    orderType: OrderTypeFeature,
    payload: ItemPayload[],
    customerFeatures: Record<string, string>,
  ): Promise<ItemsResult[]> {
    return this.#purchaseOptionsFacade.canAddItems({
      shoppingCartId,
      payload: payload.map((p) => ({
        ...p,
        customerFeatures: customerFeatures,
        orderType: orderType,
      })),
    });
  }

  removeItemFromShoppingCart(
    shoppingCartId: number,
    shoppingCartItemId: number,
  ): Promise<ShoppingCartDTO> {
    const shoppingCart = this.#purchaseOptionsFacade.removeItem({
      shoppingCartId,
      shoppingCartItemId,
    });

    return shoppingCart;
  }

  getInStoreDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
    return {
      data: { target: 1, targetBranch: { id: branch.id } },
    };
  }

  getPickupDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
    return {
      data: { target: 1, targetBranch: { id: branch.id } },
    };
  }

  getDeliveryDestination(
    availability: AvailabilityDTO,
  ): EntityDTOContainerOfDestinationDTO {
    return {
      data: { target: 2, logistician: availability?.logistician },
    };
  }

  getDownloadDestination(
    availability: AvailabilityDTO,
  ): EntityDTOContainerOfDestinationDTO {
    return {
      data: { target: 16, logistician: availability?.logistician },
    };
  }

  async addItemToShoppingCart(
    shoppingCartId: number,
    items: AddToShoppingCartDTO[],
  ) {
    const shoppingCart = await this.#purchaseOptionsFacade.addItem({
      shoppingCartId,
      items,
    });
    console.log('added item to cart', { shoppingCart });
    // FIX BUILD ERRORS
    // this._checkoutService.updateProcessCount(
    //   this._app.activatedProcessId,
    //   shoppingCart,
    // );
    return shoppingCart;
  }

  async updateItemInShoppingCart(
    shoppingCartId: number,
    shoppingCartItemId: number,
    payload: UpdateShoppingCartItemDTO,
  ) {
    const shoppingCart = await this.#purchaseOptionsFacade.updateItem({
      shoppingCartId,
      shoppingCartItemId,
      values: payload,
    });
    console.log('updated item in cart', { shoppingCart });
    // FIX BUILD ERRORS
    // this._checkoutService.updateProcessCount(
    //   this._app.activatedProcessId,
    //   shoppingCart,
    // );
  }

  @memorize({ comparer: (_) => true })
  getBranches(): Observable<Branch[]> {
    return this._availabilityService.getBranches().pipe(
      map((branches) => {
        return branches.filter((branch) => branch.isShippingEnabled == true);
      }),
      shareReplay(1),
    );
  }

  getBranch(params: { id: number }): Observable<Branch>;
  getBranch(params: { branchNumber: string }): Observable<Branch>;
  getBranch(params: { id: number; branchNumber: string }): Observable<Branch>;
  getBranch(params: {
    id?: number;
    branchNumber?: string;
  }): Observable<Branch> {
    return this.getBranches().pipe(
      map((branches) => {
        const branch = branches.find(
          (branch) =>
            branch.id == params.id ||
            branch.branchNumber == params.branchNumber,
        );
        return branch;
      }),
    );
  }
}
