import {
  ChangeDetectionStrategy,
  Component,
  computed,
  effect,
  inject,
  signal,
  untracked,
  WritableSignal,
} from '@angular/core';
import { ButtonComponent } from '@isa/ui/buttons';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaActionChevronLeft } from '@isa/icons';
import {
  EligibleForReturnState,
  ReturnCanReturnService,
  ReturnProcess,
  ReturnProcessService,
  ReturnProcessStore,
} from '@isa/oms/data-access';
import { injectTabId } from '@isa/core/tabs';
import { ReturnProcessItemComponent } from './return-process-item/return-process-item.component';
import { Location } from '@angular/common';
import { RouterLink } from '@angular/router';
import { logger, provideLoggerContext } from '@isa/core/logging';

/**
 * Component responsible for displaying the return process steps for a given process ID.
 * It allows users to specify the condition and reason for returning items within a process.
 */
@Component({
  selector: 'oms-feature-return-process',
  templateUrl: './return-process.component.html',
  styleUrls: ['./return-process.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    ButtonComponent,
    NgIconComponent,
    ReturnProcessItemComponent,
    RouterLink,
  ],
  providers: [
    provideIcons({ isaActionChevronLeft }),
    provideLoggerContext({ component: 'ReturnProcessComponent' }),
  ],
})
export class ReturnProcessComponent {
  /** Provides access to the browser's location for navigation tasks like going back. */
  location = inject(Location);

  /** Manages the state for return process entities. */
  #returnProcessStore = inject(ReturnProcessStore);

  /** Provides methods for interacting with return process data and logic. */
  #returnProcessService = inject(ReturnProcessService);

  /** Logger instance specific to this component context. */
  #logger = logger();

  /** Signal emitting the numeric ID of the currently active return process, derived from the route parameters. Null if no ID is present. */
  processId = injectTabId();

  #returnCanReturnService = inject(ReturnCanReturnService);

  /**
   * Computed signal that filters return processes from the store
   * based on the current `processId`. Returns an empty array if
   * `processId` is not available.
   */
  returnProcesses = computed<ReturnProcess[]>(() => {
    const processId = this.processId();

    if (!processId) {
      this.#logger.warn('No process id found');
      return [];
    }

    // Ensure entities() returns an array before filtering
    const entities = this.#returnProcessStore.entities() ?? [];
    return entities.filter(
      (process: ReturnProcess) => process.processId === processId,
    );
  });

  /**
   * Determines whether the user can proceed to the summary page in the return process flow.
   *
   * This computed property evaluates multiple conditions:
   * 1. Verifies that there are return processes available
   * 2. Ensures all processes are eligible for return (have EligibleForReturnState.Eligible)
   * 3. Confirms that all required questions have been completely answered for each process
   *
   * @returns {boolean} True if all return processes are eligible and have all questions answered,
   *                    false otherwise
   */
  canContinueToSummary = computed(() => {
    const processes = this.returnProcesses();
    // Return false immediately if there are no processes
    if (processes.length === 0) {
      return false;
    }

    // Check if any process is eglibile for return
    const isEligible = processes.some((p) => {
      return (
        this.#returnProcessService.eligibleForReturn(p)?.state ===
        EligibleForReturnState.Eligible
      );
    });

    // Check if every process has its questions fully answered
    return processes.every((p) => {
      const progress =
        this.#returnProcessService.returnProcessQuestionsProgress(p);
      // Ensure progress exists and answered equals total
      return (
        progress != null && progress.answered === progress.total && isEligible
      );
    });
  });

  /**
   * Signal indicating whether all return processes can be returned.
   * Updated asynchronously based on the result of canReturn checks for each process.
   */
  canReturn: WritableSignal<boolean> = signal(false);

  /**
   * Initializes the component and sets up an effect to determine if all return processes
   * are eligible for return. The effect observes changes to `returnProcesses` and updates
   * the `canReturn` signal accordingly.
   *
   * The logic:
   * - Resets `canReturn` to undefined on each effect run.
   * - For each process, asynchronously checks if it can be returned using `ReturnCanReturnService`.
   * - If all processes return a positive result (`result === true`), sets `canReturn` to true.
   * - Handles empty or missing process lists gracefully.
   *
   * @remarks
   * - Uses `untracked` to avoid tracking the async block in the effect's dependency graph.
   * - Ensures type safety and avoids unnecessary state updates.
   * - Logs errors if the canReturn check fails for any process.
   */
  constructor() {
    effect(() => {
      const processes = this.returnProcesses();
      this.canReturn.set(false);

      // Avoid tracking the async block in the effect's dependency graph
      untracked(async () => {
        if (!Array.isArray(processes) || processes.length === 0) {
          return;
        }

        const canReturnResults: boolean[] = [];
        for (const returnProcess of processes) {
          try {
            const canReturnResponse =
              await this.#returnCanReturnService.canReturn(returnProcess);

            canReturnResults.push(Boolean(canReturnResponse?.result));
          } catch (error) {
            this.#logger.error(
              'Failed to check canReturn for process',
              error as Error,
              () => ({
                processId: returnProcess.processId,
              }),
            );
            canReturnResults.push(false);
          }
        }

        if (
          canReturnResults.length === processes.length &&
          canReturnResults.every(Boolean)
        ) {
          this.canReturn.set(true);
        } else {
          this.canReturn.set(false);
        }
      });
    });
  }
}
