import { Component, ChangeDetectionStrategy, signal, OnDestroy, inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { CartService, ApiService, LanguageService } from '../../services';
import { AuthService } from '@marketplaces/auth';
import { Item, CartItem, DeliveryOption } from '../../models';
import { EMPTY, interval, of, Subscription } from 'rxjs';
import { catchError, exhaustMap, take, timeout } from 'rxjs/operators';
import { DeliverySelectorComponent } from '../../components/delivery-selector/delivery-selector.component';
import { TelegramLoginComponent } from '../../components/telegram-login/telegram-login.component';
import { getDiscountedPrice, getMainImage, trackByItemId, getBadgeClass, getTranslatedField, onImageError } from '../../utils/item.utils';
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
import { TranslatePipe } from '../../i18n/translate.pipe';
import { TranslateService } from '../../i18n/translate.service';
import { PAYMENT_POLL_INTERVAL_MS, PAYMENT_MIN_POLL_SECONDS, PAYMENT_TIMEOUT_CLOSE_MS, LINK_COPIED_DURATION_MS } from '../../config/constants';
import { IconComponent } from '../../shared/ui/icon/icon.component';
import { EmptyStateComponent } from '../../shared/ui/empty-state/empty-state.component';
import { ButtonComponent } from '../../shared/ui/button/button.component';
import { AnalyticsService } from '../../core/analytics/services/analytics.service';
import { UserNotificationService } from '../../features/website/user-experience/services/user-notification.service';
import { ConfirmDialogComponent } from '../../shared/ui/confirm-dialog/confirm-dialog.component';
import { DialogComponent } from '../../shared/ui/dialog/dialog.component';
import { CurrencyConvertPipe } from '../../pipes/currency-convert.pipe';
import { CurrencyRatesService } from '../../services/currency-rates.service';
import { MARKETPLACES_PAYMENT_GATEWAY, PaymentAttempt, PaymentMethod as PackagePaymentMethod } from '@marketplaces/payment';

type PaymentMethod = 'qr' | 'card';

@Component({
  selector: 'app-cart',
  imports: [DecimalPipe, RouterLink, FormsModule, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe, IconComponent, EmptyStateComponent, ButtonComponent, ConfirmDialogComponent, DialogComponent, CurrencyConvertPipe],
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CartComponent implements OnDestroy {
  items;
  itemCount;
  totalPrice;
  totalDeliveryPrice;
  totalWithDelivery;
  hasDeliveryPrice;
  allRequiredDeliveriesSelected;
  termsAccepted = false;
  
  private i18n = inject(TranslateService);
  private authService = inject(AuthService);
  private notifications = inject(UserNotificationService);
  
  isAuthenticated = this.authService.isAuthenticated;

  // Swipe state
  swipedItemId = signal<number | null>(null);

  // Payment popup states
  showPaymentPopup = signal<boolean>(false);
  paymentStatus = signal<'creating' | 'waiting' | 'success' | 'timeout' | 'error' | null>('creating');
  qrCodeUrl = signal<string>('');
  paymentUrl = signal<string>('');
  bankPaymentUrl = signal<string>('');
  bankPaymentFrameUrl = signal<SafeResourceUrl | null>(null);
  showBankPaymentPopup = signal<boolean>(false);
  selectedPaymentMethod = signal<PaymentMethod>('qr');
  paymentId = signal<string>('');
  linkCopied = signal<boolean>(false);
  // FH-1.4: one key per checkout attempt, generated when the popup opens and
  // reused across retryPayment() - a double-click can't reach here twice
  // (checkoutInFlight guards the entry point above), and a retry after a
  // failed attempt must not read as a second payment for the same cart.
  private checkoutIdempotencyKey = '';
  
  // Email collection after successful payment
  userEmail = signal<string>('');
  userPhone = signal<string>('');
  emailTouched = signal<boolean>(false);
  phoneTouched = signal<boolean>(false);
  emailError = signal<string>('');
  phoneError = signal<string>('');
  emailSubmitting = signal<boolean>(false);
  purchaseSubmitted = signal<boolean>(false);
  paidItems: CartItem[] = [];
  
  maxChecks = Math.ceil(PAYMENT_MIN_POLL_SECONDS / (PAYMENT_POLL_INTERVAL_MS / 1000));
  private pollingSubscription?: Subscription;
  private closeTimeout?: ReturnType<typeof setTimeout>;

  private currencyRates = inject(CurrencyRatesService);
  private readonly analytics = inject(AnalyticsService);
  /**
   * Payment creation and status polling now go through @marketplaces/payment
   * (POST/GET {qrApiUrl}/api/v1/payments) instead of api.service.ts's
   * createPaymentIntent/checkCartPaymentStatus - that endpoint pair is now
   * superseded, see the comment on createPaymentIntent() below. Only the I/O
   * layer changed; the surrounding popup state machine (paymentStatus,
   * checkoutInFlight, the bank-iframe UX, timeout/success handling) is
   * untouched and stays hand-rolled - <mp-payment>'s own UI is a different,
   * simpler paradigm (window.open for redirects, no iframe) that would be a
   * separate, much larger change to adopt wholesale.
   */
  private readonly paymentGateway = inject(MARKETPLACES_PAYMENT_GATEWAY);

  constructor(
    private cartService: CartService,
    private apiService: ApiService,
    private router: Router,
    private langService: LanguageService,
    private sanitizer: DomSanitizer
  ) {
    this.items = this.cartService.items;
    this.itemCount = this.cartService.itemCount;
    this.totalPrice = this.cartService.totalPrice;
    this.totalDeliveryPrice = this.cartService.totalDeliveryPrice;
    this.totalWithDelivery = this.cartService.totalWithDelivery;
    this.hasDeliveryPrice = this.cartService.hasDeliveryPrice;
    this.allRequiredDeliveriesSelected = this.cartService.allRequiredDeliveriesSelected;
  }

  requestLogin(): void {
    this.authService.requestLogin();
  }

  ngOnDestroy(): void {
    this.stopPolling();
    if (this.closeTimeout) {
      clearTimeout(this.closeTimeout);
    }
  }

  removeItem(item: CartItem): void {
    this.cartService.removeItem(item.itemID, this.cartVariant(item));
    this.swipedItemId.set(null);
  }

  updateQuantity(item: CartItem, quantity: number): void {
    this.cartService.updateQuantity(item.itemID, quantity, this.cartVariant(item));
  }

  increaseQuantity(item: CartItem): void {
    this.updateQuantity(item, item.quantity + 1);
  }

  decreaseQuantity(item: CartItem): void {
    if (item.quantity <= 1) {
      this.removeItem(item);
    } else {
      this.updateQuantity(item, item.quantity - 1);
    }
  }

  private cartVariant(item: CartItem): { colour?: string; size?: string; price?: number; currency?: string } {
    return {
      colour: item.colour,
      size: item.size,
      price: item.price,
      currency: item.currency,
    };
  }

  onSwipeStart(itemID: number, event: TouchEvent): void {
    const startX = event.touches[0].clientX;
    
    const onMove = (e: TouchEvent) => {
      const currentX = e.touches[0].clientX;
      const diff = startX - currentX;
      
      if (diff > 50) {
        this.swipedItemId.set(itemID);
        cleanup();
      } else if (diff < -10) {
        this.swipedItemId.set(null);
        cleanup();
      }
    };
    
    const cleanup = () => {
      document.removeEventListener('touchmove', onMove);
      document.removeEventListener('touchend', cleanup);
    };
    
    document.addEventListener('touchmove', onMove);
    document.addEventListener('touchend', cleanup);
  }

  readonly clearCartConfirmOpen = signal(false);

  clearCart(): void {
    this.clearCartConfirmOpen.set(true);
  }

  confirmClearCart(): void {
    this.cartService.clearCart();
    this.clearCartConfirmOpen.set(false);
  }

  readonly getMainImage = getMainImage;
  readonly onImageError = onImageError;
  readonly trackByItemId = trackByItemId;
  readonly getDiscountedPrice = getDiscountedPrice;
  readonly getBadgeClass = getBadgeClass;

  itemName(item: Item): string { return getTranslatedField(item, 'name', this.langService.currentLanguage()); }
  itemDesc(item: Item): string { return getTranslatedField(item, 'simpleDescription', this.langService.currentLanguage()); }
  get currentCurrency(): string { return this.langService.currentCurrency(); }
  /** Cart totals are summed in the base currency (RUB); convert to whatever the shopper has selected. */
  convertTotal(amountInBaseCurrency: number): number {
    return this.currencyRates.convert(amountInBaseCurrency, this.currencyRates.baseCurrency, this.currentCurrency);
  }
  /**
   * A double-click (or any rapid repeat click) on the checkout button fires
   * two synchronous handler calls before showPaymentPopup's change detection
   * has a chance to cover the button - found via E2E
   * (checkout-idempotent-click.spec.ts), which caught two real
   * POST /api/v2/storefront/checkout requests from one double-click.
   * checkoutInFlight closes that window: it is set before anything async
   * happens, so the second call sees it and bails immediately.
   */
  private readonly checkoutInFlight = signal(false);

  get isCheckoutDisabled(): boolean {
    return !this.termsAccepted || !this.isAuthenticated() || !this.allRequiredDeliveriesSelected() || this.checkoutInFlight();
  }

  selectDelivery(itemID: number, selectedDelivery: DeliveryOption | null): void {
    this.cartService.setSelectedDelivery(itemID, selectedDelivery);
  }

  checkout(paymentMethod: PaymentMethod): void {
    if (this.checkoutInFlight()) {
      return;
    }

    if (!this.allRequiredDeliveriesSelected()) {
      this.notifications.show(this.i18n.t('cart.deliveryRequired'), 'warning');
      return;
    }

    if (!this.termsAccepted) {
      this.notifications.show(this.i18n.t('cart.acceptTerms'), 'warning');
      return;
    }

    this.checkoutInFlight.set(true);
    this.analytics.track('checkout_started', { itemCount: this.items().length });
    this.openPaymentPopup(paymentMethod);
  }

  openPaymentPopup(paymentMethod: PaymentMethod): void {
    this.checkoutIdempotencyKey = crypto.randomUUID();
    this.analytics.track('payment_started', { paymentMethod });
    this.showPaymentPopup.set(true);
    this.selectedPaymentMethod.set(paymentMethod);
    this.paymentStatus.set('creating');
    this.paymentId.set('');
    this.qrCodeUrl.set('');
    this.paymentUrl.set('');
    this.bankPaymentUrl.set('');
    this.bankPaymentFrameUrl.set(null);
    this.showBankPaymentPopup.set(false);
    this.linkCopied.set(false);
    this.userEmail.set('');
    this.userPhone.set('');
    this.emailTouched.set(false);
    this.phoneTouched.set(false);
    this.emailError.set('');
    this.phoneError.set('');
    this.emailSubmitting.set(false);
    this.purchaseSubmitted.set(false);
    this.paidItems = [...this.items()];

    // Contract §3.2 stale-quote policy: the amount about to be charged must
    // be computed from a rate fetched now, not one cached from whenever the
    // shopper last switched currency or opened this page. 'creating' is
    // already showing, so this adds a wait, not a new state.
    // ensureFreshQuote never rejects (a fetch failure resolves with the
    // previously cached rate left in place) - createPayment always runs.
    void this.currencyRates.ensureFreshQuote(this.currentCurrency).then(() => this.createPayment(paymentMethod));
  }

  closePaymentPopup(): void {
    this.showPaymentPopup.set(false);
    this.closeBankPaymentPopup();
    this.stopPolling();
    if (this.closeTimeout) {
      clearTimeout(this.closeTimeout);
      this.closeTimeout = undefined;
    }
    // Every retry/close path routes through here - release the checkout
    // button so a shopper who dismisses an error can actually try again.
    this.checkoutInFlight.set(false);
  }

  retryPayment(): void {
    if (this.closeTimeout) {
      clearTimeout(this.closeTimeout);
      this.closeTimeout = undefined;
    }

    this.paymentStatus.set('creating');
    this.paymentId.set('');
    this.qrCodeUrl.set('');
    this.paymentUrl.set('');
    this.bankPaymentUrl.set('');
    this.bankPaymentFrameUrl.set(null);
    this.showBankPaymentPopup.set(false);
    this.linkCopied.set(false);
    this.createPayment(this.selectedPaymentMethod());
  }

  /**
   * Server-authoritative checkout (PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md
   * §5.2): the client sends offer ids and quantities, never a computed
   * amount. Two calls, not one - createCartPayment's single-request shape
   * doesn't exist in the new contract because the backend must price the
   * session before a payment intent can reference it.
   *
   * offerId uses item.itemID: this codebase has no distinct Offer entity yet
   * (Phase 3, Product/Offer split, not shipped in this model) - itemID is
   * the same catalog identifier every other endpoint already keys off.
   * Revisit once Offer exists as its own id.
   */
  createPayment(paymentMethod: PaymentMethod): void {
    const merchantReference = this.generateOrderId();
    const checkoutPayload = {
      offers: this.items().map(item => ({ offerId: String(item.itemID), qty: item.quantity })),
      currency: this.langService.currentCurrency(),
    };

    this.apiService.createCheckoutSession(checkoutPayload).subscribe({
      next: session => this.createPaymentIntent(session, paymentMethod, merchantReference),
      error: err => {
        console.error('Error creating checkout session:', err);
        this.setPaymentError();
      },
    });
  }

  /**
   * Superseded api.service.ts's createPaymentIntent (POST
   * /api/v2/storefront/payments/intents, our own inferred contract) with
   * @marketplaces/payment's real, published one. That method, createPayment
   * (legacy /qr), createCartPayment (legacy /cart), checkCartPaymentStatus,
   * checkCartCardPaymentStatus, checkPaymentStatus, and the
   * QrCreateResponse-based resolvePaymentQrId/resolvePaymentQrUrl/
   * resolvePaymentLink/resolveBankPaymentUrl helpers - all now deleted from
   * ApiService, confirmed dead first (zero remaining callers) before removal.
   */
  private createPaymentIntent(
    session: import('../../services/api.service').CheckoutSessionResponse,
    paymentMethod: PaymentMethod,
    merchantReference: string,
  ): void {
    this.paymentGateway.create(paymentMethod as PackagePaymentMethod, {
      checkoutSessionId: session.checkoutSessionId,
      // PaymentRequest has no dedicated idempotency field (package contract,
      // node_modules/@marketplaces/payment) - metadata is the only
      // extensibility point, same as merchantReference already uses.
      metadata: { merchantReference, idempotencyKey: this.checkoutIdempotencyKey },
    }).subscribe({
      next: (attempt) => this.handlePaymentAttempt(attempt, paymentMethod),
      error: (err) => {
        console.error('Error creating payment intent:', err);
        this.setPaymentError();
      }
    });
  }

  private handlePaymentAttempt(attempt: PaymentAttempt, paymentMethod: PaymentMethod): void {
    if (!attempt.paymentId || (attempt.status !== 'created' && attempt.status !== 'pending' && !attempt.action)) {
      console.error('Payment attempt missing required fields:', attempt);
      this.setPaymentError();
      return;
    }

    this.paymentId.set(attempt.paymentId);

    if (attempt.action?.type === 'qr') {
      // Same external QR-image rendering used everywhere else in this
      // component (previously via ApiService.resolvePaymentQrUrl) - kept
      // rather than switching to the package's own client-side qrcode
      // generation, to avoid adding a second QR-rendering path for one call site.
      this.qrCodeUrl.set(`https://api.qrserver.com/v1/create-qr-code/?size=256x256&margin=8&data=${encodeURIComponent(attempt.action.url)}`);
      this.paymentUrl.set(attempt.action.url);
    } else if (attempt.action?.type === 'redirect') {
      this.bankPaymentUrl.set(attempt.action.url);
    }

    this.paymentStatus.set('waiting');
    // The package's PaymentAttempt carries no TTL/expiry field, unlike the
    // legacy provider's qrTTL - polling duration falls back to
    // PAYMENT_MIN_POLL_SECONDS alone. Revisit if the real backend adds one.
    this.startPolling();
    if (paymentMethod === 'card' && attempt.action?.type === 'redirect') {
      this.openBankPaymentPopup();
    }
  }

  startPolling(): void {
    this.stopPolling();
    if (!this.paymentId()) {
      this.setPaymentError();
      return;
    }

    const pollSeconds = PAYMENT_MIN_POLL_SECONDS;
    this.maxChecks = Math.ceil(pollSeconds / (PAYMENT_POLL_INTERVAL_MS / 1000));

    this.pollingSubscription = interval(PAYMENT_POLL_INTERVAL_MS)
      .pipe(
        take(this.maxChecks),
        exhaustMap(() =>
          this.paymentGateway.status(this.paymentId(), this.selectedPaymentMethod() as PackagePaymentMethod).pipe(
            timeout(8000),
            catchError((err) => {
              console.error('Error checking payment status:', err);
              this.setPaymentError();
              return EMPTY;
            })
          )
        )
      )
      .subscribe({
        next: (response) => {
          if (!response) {
            return;
          }

          // Package's PaymentStatus is a fixed union
          // ('created'|'pending'|'authorized'|'paid'|'failed'|'cancelled'|'expired'),
          // not a free-form string+code pair like the legacy provider - no
          // .toUpperCase() normalization needed, and no 'REJECTED'/'APPROVED'
          // equivalents exist (those were legacy-provider-specific spellings).
          const paymentStatus = response.status;

          if (paymentStatus === 'failed' || paymentStatus === 'expired' || paymentStatus === 'cancelled') {
            this.paymentStatus.set('timeout');
            this.closeBankPaymentPopup();
            this.stopPolling();
            if (this.closeTimeout) clearTimeout(this.closeTimeout);
            this.closeTimeout = setTimeout(() => {
              this.closePaymentPopup();
            }, PAYMENT_TIMEOUT_CLOSE_MS);
            return;
          }

          // 'authorized' counts as success too (PaymentResult's own status
          // union) - a card payment can settle as authorized before capture.
          if (paymentStatus === 'paid' || paymentStatus === 'authorized') {
            this.paymentStatus.set('success');
            this.closeBankPaymentPopup();
            this.stopPolling();
                        // Auto-submit purchase after 5 seconds
            if (this.closeTimeout) clearTimeout(this.closeTimeout);
            this.closeTimeout = setTimeout(() => {
              this.autoSubmitPurchase();
            }, 5000);
            this.recordOrder();
            this.cartService.clearCart();


          }
          // Continue checking for 3 minutes regardless of other statuses
        },
        complete: () => {
          this.stopPolling();
          // If all checks are done but payment not completed
          if (this.paymentStatus() === 'waiting') {
            this.paymentStatus.set('timeout');
            this.closeBankPaymentPopup();
            // Close popup after showing timeout message
            if (this.closeTimeout) clearTimeout(this.closeTimeout);
            this.closeTimeout = setTimeout(() => {
              this.closePaymentPopup();
            }, PAYMENT_TIMEOUT_CLOSE_MS);
          }
        }
      });
  }

  stopPolling(): void {
    if (this.pollingSubscription) {
      this.pollingSubscription.unsubscribe();
      this.pollingSubscription = undefined;
    }
  }

  openBankPaymentPopup(): void {
    const bankUrl = this.bankPaymentUrl();
    if (!bankUrl) {
      return;
    }

    // FH-1.2: bankUrl comes from the backend's own payment-attempt response
    // (attempt.action.url), so this isn't validating an arbitrary client
    // input - it's refusing to trust-bypass-render anything the backend
    // didn't actually hand back as a real https redirect target.
    if (!this.isHttpsUrl(bankUrl)) {
      console.error('Refusing to open bank payment popup: non-https redirect URL', bankUrl);
      this.setPaymentError();
      return;
    }

    this.bankPaymentFrameUrl.set(this.sanitizer.bypassSecurityTrustResourceUrl(bankUrl));
    this.showBankPaymentPopup.set(true);
  }

  private isHttpsUrl(url: string): boolean {
    try {
      return new URL(url).protocol === 'https:';
    } catch {
      return false;
    }
  }

  closeBankPaymentPopup(): void {
    this.showBankPaymentPopup.set(false);
    this.bankPaymentFrameUrl.set(null);
  }

  private setPaymentError(): void {
    this.paymentStatus.set('error');
    this.closeBankPaymentPopup();
    this.stopPolling();
    if (this.closeTimeout) {
      clearTimeout(this.closeTimeout);
      this.closeTimeout = undefined;
    }
    // The popup may stay open to show the error rather than closing, so
    // this can't rely on closePaymentPopup() being called - release the
    // checkout button here too, or a failed attempt locks retry out forever.
    this.checkoutInFlight.set(false);
  }

  /**
   * Records the just-paid cart as a backoffice AdminOrder (POST /orders) so it shows up
   * in Backoffice → Orders/Transactions. Best-effort and fire-and-forget: a failure here
   * must never affect the already-confirmed payment or block autoSubmitPurchase.
   */
  private recordOrder(): void {
    const email = this.userEmail().trim();
    const phone = this.userPhone().replace(/\D/g, '');

    this.apiService.createOrder({
      items: this.paidItems.map((item: CartItem) => ({
        productId: String(item.itemID),
        name: item.name,
        quantity: item.quantity,
      })),
      customer: {
        name: this.getTelegramUsername() || this.i18n.t('common.guest'),
        email,
        phone,
      },
      payment: {
        method: this.selectedPaymentMethod(),
        currency: this.langService.currentCurrency(),
      },
    }).subscribe({
      error: (err) => console.error('Error recording order:', err),
    });
  }

  /**
   * Fallback fired a few seconds after payment success if the user hasn't
   * already submitted the email/phone form themselves (submitEmail()).
   * Navigates home only once the submission result is known, never before -
   * and sends whatever the user has typed so far instead of blank fields.
   */
  private autoSubmitPurchase(): void {
    if (this.purchaseSubmitted()) {
      return;
    }

    const telegramUserId = this.getTelegramUserId();

    // Telegram ID is mandatory for submitPurchaseEmail.
    if (!telegramUserId) {
      this.notifications.show(this.i18n.t('cart.telegramIdMissing'), 'warning');
      this.emailSubmitting.set(false);
      this.closePaymentPopup();
      const lang = this.langService.currentLanguage();
      this.router.navigate([`/${lang}`]);
      return;
    }

    this.emailSubmitting.set(true);

    const emailData = {
      email: this.userEmail().trim(),
      phone: this.userPhone().replace(/\D/g, ''),
      telegramUserId: telegramUserId,
      items: this.paidItems.map((item: CartItem) => ({
        itemID: item.itemID,
        name: item.name,
        price: item.discount > 0
          ? item.price * (1 - item.discount / 100)
          : item.price,
        currency: item.currency,
        quantity: item.quantity,
        ...(item.selectedDelivery ? { delivery: [item.selectedDelivery] } : {})
      }))
    };

    this.apiService.submitPurchaseEmail(emailData).subscribe({
      next: () => {
        this.purchaseSubmitted.set(true);
        this.emailSubmitting.set(false);
        this.closePaymentPopup();
        const lang = this.langService.currentLanguage();
        this.router.navigate([`/${lang}`]);
      },
      error: (err) => {
        console.error('Error submitting purchase:', err);
        this.emailSubmitting.set(false);
        // Still close popup and redirect even if submission fails
        this.closePaymentPopup();
        const lang = this.langService.currentLanguage();
        this.router.navigate([`/${lang}`]);
      }
    });
  }

  copyPaymentLink(): void {
    const url = this.paymentUrl();
    if (url) {
      navigator.clipboard.writeText(url).then(() => {
        this.linkCopied.set(true);
        setTimeout(() => this.linkCopied.set(false), LINK_COPIED_DURATION_MS);
      }).catch(err => {
        console.error(this.i18n.t('cart.copyError'), err);
      });
    }
  }

  submitEmail(): void {
    // Mark both fields as touched
    this.emailTouched.set(true);
    this.phoneTouched.set(true);
    
    // Validate both fields
    this.validateEmail();
    const digitsOnly = this.userPhone().replace(/\D/g, '');
    this.validatePhone(digitsOnly);
    
    // Check if there are any errors
    if (this.emailError() || this.phoneError()) {
      return;
    }
    
    const email = this.userEmail().trim();
    const phoneRaw = this.userPhone().replace(/\D/g, ''); // Remove all formatting, send only digits
    
    this.emailSubmitting.set(true);
    
    const emailData = {
      email: email,
      phone: phoneRaw,
      telegramUserId: this.getTelegramUserId(),
      items: this.paidItems.map((item: CartItem) => ({
        itemID: item.itemID,
        name: item.name,
        price: item.discount > 0 
          ? item.price * (1 - item.discount / 100)
          : item.price,
        currency: item.currency,
        quantity: item.quantity,
        ...(item.selectedDelivery ? { delivery: [item.selectedDelivery] } : {})
      }))
    };
    
    this.apiService.submitPurchaseEmail(emailData).subscribe({
      next: () => {
        this.purchaseSubmitted.set(true);
        if (this.closeTimeout) {
          clearTimeout(this.closeTimeout);
          this.closeTimeout = undefined;
        }
        this.emailSubmitting.set(false);
        this.notifications.show(this.i18n.t('cart.emailSuccess'), 'success');
        // Close popup and redirect to home page
        setTimeout(() => {
          this.closePaymentPopup();
          const lang = this.langService.currentLanguage();
          this.router.navigate([`/${lang}`]);
        }, 500);
      },
      error: (err) => {
        console.error('Error submitting email:', err);
        this.emailSubmitting.set(false);
        this.notifications.show(this.i18n.t('cart.emailError'), 'warning');
      }
    });
  }

  private getTelegramUserId(): string | null {
    const sessionTelegramUserId = this.authService.session()?.userId;
    if (sessionTelegramUserId !== null && sessionTelegramUserId !== undefined) {
      return sessionTelegramUserId.toString();
    }

    if (typeof window !== 'undefined' && window.Telegram?.WebApp?.initDataUnsafe?.user) {
      return window.Telegram.WebApp.initDataUnsafe.user.id.toString();
    }

    return null;
  }

  private getTelegramUsername(): string {
    const sessionUsername = this.authService.session()?.username;
    if (sessionUsername) {
      return sessionUsername;
    }

    if (typeof window !== 'undefined' && window.Telegram?.WebApp?.initDataUnsafe?.user) {
      return window.Telegram.WebApp.initDataUnsafe.user.username || 'nontelegram';
    }

    return 'nontelegram';
  }

  private generateOrderId(): string {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2, 8);
    return `order_${timestamp}_${random}`;
  }

  onPhoneInput(event: Event): void {
    const input = event.target as HTMLInputElement;
    let value = input.value.replace(/\D/g, ''); // Remove all non-digits
    
    // Auto-add +7 for Russian numbers
    if (value.length > 0 && !value.startsWith('7') && !value.startsWith('8')) {
      value = '7' + value;
    }
    
    // Convert 8 to 7 for Russian format
    if (value.startsWith('8')) {
      value = '7' + value.substring(1);
    }
    
    // Format: +7 (XXX) XXX-XX-XX
    let formatted = '';
    if (value.length > 0) {
      formatted = '+7';
      if (value.length > 1) {
        formatted += ' (' + value.substring(1, 4);
      }
      if (value.length >= 4) {
        formatted += ') ' + value.substring(4, 7);
      }
      if (value.length >= 7) {
        formatted += '-' + value.substring(7, 9);
      }
      if (value.length >= 9) {
        formatted += '-' + value.substring(9, 11);
      }
    }
    
    this.userPhone.set(formatted);
    this.validatePhone(value);
  }

  onPhoneBlur(): void {
    this.phoneTouched.set(true);
    const digitsOnly = this.userPhone().replace(/\D/g, '');
    this.validatePhone(digitsOnly);
  }

  validatePhone(digitsOnly: string): void {
    if (!this.phoneTouched() && digitsOnly.length === 0) {
      this.phoneError.set('');
      return;
    }

    if (digitsOnly.length === 0) {
      this.phoneError.set(this.i18n.t('cart.phoneRequired'));
    } else if (digitsOnly.length < 11) {
      this.phoneError.set(this.i18n.t('cart.phoneMoreDigits', { count: 11 - digitsOnly.length }));
    } else if (digitsOnly.length > 11) {
      this.phoneError.set(this.i18n.t('cart.phoneTooMany'));
    } else {
      this.phoneError.set('');
    }
  }

  onEmailInput(event: Event): void {
    const input = event.target as HTMLInputElement;
    this.userEmail.set(input.value);
    if (this.emailTouched()) {
      this.validateEmail();
    }
  }

  onEmailBlur(): void {
    this.emailTouched.set(true);
    this.validateEmail();
  }

  validateEmail(): void {
    const email = this.userEmail().trim();
    
    if (!this.emailTouched() && email.length === 0) {
      this.emailError.set('');
      return;
    }

    if (email.length === 0) {
      this.emailError.set(this.i18n.t('cart.emailRequired'));
    } else if (email.length < 5) {
      this.emailError.set(this.i18n.t('cart.emailTooShort'));
    } else if (email.length > 100) {
      this.emailError.set(this.i18n.t('cart.emailTooLong'));
    } else if (!email.includes('@')) {
      this.emailError.set(this.i18n.t('cart.emailNeedsAt'));
    } else if (!email.includes('.')) {
      this.emailError.set(this.i18n.t('cart.emailNeedsDomain'));
    } else {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        this.emailError.set(this.i18n.t('cart.emailInvalid'));
      } else {
        this.emailError.set('');
      }
    }
  }
}
