import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  OnDestroy,
  OnInit,
  signal,
  ViewChild,
} from '@angular/core';
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,
  PurchaseDetailItemResponse,
  PurchaseDetailResponse,
} from '../../../../core/services/checkout.service';
import { AuthService } from '../../../../core/services/auth/auth.service';
import { CartService } from '../../../../core/services/cart/cart.service';
import { BankAccount } from '../../../../core/services/tenant.interface';
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 {
  CheckoutForm,
  PaymentMethod,
  PaymentMethodOption,
  QrPaymentStatus,
  TransferAccount,
  TransferField,
  TransferValidationStatus,
} from './checkout-page.models';

@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 authService = inject(AuthService);
  private readonly cartService = inject(CartService);
  private readonly qrPollingIntervalMs = 5_000;
  private readonly qrPollingMaxAttempts = 9;

  private qrPollingTimeoutId: ReturnType<typeof setTimeout> | null = null;
  private qrPollingAttempts = 0;
  private qrPollingRunId = 0;
  private paymentMethodRequestId = 0;
  private navigationStarted = false;

  @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 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' },
    { id: 'telepagos', label: 'TelePagos' },
  ];
  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 isEditingItems = signal(false);
  protected readonly isUpdatingItem = signal(false);
  protected readonly isPreparingItemEdit = signal(false);
  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');
  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.queryParamMap.get('purchase'));
    if (!Number.isInteger(purchaseId) || purchaseId <= 0) {
      void this.router.navigate(['/']);
      return;
    }

    void this.loadPurchase(purchaseId);
  }

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

  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,
    };
  }

  protected async onEditingItemsChange(editing: boolean): Promise<void> {
    if (this.isUpdatingItem() || this.isPreparingItemEdit()) {
      return;
    }

    if (!editing) {
      this.isEditingItems.set(false);

      if (this.stepper?.currentStepIndex() === 1) {
        void this.selectPaymentMethod(this.selectedPaymentMethod());
      }

      return;
    }

    this.isEditingItems.set(true);
    this.stopQrPolling();
    this.qrData.set(null);
    this.qrPaymentStatus.set('idle');
    this.transferAccount.set(null);
    this.transferValidationStatus.set('idle');

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

    if (!tenant || !purchaseId) {
      this.isEditingItems.set(false);
      return;
    }

    this.isPreparingItemEdit.set(true);
    try {
      const purchase = await this.checkoutService.prepareItemEditing(tenant.codigo, purchaseId);
      this.createdPurchase.set(purchase);
    } catch (error) {
      console.error('Failed to prepare purchase item editing:', error);
      this.isEditingItems.set(false);
    } finally {
      this.isPreparingItemEdit.set(false);
    }
  }

  protected async onPurchaseItemQuantityChange(event: {
    item: CartItemMock;
    quantity: number;
  }): Promise<void> {
    const tenant = this.tenantService.tenant();
    const purchaseId = this.createdPurchaseId();
    const itemId = event.item.cartItemId;

    if (!tenant || !purchaseId || !itemId || this.isUpdatingItem() || !this.isEditingItems()) {
      return;
    }

    this.isUpdatingItem.set(true);
    try {
      const purchase = await this.checkoutService.updateItemQuantity(
        tenant.codigo,
        purchaseId,
        itemId,
        event.quantity,
      );
      this.createdPurchase.set(purchase);
    } catch (error) {
      console.error('Failed to update purchase item quantity:', error);
    } finally {
      this.isUpdatingItem.set(false);
    }
  }

  protected async onStep1Continue(): Promise<void> {
    if (this.form.invalid || this.isUpdatingPurchase() || this.isEditingItems()) 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.stepper.next();

      // Auto trigger intent for default option
      void this.selectPaymentMethod(this.selectedPaymentMethod());
    } catch (error) {
      console.error('Failed to create purchase:', error);
      // Here we could show an alert or toast
    } finally {
      this.isUpdatingPurchase.set(false);
    }
  }

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

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

    if (this.navigationStarted) {
      return true;
    }

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

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

    try {
      await this.checkoutService.cancelPurchase(tenant.codigo, purchaseId);
      this.createdPurchaseId.set(null);
      this.createdPurchase.set(null);

      try {
        await firstValueFrom(this.cartService.loadCart());
      } catch (error) {
        console.error('Failed to restore cart after cancelling checkout:', error);
      }

      return true;
    } catch (error) {
      console.error('Failed to cancel purchase:', error);
      return false;
    }
  }

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

    this.stopQrPolling();
    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 || method === 'telepagos') {
      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);
    } finally {
      this.isGeneratingIntent.set(false);
    }
  }

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

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

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

    this.transferDni.set(dni);
    this.transferValidationStatus.set('idle');
    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);
    } 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.isEditingItems() ||
      this.transferValidationStatus() === 'checking'
    ) {
      return;
    }

    this.transferValidationStatus.set('checking');

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

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

      this.transferValidationStatus.set('error');
    } catch (error) {
      console.error('Failed to submit purchase for review:', error);
      this.transferValidationStatus.set('error');
    }
  }

  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;
      }

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

      if (purchase.status === 'rejected' || purchase.status === 'cancelled') {
        this.stopQrPolling();
        this.qrPaymentStatus.set('failed');
        return;
      }
    } catch (error) {
      console.error('Failed to validate QR payment:', error);
    } 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();

    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;
    }

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

      if (
        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.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);
      void this.router.navigate(['/']);
    }
  }
}
