import { inject, Injectable } from '@angular/core';
import { map, Observable, throwError } from 'rxjs';
import {
  ReceiptItemTaskListItem,
  TaskActionTypes,
  TaskActionTypeType,
} from '../models';
import { isTaskType } from '../helpers';
import { QueryTokenInput, QueryTokenSchema } from '../schemas';
import { ZodError } from 'zod';
import { ReturnParseQueryTokenError } from '../errors';
import {
  ReceiptService,
  ResponseArgsOfReceiptItemTaskListItemDTO,
} from '@generated/swagger/oms-api';

/**
 * Service responsible for managing return task list operations.
 *
 * Provides functionality to query receipt item tasks and complete tasks
 * in the return task list process.
 */
@Injectable({ providedIn: 'root' })
export class ReturnTaskListService {
  #receiptService = inject(ReceiptService);

  /**
   * Queries receipt item tasks that are not yet completed.
   *
   * Constructs a query token with a filter for incomplete tasks,
   * validates it using Zod schema validation, and fetches the tasks
   * from the receipt service.
   *
   * @returns An Observable of receipt item task list items that match the query
   * @throws ReturnParseQueryTokenError when query token parsing fails
   */
  queryReceiptItemTasks(
    queryToken: QueryTokenInput,
  ): Observable<ReceiptItemTaskListItem[]> {
    try {
      queryToken = QueryTokenSchema.parse(queryToken);
    } catch (error) {
      if (error instanceof ZodError) {
        return throwError(
          () => new ReturnParseQueryTokenError(queryToken, error.message),
        );
      }
      if (error instanceof Error) {
        return throwError(
          () => new ReturnParseQueryTokenError(queryToken, error.message),
        );
      }
      return throwError(
        () => new ReturnParseQueryTokenError(queryToken, 'Unknown error'),
      );
    }

    try {
      return this.#receiptService.ReceiptQueryReceiptItemTasks(queryToken).pipe(
        map((res) => {
          if (res.error || !res.result) {
            throw new Error(
              res.message || 'Failed to fetch receipt item tasks',
            );
          }

          return res.result as ReceiptItemTaskListItem[];
        }),
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  /**
   * Marks a receipt item task as completed.
   *
   * @param taskId - The unique identifier of the task to complete
   * @returns An Observable containing the updated receipt item task after completion
   */
  completeTask(taskId: number): Observable<ReceiptItemTaskListItem> {
    try {
      return this.#receiptService.ReceiptReceiptItemTaskCompleted(taskId).pipe(
        map((res) => {
          if (res.error || !res.result) {
            throw new Error(res.message || 'Failed to complete item task');
          }
          return res.result as ReceiptItemTaskListItem;
        }),
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  /**
   * Updates the type of a receipt item task (OK or NOK).
   *
   * @param updateTask - Object containing the task details
   * @param updateTask.type - The type to update the task to (OK or NOK)
   * @param updateTask.taskId - The unique identifier of the task to update
   * @returns An Observable containing the updated receipt item task
   * @throws Error when the update operation fails or returns an error
   */
  updateTaskType(updateTask: {
    type: Exclude<TaskActionTypeType, 'UNKNOWN' | 'RETOURE_UNKNOWN'>;
    taskId: number;
  }) {
    try {
      return this._updateTaskRequestHelper(updateTask).pipe(
        map((res) => {
          if (res.error || !res.result) {
            throw new Error(res.message || 'Failed to update item task');
          }
          return res.result as ReceiptItemTaskListItem;
        }),
      );
    } catch (error) {
      return throwError(() => error);
    }
  }

  /**
   * Helper method that routes the task update request to the appropriate service method
   * based on the specified task type.
   *
   * @param updateTask - Object containing the task details
   * @param updateTask.type - The type to update the task to (OK or NOK)
   * @param updateTask.taskId - The unique identifier of the task to update
   * @returns An Observable with the response from the receipt service
   * @throws Error when taskId is missing or when an invalid task type is provided
   * @private
   */
  private _updateTaskRequestHelper(updateTask: {
    type: Exclude<TaskActionTypeType, 'UNKNOWN' | 'RETOURE_UNKNOWN'>;
    taskId: number;
  }): Observable<ResponseArgsOfReceiptItemTaskListItemDTO> {
    if (!updateTask?.taskId) {
      return throwError(() => new Error('Task ID missing'));
    }

    if (isTaskType(updateTask.type, TaskActionTypes.OK)) {
      return this.#receiptService.ReceiptSetReceiptItemTaskToOK(
        updateTask.taskId,
      );
    } else if (isTaskType(updateTask.type, TaskActionTypes.NOK)) {
      return this.#receiptService.ReceiptSetReceiptItemTaskToNOK(
        updateTask.taskId,
      );
    }

    return throwError(() => new Error('Invalid task type'));
  }
}
