import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  OnDestroy,
  OnInit,
  signal,
  ViewChild,
} from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { firstValueFrom, startWith } from 'rxjs';

import { TenantService } from '../../../../core/services/tenant.service';
import {
  CheckoutService,
  PurchasePaymentCandidateReason,
  PurchaseDetailItemResponse,
  PurchaseDetailResponse,
  PurchaseStatusResponse,
} from '../../../../core/services/checkout.service';
import { AuthService } from '../../../../core/services/auth/auth.service';
import { GlobalLoadingService } from '../../../../core/services/global-loading/global-loading.service';
import { ToastService } from '../../../../core/services/toast.service';
import { CartService } from '../../../../core/services/cart/cart.service';
import { CartComponent, CartItemMock } from '../../../../shared/components/cart/cart.component';
import { StepComponent } from '../../../../shared/components/stepper/step.component';
import { StepperComponent } from '../../../../shared/components/stepper/stepper.component';
import { CheckoutDataStepComponent } from './checkout-data-step.component';
import { CheckoutPaymentStepComponent } from './checkout-payment-step.component';
import { CheckoutCountdownService } from '../../../../core/services/checkout-countdown.service';
import {
  CheckoutForm,
  PaymentMethod,
  PaymentMethodOption,
  QrPaymentStatus,
  TransferAccount,
  TransferField,
  TransferValidationStatus,
} from './checkout-page.models';

interface ApiErrorResponse {
  code?: string;
  message?: string;
  errors?: Record<string, string[]>;
}

