import { inject, Injectable } from "@angular/core";
import {
  FetchReturnDetails,
  FetchReturnDetailsSchema,
  ReturnReceiptValues,
} from "../schemas";
import { firstValueFrom } from "rxjs";
import { ReceiptService } from "@generated/swagger/oms-api";
import { CanReturn, Receipt, ReceiptItem, ReceiptListItem } from "../models";
import { CategoryQuestions, ProductCategory } from "../questions";
import { KeyValue } from "@angular/common";
import { ReturnCanReturnService } from "./return-can-return.service";
import { takeUntilAborted } from "@isa/common/data-access";
import { z } from "zod";

/**
 * Service responsible for managing receipt return details and operations.
 *
 * This service provides functionality to:
 * - Check if items are eligible for return
 * - Fetch receipt details by receipt ID
 * - Query receipts by customer email
 * - Get available product categories for returns
 */
@Injectable({ providedIn: "root" })
export class ReturnDetailsService {
  #receiptService = inject(ReceiptService);
  #returnCanReturnService = inject(ReturnCanReturnService);
  /**
   * Determines if a specific receipt item can be returned for a given category.
   *
   * @param params - The parameters for the return check.
   * @param params.item - The receipt item to check.
   * @param params.category - The product category to check against.
   * @param abortSignal - Optional AbortSignal to cancel the request.
   * @returns A promise resolving to the result of the canReturn check, containing
   *          eligibility status and any relevant constraints or messages.
   * @throws Will throw an error if the return check fails or is aborted.
   */
  async canReturn(
    {
      receiptItemId,
      quantity,
      category,
    }: { receiptItemId: number; quantity: number; category: ProductCategory },
    abortSignal?: AbortSignal,
  ): Promise<CanReturn> {
    const returnReceiptValues: ReturnReceiptValues = {
      quantity,
      receiptItem: {
        id: receiptItemId,
      },
      category,
    };

    return this.#returnCanReturnService.canReturn(
      returnReceiptValues,
      abortSignal,
    );
  }
  /**
   * Gets all available product categories that have defined question sets.
   *
   * This method filters out the "Unknown" category and returns all other
   * categories defined in the CategoryQuestions object.
   *
   * @returns {KeyValue<ProductCategory, string>[]} Array of key-value pairs representing
   *          available categories, where the key is the ProductCategory enum value
   *          and the value is the string representation.
   */
  availableCategories(): KeyValue<ProductCategory, string>[] {
    const categories = Object.keys(CategoryQuestions).map((key) => {
      return { key: key as ProductCategory, value: key };
    });

    return categories.filter(
      (category) => category.key !== ProductCategory.Unknown,
    );
  }
  /**
   * Fetches the return details for a specific receipt.
   *
   * This method retrieves detailed information about a receipt using its ID.
   * The data is validated using Zod schema validation before making the API call.
   *
   * @param params - The parameters required to fetch the return details, including the receipt ID.
   * @param abortSignal - Optional AbortSignal that can be used to cancel the request.
   * @returns A promise that resolves to the fetched receipt details.
   * @throws Will throw an error if the parameters are invalid or if the API response contains an error.
   */
  async fetchReturnDetails(
    params: FetchReturnDetails,
    abortSignal?: AbortSignal,
  ): Promise<Receipt> {
    const parsed = FetchReturnDetailsSchema.parse(params);

    let req$ = this.#receiptService.ReceiptGetReceipt({
      receiptId: parsed.receiptId,
      eagerLoading: 2,
    });

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

    const res = await firstValueFrom(req$);

    if (res.error || !res.result) {
      throw new Error(res.message || "Failed to fetch return details");
    }

    return res.result as Receipt;
  }
  /**
   * Schema definition for email-based receipt query parameters.
   * Validates that the email parameter is a properly formatted email address.
   */
  static FetchReceiptsEmailParamsSchema = z.object({
    email: z.string(),
  });

  /**
   * Fetches receipts associated with a specific email address.
   *
   * This method queries the receipt service for receipts that match the provided email address.
   * The email is validated using Zod schema validation before making the API call.
   *
   * @param params - The parameters containing the email to search for.
   * @param params.email - Email address to search for in receipt records.
   * @param abortSignal - Optional AbortSignal that can be used to cancel the request.
   * @returns A promise that resolves to an array of receipt list items matching the email.
   * @throws Will throw an error if the email is invalid or if the API response contains an error.
   */
  async fetchReceiptsByEmail(
    params: z.infer<typeof ReturnDetailsService.FetchReceiptsEmailParamsSchema>,
    abortSignal?: AbortSignal,
  ): Promise<ReceiptListItem[]> {
    const { email } =
      ReturnDetailsService.FetchReceiptsEmailParamsSchema.parse(params);

    let req$ = this.#receiptService.ReceiptQueryReceipt({
      queryToken: {
        input: { qs: email },
        filter: { receipt_type: "1;128;1024" },
      },
    });

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

    const res = await firstValueFrom(req$);
    if (res.error || !res.result) {
      throw new Error(res.message || "Failed to fetch return items by email");
    }

    return res.result as ReceiptListItem[];
  }
}