@Component({
  selector: 'app-checkout-page',
  standalone: true,
  imports: [
    CartComponent,
    StepperComponent,
    StepComponent,
    CheckoutDataStepComponent,
    CheckoutPaymentStepComponent,
  ],
  templateUrl: './checkout-page.component.html',
  styleUrl: './checkout-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckoutPageComponent implements OnInit, OnDestroy {
  private readonly formBuilder = inject(FormBuilder);
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly tenantService = inject(TenantService);
  private readonly checkoutService = inject(CheckoutService);
  private readonly checkoutCountdownService = inject(CheckoutCountdownService);
  private readonly authService = inject(AuthService);
  private readonly globalLoadingService = inject(GlobalLoadingService);
  private readonly toastService = inject(ToastService);
  private readonly cartService = inject(CartService);
  private readonly qrPollingIntervalMs = 5_000;
  private readonly qrPollingMaxAttempts = 120;
  private readonly transferPollingIntervalMs = 3_000;
  private readonly transferPollingMaxAttempts = 20;

  private qrPollingTimeoutId: ReturnType<typeof setTimeout> | null = null;
  private qrPollingAttempts = 0;
  private qrPollingRunId = 0;
  private transferPollingTimeoutId: ReturnType<typeof setTimeout> | null = null;
  private transferPollingAttempts = 0;
  private transferPollingRunId = 0;
  private paymentMethodRequestId = 0;
  private navigationStarted = false;
  private cancelPurchasePromise: Promise<boolean> | null = null;

  @ViewChild(StepperComponent) stepper!: StepperComponent;

  protected readonly form: CheckoutForm = this.formBuilder.nonNullable.group({
    nombre: ['', [Validators.required]],
    email: ['', [Validators.required, Validators.email]],
    dni: ['', [Validators.required]],
    telefono: ['', [Validators.required]],
  });

  protected readonly createdPurchase = signal<PurchaseDetailResponse | null>(null);
  protected readonly isLoadingPurchase = signal(true);
  protected readonly checkoutStepIndex = signal(0);
  protected readonly checkoutRemainingTime = computed(() => {
    const purchase = this.createdPurchase();

    if (
      !purchase ||
      (typeof purchase.expires_at !== 'string' && typeof purchase.expires_in_seconds !== 'number')
    ) {
      return null;
    }

    const remainingSeconds = this.checkoutCountdownService.remainingSeconds();

    if (remainingSeconds === null) {
      return null;
    }

    const minutes = Math.floor(remainingSeconds / 60);
    const seconds = remainingSeconds % 60;

    return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  });
  protected readonly cartSubtotal = computed(() => {
    const purchase = this.createdPurchase();
    return purchase ? parseFloat(purchase.subtotal) : 0;
  });

  protected readonly cartDiscount = computed(() => 0);

  protected readonly cartTotal = computed(() => {
    const purchase = this.createdPurchase();
    return purchase ? parseFloat(purchase.total) : this.cartSubtotal() - this.cartDiscount();
  });

  protected readonly mappedCartItems = computed<CartItemMock[]>(() => {
    const purchase = this.createdPurchase();
    return purchase ? purchase.items.map((item) => this.mapPurchaseItemToMock(item)) : [];
  });

  protected readonly isStep1Valid = signal(this.form.valid);
  protected readonly paymentMethods: ReadonlyArray<PaymentMethodOption> = [
    { id: 'qr', label: 'QR' },
    { id: 'transfer', label: 'Transferencia' },
  ];
  protected readonly selectedPaymentMethod = signal<PaymentMethod>('qr');
  protected readonly copiedTransferField = signal<TransferField | null>(null);
  protected readonly transferAccount = signal<TransferAccount | null>(null);
  protected readonly transferDni = signal<string>('');

  protected readonly isUpdatingPurchase = signal(false);
  protected readonly isCancellingPurchase = signal(false);
  protected readonly hasSubmittedTransfer = signal(false);
  protected readonly isPurchaseModificationDisabled = computed(
    () => this.isCancellingPurchase() || this.hasSubmittedTransfer(),
  );
  protected readonly createdPurchaseId = signal<number | null>(null);
  protected readonly isGeneratingIntent = signal(false);
  protected readonly isPaymentLoading = computed(() => this.isGeneratingIntent());
  protected readonly qrData = signal<string | null>(null);
  protected readonly qrPaymentStatus = signal<QrPaymentStatus>('idle');
  protected readonly isCheckingQrPayment = signal(false);
  protected readonly transferValidationStatus = signal<TransferValidationStatus>('idle');
  private readonly transferPrimaryCandidateReason = signal<PurchasePaymentCandidateReason | null>(
    null,
  );
  protected readonly transferVerificationErrorTitle = computed(() =>
    this.transferPrimaryCandidateReason() === 'exact_amount_near_dni'
      ? 'El DNI no corresponde con el de la transferencia'
      : null,
  );
  protected readonly whatsappUrl = computed(
    () =>
      this.tenantService
        .tenant()
        ?.social_media?.find((socialMedia) => socialMedia.code === 'whatsapp')?.url ?? null,
  );

  constructor() {
    this.form.statusChanges
      .pipe(startWith(this.form.status))
      .subscribe(() => this.isStep1Valid.set(this.form.valid));

    const user = this.authService.user();
    if (user) {
      this.form.patchValue({
        nombre: user.nombre_apellido,
        email: user.email,
        dni: user.dni ?? '',
        telefono: user.telefono ?? '',
      });
      this.form.controls.nombre.disable();
      this.form.controls.email.disable();
    }
  }

  ngOnInit(): void {
    const purchaseId = Number(this.route.snapshot.paramMap.get('id'));
    if (!Number.isInteger(purchaseId) || purchaseId <= 0) {
      void this.router.navigate(['/']);
      return;
    }

    void this.loadPurchase(purchaseId);
  }

  ngOnDestroy(): void {
    this.stopQrPolling();
    this.stopTransferPolling();
  }

  private mapPurchaseItemToMock(item: PurchaseDetailItemResponse): CartItemMock {
    return {
      cartItemId: item.id,
      imageUrl: item.item_details.imagen,
      product: item.item_details.nombre,
      originalPrice: null,
      discountedPrice: parseFloat(item.unit_price),
      discountPercentage: null,
      attributes: item.item_details.attributes.map((attribute) => ({
        label: attribute.name,
        value: attribute.value === null ? '' : String(attribute.value),
      })),
      quantity: item.quantity,
      variantId: item.source_variant_id,
    };
  }

  protected async onModifyPurchase(): Promise<void> {
    if (this.isPurchaseModificationDisabled()) {
      return;
    }

    this.globalLoadingService.start();
    try {
      const cancelled = await this.cancelCurrentPurchase();
      if (!cancelled) return;

      await this.router.navigate(['/'], {
        queryParams: { openCart: true },
      });
    } finally {
      this.globalLoadingService.stop();
    }
  }

  protected async onStep1Continue(): Promise<void> {
    if (this.form.invalid || this.isUpdatingPurchase()) return;

    const tenant = this.tenantService.tenant();
    const purchaseId = this.createdPurchaseId();
    if (!tenant || !purchaseId) return;

    this.isUpdatingPurchase.set(true);

    try {
      const formValue = this.form.getRawValue();
      const purchase = await this.checkoutService.updateCustomerData(tenant.codigo, purchaseId, {
        dni: formValue.dni,
        telefono: formValue.telefono,
        email: formValue.email,
        nombre_apellido: formValue.nombre,
      });
      this.createdPurchase.set(purchase);
      this.checkoutCountdownService.synchronize(purchase);

      this.stepper.next();

      // Auto trigger intent for default option
      void this.selectPaymentMethod(this.selectedPaymentMethod());
    } catch (error) {
      console.error('Failed to create purchase:', error);
      this.showRequestError(error, 'No se pudieron actualizar los datos de la compra.');
    } finally {
      this.isUpdatingPurchase.set(false);
    }
  }

  protected async onCancel(): Promise<void> {
    void this.router.navigate(['/']);
  }

  public async canDeactivate(): Promise<boolean> {
    this.stopQrPolling();
    this.stopTransferPolling();

    if (this.cancelPurchasePromise) {
      return this.cancelPurchasePromise;
    }

    if (this.navigationStarted) {
      return true;
    }

    this.globalLoadingService.start();
    try {
      return await this.cancelCurrentPurchase();
    } finally {
      this.globalLoadingService.stop();
    }
  }

  private cancelCurrentPurchase(): Promise<boolean> {
    if (this.cancelPurchasePromise) {
      return this.cancelPurchasePromise;
    }

    this.cancelPurchasePromise = this.performPurchaseCancellation().finally(() => {
      this.cancelPurchasePromise = null;
    });

    return this.cancelPurchasePromise;
  }

  private async performPurchaseCancellation(): Promise<boolean> {
    const tenant = this.tenantService.tenant();
    const purchaseId = this.createdPurchaseId();
    if (!tenant || !purchaseId) {
      this.checkoutCountdownService.clear();
      return true;
    }

    this.isCancellingPurchase.set(true);

    try {
      await this.checkoutService.cancelPurchase(tenant.codigo, purchaseId);
      await firstValueFrom(this.cartService.loadCart());
      this.createdPurchaseId.set(null);
      this.createdPurchase.set(null);
      this.checkoutCountdownService.clear();

      this.navigationStarted = true;
      return true;
    } catch (error) {
      console.error('Failed to cancel the current purchase:', error);

      if (this.isStockReservationExpiredError(error)) {
        this.showRequestError(error, 'La reserva de stock venció.');
        this.cartService.loadCart(true).subscribe({
          error: (refreshError) => console.error('Error refreshing expired cart', refreshError),
        });
        this.createdPurchaseId.set(null);
        this.createdPurchase.set(null);
        this.checkoutCountdownService.clear();
        this.navigationStarted = true;
        return true;
      }

      this.showRequestError(error, 'No se pudo cancelar la compra.');
      return false;
    } finally {
      this.isCancellingPurchase.set(false);
    }
  }

  protected async selectPaymentMethod(method: PaymentMethod): Promise<void> {
    if (this.navigationStarted || this.hasSubmittedTransfer()) {
      return;
    }

    this.stopQrPolling();
    this.stopTransferPolling();
    this.qrPaymentStatus.set('idle');
    this.transferValidationStatus.set('idle');
    this.selectedPaymentMethod.set(method);
    const requestId = ++this.paymentMethodRequestId;

    if (method === 'qr') {
      this.qrData.set(null);
    }

    const purchaseId = this.createdPurchaseId();
    const tenant = this.tenantService.tenant();

    if (!purchaseId || !tenant) {
      return;
    }

    if (method === 'transfer') {
      this.transferAccount.set(null);
      return;
    }

    this.isGeneratingIntent.set(true);

    try {
      const response = await this.checkoutService
        .withCustomLoading()
        .generatePaymentIntent(tenant.codigo, purchaseId, method);

      if (method === 'qr' && response.qr_data?.qr_code) {
        if (requestId !== this.paymentMethodRequestId || this.selectedPaymentMethod() !== 'qr') {
          return;
        }

        this.qrData.set(response.qr_data.qr_code);
        this.markPurchasePendingPayment();
        this.startQrPolling();
      }
    } catch (error) {
      console.error('Failed to generate payment intent:', error);
      this.showRequestError(error, 'No se pudo generar el pago.');
    } finally {
      this.isGeneratingIntent.set(false);
    }
  }

  protected async generateTransferIntent(dni: string): Promise<void> {
    if (this.hasSubmittedTransfer()) {
      return;
    }

    const purchaseId = this.createdPurchaseId();
    const tenant = this.tenantService.tenant();

    if (!purchaseId || !tenant) {
      return;
    }

    this.transferDni.set(dni);
    this.stopTransferPolling();
    this.transferValidationStatus.set('idle');
    this.transferPrimaryCandidateReason.set(null);
    this.isGeneratingIntent.set(true);
    try {
      const response = await this.checkoutService
        .withCustomLoading()
        .generatePaymentIntent(tenant.codigo, purchaseId, 'transfer', dni);

      if (response.transfer_data) {
        this.markPurchasePendingPayment();
        this.transferAccount.set({
          titular: response.transfer_data.titular,
          entidad: response.transfer_data.entidad,
          cvu: response.transfer_data.cvu,
          alias: response.transfer_data.alias,
        });
      }
    } catch (error) {
      console.error('Failed to generate transfer payment intent:', error);
      this.showRequestError(error, 'No se pudo generar el pago por transferencia.');
    } finally {
      this.isGeneratingIntent.set(false);
    }
  }

  protected async copyTransferValue(field: TransferField, value: string): Promise<void> {
    if (!globalThis.navigator?.clipboard?.writeText) return;

    try {
      await globalThis.navigator.clipboard.writeText(value);
      this.copiedTransferField.set(field);

      setTimeout(() => {
        if (this.copiedTransferField() === field) {
          this.copiedTransferField.set(null);
        }
      }, 1800);
    } catch {
      this.copiedTransferField.set(null);
    }
  }

  protected async onComplete(): Promise<void> {
    const purchaseId = this.createdPurchaseId();
    const tenant = this.tenantService.tenant();

    if (
      !purchaseId ||
      !tenant ||
      this.navigationStarted ||
      this.transferValidationStatus() === 'checking'
    ) {
      return;
    }

    this.stopTransferPolling();
    this.hasSubmittedTransfer.set(true);
    this.transferValidationStatus.set('checking');
    this.transferPrimaryCandidateReason.set(null);
    this.transferPollingAttempts = 0;

    const runId = this.transferPollingRunId;

    try {
      const purchase = await this.checkoutService
        .withCustomLoading()
        .submitPurchaseForReview(tenant.codigo, purchaseId);

      if (runId !== this.transferPollingRunId) {
        return;
      }

      this.checkoutCountdownService.synchronize(purchase);
      this.captureTransferCandidateReason(purchase);

      if (purchase.status === 'paid') {
        this.navigateToPurchaseStatus(purchaseId);
        return;
      }

      this.scheduleTransferPoll(runId);
    } catch (error) {
      const expired = this.handleCheckoutError(
        error,
        'No se pudo enviar el pago para su validación.',
      );

      if (!expired && runId === this.transferPollingRunId) {
        this.transferValidationStatus.set('error');
      }
    }
  }

  private scheduleTransferPoll(runId: number): void {
    this.transferPollingTimeoutId = setTimeout(() => {
      this.transferPollingTimeoutId = null;
      void this.checkTransferPayment(runId);
    }, this.transferPollingIntervalMs);
  }

  private async checkTransferPayment(runId: number): Promise<void> {
    if (runId !== this.transferPollingRunId) {
      return;
    }

    const purchaseId = this.createdPurchaseId();
    const tenant = this.tenantService.tenant();

    if (!purchaseId || !tenant || this.selectedPaymentMethod() !== 'transfer') {
      this.stopTransferPolling();
      return;
    }

    this.transferPollingAttempts += 1;

    try {
      const purchase = await this.checkoutService
        .withCustomLoading()
        .getPurchase(tenant.codigo, purchaseId);

      if (runId !== this.transferPollingRunId) {
        return;
      }

      this.checkoutCountdownService.synchronize(purchase);
      this.captureTransferCandidateReason(purchase);

      if (purchase.status === 'paid') {
        this.navigateToPurchaseStatus(purchaseId);
        return;
      }

      if (purchase.status === 'expired') {
        this.navigateToPurchaseStatus(purchaseId);
        return;
      }
    } catch (error) {
      console.error('Failed to validate transfer payment:', error);
      if (this.isPurchaseExpiredError(error)) {
        this.showRequestError(error, 'La compra venció. Iniciá una nueva compra.');
        return;
      }
    }

    if (runId !== this.transferPollingRunId) {
      return;
    }

    if (this.transferPollingAttempts >= this.transferPollingMaxAttempts) {
      this.stopTransferPolling();
      this.transferValidationStatus.set('error');
      return;
    }

    this.scheduleTransferPoll(runId);
  }

  private captureTransferCandidateReason(purchase: PurchaseStatusResponse): void {
    const reason = purchase.payment_verification?.primary?.reason;

    if (reason) {
      this.transferPrimaryCandidateReason.set(reason);
    }
  }

  private stopTransferPolling(): void {
    this.transferPollingRunId += 1;

    if (this.transferPollingTimeoutId !== null) {
      clearTimeout(this.transferPollingTimeoutId);
      this.transferPollingTimeoutId = null;
    }
  }

  protected retryQrPolling(): void {
    if (this.selectedPaymentMethod() === 'qr' && this.qrData()) {
      this.startQrPolling();
    }
  }

  private startQrPolling(): void {
    this.stopQrPolling();
    this.qrPollingAttempts = 0;
    this.qrPaymentStatus.set('waiting');

    const runId = this.qrPollingRunId;
    this.scheduleQrPoll(runId);
  }

  private scheduleQrPoll(runId: number): void {
    this.qrPollingTimeoutId = setTimeout(() => {
      this.qrPollingTimeoutId = null;
      void this.checkQrPayment(runId);
    }, this.qrPollingIntervalMs);
  }

  private async checkQrPayment(runId: number): Promise<void> {
    if (runId !== this.qrPollingRunId) {
      return;
    }

    const purchaseId = this.createdPurchaseId();
    const tenant = this.tenantService.tenant();

    if (!purchaseId || !tenant || this.selectedPaymentMethod() !== 'qr') {
      this.stopQrPolling();
      return;
    }

    this.qrPollingAttempts += 1;
    this.isCheckingQrPayment.set(true);

    try {
      const purchase = await this.checkoutService
        .withCustomLoading()
        .getPurchase(tenant.codigo, purchaseId);

      if (runId !== this.qrPollingRunId) {
        return;
      }

      this.checkoutCountdownService.synchronize(purchase);

      if (purchase.status === 'paid') {
        this.handleConfirmedPayment(purchaseId);
        return;
      }

      if (purchase.status === 'rejected' || purchase.status === 'cancelled') {
        this.stopQrPolling();
        this.qrPaymentStatus.set('failed');
        return;
      }

      if (purchase.status === 'expired') {
        this.navigateToPurchaseStatus(purchaseId);
        return;
      }
    } catch (error) {
      console.error('Failed to validate QR payment:', error);
      if (this.isPurchaseExpiredError(error)) {
        this.showRequestError(error, 'La compra venció. Iniciá una nueva compra.');
        return;
      }
    } finally {
      if (runId === this.qrPollingRunId) {
        this.isCheckingQrPayment.set(false);
      }
    }

    if (runId !== this.qrPollingRunId) {
      return;
    }

    if (this.qrPollingAttempts >= this.qrPollingMaxAttempts) {
      this.stopQrPolling();
      this.qrPaymentStatus.set('timed_out');
      return;
    }

    this.scheduleQrPoll(runId);
  }

  private stopQrPolling(): void {
    this.qrPollingRunId += 1;
    this.isCheckingQrPayment.set(false);

    if (this.qrPollingTimeoutId !== null) {
      clearTimeout(this.qrPollingTimeoutId);
      this.qrPollingTimeoutId = null;
    }
  }

  private markPurchasePendingPayment(): void {
    this.createdPurchase.update((purchase) =>
      purchase
        ? {
            ...purchase,
            status: 'pending_payment',
          }
        : purchase,
    );
  }

  private handleConfirmedPayment(purchaseId: number): void {
    this.navigateToPurchaseStatus(purchaseId);
  }

  private navigateToPurchaseStatus(purchaseId: number): void {
    if (this.navigationStarted) {
      return;
    }

    this.navigationStarted = true;
    this.stopQrPolling();
    this.stopTransferPolling();
    this.checkoutCountdownService.clear();

    void this.router.navigate(['/checkout/status', purchaseId]);
  }

  private async loadPurchase(purchaseId: number): Promise<void> {
    const tenant = this.tenantService.tenant();
    if (!tenant) {
      void this.router.navigate(['/']);
      return;
    }

    this.createdPurchaseId.set(purchaseId);

    try {
      const purchase = await this.checkoutService
        .withCustomLoading()
        .getPurchase(tenant.codigo, purchaseId);

      if (
        (purchase.status === 'pending_payment' && purchase.expires_at === null) ||
        purchase.status === 'in_review' ||
        purchase.status === 'paid' ||
        purchase.status === 'cancelled' ||
        purchase.status === 'rejected' ||
        purchase.status === 'expired'
      ) {
        this.navigateToPurchaseStatus(purchaseId);
        return;
      }

      if (purchase.status !== 'created' && purchase.status !== 'pending_payment') {
        void this.router.navigate(['/']);
        return;
      }

      this.createdPurchaseId.set(purchase.id);
      this.createdPurchase.set(purchase);
      this.checkoutCountdownService.synchronize(purchase);
      this.checkoutStepIndex.set(purchase.status === 'pending_payment' ? 1 : 0);

      if (
        purchase.status === 'pending_payment' &&
        this.paymentMethods.some((method) => method.id === purchase.payment_method)
      ) {
        this.selectedPaymentMethod.set(purchase.payment_method as PaymentMethod);
        this.transferDni.set(purchase.transfer_payer_dni ?? '');
      }

      this.isLoadingPurchase.set(false);

      if (purchase.status === 'pending_payment' && purchase.payment_method === 'qr') {
        await this.selectPaymentMethod('qr');
      }
    } catch (error) {
      console.error('Failed to load purchase:', error);
      const expired = this.showRequestError(error, 'No se pudo cargar la compra.');
      if (!expired) {
        void this.router.navigate(['/']);
      }
    }
  }

  private showRequestError(error: unknown, fallbackMessage: string): boolean {
    const payload =
      typeof error === 'object' && error !== null && 'error' in error
        ? (error as { error?: ApiErrorResponse }).error
        : undefined;
    const message =
      typeof payload?.message === 'string' && payload.message.trim()
        ? payload.message
        : fallbackMessage;

    this.toastService.danger(message);

    if (payload?.code === 'purchase.expired') {
      this.navigateToExpiredPurchaseStatus();
      return true;
    }

    return false;
  }

  private handleCheckoutError(error: unknown, fallbackMessage: string): boolean {
    if (!(error instanceof HttpErrorResponse)) {
      this.toastService.danger(fallbackMessage);
      return false;
    }

    const response = error.error as ApiErrorResponse | null;

    if (response?.code === 'purchase.expired' || this.hasExpiredPurchase()) {
      this.stopQrPolling();
      this.stopTransferPolling();
      this.toastService.danger(
        response?.code === 'purchase.expired' && response.message
          ? response.message
          : 'La compra venció. Iniciá una nueva compra.',
      );
      this.navigateToExpiredPurchaseStatus();
      return true;
    }

    const validationMessage = response?.errors
      ? Object.values(response.errors).flat().find(Boolean)
      : undefined;

    this.toastService.danger(validationMessage ?? response?.message ?? fallbackMessage);
    return false;
  }

  private navigateToExpiredPurchaseStatus(): void {
    const purchaseId = this.createdPurchaseId() ?? this.createdPurchase()?.id;

    if (purchaseId) {
      if (this.navigationStarted) {
        return;
      }

      this.navigationStarted = true;
      this.stopQrPolling();
      this.stopTransferPolling();
      this.checkoutCountdownService.clear();
      void this.router.navigate(['/checkout/status', purchaseId], {
        queryParams: { status: 'expired' },
      });
      return;
    }

    void this.router.navigate(['/']);
  }

  private isPurchaseExpiredError(error: unknown): boolean {
    if (typeof error !== 'object' || error === null || !('error' in error)) {
      return false;
    }

    return (error as { error?: ApiErrorResponse }).error?.code === 'purchase.expired';
  }

  private isStockReservationExpiredError(error: unknown): boolean {
    if (typeof error !== 'object' || error === null || !('error' in error)) {
      return false;
    }

    return (error as { error?: ApiErrorResponse }).error?.code === 'stock_reservation.expired';
  }

  private hasExpiredPurchase(): boolean {
    const purchase = this.createdPurchase();

    if (!purchase) {
      return false;
    }

    if (purchase.status === 'expired') {
      return true;
    }

    if (!purchase.expires_at) {
      return false;
    }

    return Date.parse(purchase.expires_at) <= Date.now();
  }
}
