import { uiLocale } from '@/utils/uiLocale';
import { useState, useMemo, useEffect, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { useNavigate, useSearchParams, Link } from 'react-router';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { motion, AnimatePresence } from 'framer-motion';
import { giftApi } from '../api/gift';
import { brandingApi, type TelegramWidgetConfig } from '../api/branding';
import type {
  GiftConfig,
  GiftTariff,
  GiftTariffPeriod,
  GiftPaymentMethod,
  GiftPurchaseRequest,
  SentGift,
  ReceivedGift,
} from '../api/gift';

import { cn } from '../lib/utils';
import { copyToClipboard } from '../utils/clipboard';
import { getApiErrorMessage } from '../utils/api-error';
import { formatPrice } from '../utils/format';
import { useCurrency } from '../hooks/useCurrency';
import { usePlatform, useHaptic } from '@/platform';
import { openPaymentUrl } from '../utils/openPaymentUrl';
import {
  SparklesIcon,
  GiftIcon,
  CheckIcon,
  CheckCircleIcon,
  KeyIcon,
  InboxIcon,
  ExportIcon,
  WarningCircleIcon,
  BanIcon,
} from '@/components/icons';

function formatPeriodLabel(
  days: number,
  t: (key: string, options?: Record<string, unknown>) => string,
): string {
  const key = `landing.periodLabels.d${days}`;
  const result = t(key);
  if (result !== key) return result;

  const months = Math.floor(days / 30);
  const remainder = days % 30;
  if (months > 0 && remainder === 0) {
    return t('landing.periodLabels.nMonths', { count: months });
  }
  return t('landing.periodLabels.nDays', { count: days });
}

function getGiftStatusKey(status: string): string {
  const statusMap: Record<string, string> = {
    pending_activation: 'gift.statusPendingActivation',
    delivered: 'gift.statusDelivered',
    paid: 'gift.statusAvailable',
    pending: 'gift.statusPending',
    failed: 'gift.statusFailed',
    expired: 'gift.statusExpired',
  };
  return statusMap[status] ?? 'gift.statusPending';
}

function isGiftAvailable(status: string): boolean {
  return status === 'paid' || status === 'delivered' || status === 'pending_activation';
}

function isGiftActivated(gift: SentGift): boolean {
  return gift.status === 'delivered' && gift.activated_by_username != null;
}

function formatGiftDate(dateStr: string | null): string {
  if (!dateStr) return '';
  const date = new Date(dateStr);
  return date.toLocaleDateString(uiLocale(), {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
  });
}

type TabId = 'buy' | 'activate' | 'myGifts';

function LoadingSkeleton() {
  return (
    <div className="flex min-h-dvh items-center justify-center">
      <div className="flex flex-col items-center gap-4">
        <div className="h-8 w-8 animate-spin rounded-full border-2 border-dark-600 border-t-accent-500" />
      </div>
    </div>
  );
}

function ErrorState({ message }: { message: string }) {
  const { t } = useTranslation();

  return (
    <div className="flex min-h-dvh items-center justify-center px-4">
      <div className="flex max-w-sm flex-col items-center gap-4 text-center">
        <div className="flex h-16 w-16 items-center justify-center rounded-full bg-error-500/10">
          <WarningCircleIcon className="h-8 w-8 text-error-400" />
        </div>
        <h2 className="text-lg font-semibold text-dark-50">{t('gift.failedTitle')}</h2>
        <p className="text-sm text-dark-300">{message}</p>
      </div>
    </div>
  );
}

function DisabledState() {
  const { t } = useTranslation();
  const navigate = useNavigate();

  useEffect(() => {
    const timer = setTimeout(() => navigate('/'), 3000);
    return () => clearTimeout(timer);
  }, [navigate]);

  return (
    <div className="flex min-h-dvh items-center justify-center px-4">
      <div className="flex max-w-sm flex-col items-center gap-4 text-center">
        <div className="flex h-16 w-16 items-center justify-center rounded-full bg-dark-800/50">
          <BanIcon className="h-8 w-8 text-dark-400" />
        </div>
        <h2 className="text-lg font-semibold text-dark-50">{t('gift.featureDisabled')}</h2>
        <p className="text-sm text-dark-300">{t('gift.redirecting')}</p>
      </div>
    </div>
  );
}

function TariffCard({
  tariff,
  isSelected,
  onSelect,
}: {
  tariff: GiftTariff;
  isSelected: boolean;
  onSelect: () => void;
}) {
  const { t } = useTranslation();

  return (
    <button
      type="button"
      role="radio"
      aria-checked={isSelected}
      onClick={onSelect}
      className={cn(
        'flex w-full items-center gap-4 rounded-2xl border p-4 text-start transition-all duration-200',
        isSelected
          ? 'border-accent-500/50 bg-accent-500/5'
          : 'border-dark-800/50 bg-dark-900/50 hover:border-dark-700/50',
      )}
    >
      {/* Gift circle icon */}
      <div
        className={cn(
          'flex h-12 w-12 shrink-0 items-center justify-center rounded-full transition-colors',
          isSelected ? 'bg-accent-500/20' : 'bg-dark-800/50',
        )}
      >
        <GiftIcon
          className={cn(
            'h-6 w-6 transition-colors',
            isSelected ? 'text-accent-400' : 'text-dark-400',
          )}
        />
      </div>

      {/* Info */}
      <div className="min-w-0 flex-1">
        <p className="text-base font-bold text-dark-50">{tariff.name}</p>
        <p
          className={cn(
            'text-xs font-medium uppercase tracking-wider transition-colors',
            isSelected ? 'text-accent-400' : 'text-dark-400',
          )}
        >
          {tariff.traffic_limit_gb > 0
            ? `${tariff.traffic_limit_gb} ${t('gift.gbShort')}`
            : t('gift.unlimitedTraffic')}
          {' \u2022 '}
          {t('gift.deviceCount', { count: tariff.device_limit })}
        </p>
      </div>

      {/* Checkmark circle */}
      <div
        className={cn(
          'flex h-6 w-6 shrink-0 items-center justify-center rounded-full border-2 transition-colors',
          isSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600',
        )}
      >
        {isSelected && <CheckIcon className="h-3.5 w-3.5 text-white" />}
      </div>
    </button>
  );
}

function PeriodCard({
  period,
  isSelected,
  onSelect,
}: {
  period: GiftTariffPeriod;
  isSelected: boolean;
  onSelect: () => void;
}) {
  const { t } = useTranslation();
  const hasDiscount =
    period.original_price_kopeks != null && period.original_price_kopeks > period.price_kopeks;

  return (
    <button
      type="button"
      onClick={onSelect}
      className={cn(
        'flex w-full items-center justify-between rounded-2xl p-4 transition-all duration-200',
        isSelected
          ? 'bg-gradient-to-r from-accent-500 to-accent-600 text-white shadow-lg shadow-accent-500/25'
          : 'bg-dark-800/50 hover:bg-dark-700/50',
      )}
    >
      {/* Left: period + discount */}
      <div className="flex flex-col items-start gap-1">
        <span className="text-lg font-bold">{formatPeriodLabel(period.days, t)}</span>
        {hasDiscount && period.discount_percent != null && (
          <span
            className={cn(
              'rounded-md px-2 py-0.5 text-xs font-bold',
              isSelected ? 'bg-white/20 text-on-accent' : 'bg-accent-500/20 text-accent-400',
            )}
          >
            -{period.discount_percent}%
          </span>
        )}
      </div>

      {/* Right: prices */}
      <div className="flex flex-col items-end gap-0.5">
        <span className={cn('text-lg font-bold', isSelected ? 'text-white' : 'text-accent-400')}>
          {formatPrice(period.price_kopeks)}
        </span>
        {hasDiscount && period.original_price_kopeks != null && (
          <span
            className={cn('text-xs line-through', isSelected ? 'text-white/50' : 'text-dark-500')}
          >
            {formatPrice(period.original_price_kopeks)}
          </span>
        )}
      </div>
    </button>
  );
}

function PaymentModeToggle({
  mode,
  onToggle,
  balanceLabel,
}: {
  mode: 'balance' | 'gateway';
  onToggle: (mode: 'balance' | 'gateway') => void;
  balanceLabel: string;
}) {
  const { t } = useTranslation();

  return (
    <div
      role="group"
      aria-label={t('gift.paymentMode')}
      className="flex rounded-xl bg-dark-800/50 p-1"
    >
      <button
        type="button"
        onClick={() => onToggle('balance')}
        aria-pressed={mode === 'balance'}
        className={cn(
          'flex-1 rounded-lg px-4 py-2.5 text-sm font-medium transition-all duration-200',
          mode === 'balance'
            ? 'bg-dark-700 text-dark-50 shadow-sm'
            : 'text-dark-400 hover:text-dark-200',
        )}
      >
        {balanceLabel}
      </button>
      <button
        type="button"
        onClick={() => onToggle('gateway')}
        aria-pressed={mode === 'gateway'}
        className={cn(
          'flex-1 rounded-lg px-4 py-2.5 text-sm font-medium transition-all duration-200',
          mode === 'gateway'
            ? 'bg-dark-700 text-dark-50 shadow-sm'
            : 'text-dark-400 hover:text-dark-200',
        )}
      >
        {t('gift.viaGateway')}
      </button>
    </div>
  );
}

function PaymentMethodCard({
  method,
  isSelected,
  selectedSubOption,
  onSelect,
  onSelectSubOption,
}: {
  method: GiftPaymentMethod;
  isSelected: boolean;
  selectedSubOption: string | null;
  onSelect: () => void;
  onSelectSubOption: (subOptionId: string) => void;
}) {
  const hasSubOptions = method.sub_options && method.sub_options.length > 1;

  return (
    <div
      className={cn(
        'rounded-2xl border transition-all duration-200',
        isSelected
          ? 'border-accent-500/50 bg-accent-500/5'
          : 'border-dark-800/50 bg-dark-900/50 hover:border-dark-700/50',
      )}
    >
      <button
        type="button"
        role="radio"
        aria-checked={isSelected}
        onClick={onSelect}
        className="flex w-full items-center gap-4 p-4 text-start"
      >
        {method.icon_url && (
          <div className="flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-dark-800/50">
            <img src={method.icon_url} alt="" className="h-6 w-6 object-contain" />
          </div>
        )}
        <div className="min-w-0 flex-1">
          <p className="text-sm font-medium text-dark-100">{method.display_name}</p>
          {method.description && (
            <p className="mt-0.5 truncate text-xs text-dark-400">{method.description}</p>
          )}
        </div>
        <div
          className={cn(
            'flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors',
            isSelected ? 'border-accent-500 bg-accent-500' : 'border-dark-600',
          )}
        >
          {isSelected && <div className="h-2 w-2 rounded-full bg-white" />}
        </div>
      </button>

      {isSelected && hasSubOptions && (
        <div className="border-t border-dark-800/30 px-4 pb-4 pt-3">
          <div className="flex flex-wrap gap-2">
            {method.sub_options!.map((opt) => (
              <button
                key={opt.id}
                type="button"
                onClick={() => onSelectSubOption(opt.id)}
                className={cn(
                  'rounded-xl px-4 py-2 text-sm font-medium transition-all duration-200',
                  selectedSubOption === opt.id
                    ? 'bg-accent-500 text-on-accent shadow-sm shadow-accent-500/25'
                    : 'bg-dark-800/50 text-dark-300 hover:bg-dark-700/50',
                )}
              >
                {opt.name}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function BuyTabContent({
  config,
  onPurchaseComplete,
}: {
  config: GiftConfig;
  onPurchaseComplete: () => void;
}) {
  const { t } = useTranslation();
  const queryClient = useQueryClient();
  const { openInvoice, capabilities, openLink, platform } = usePlatform();
  const haptic = useHaptic();

  // Selection state
  const [selectedTariffId, setSelectedTariffId] = useState<number | null>(null);
  const [selectedPeriodDays, setSelectedPeriodDays] = useState<number | null>(null);
  const [paymentMode, setPaymentMode] = useState<'balance' | 'gateway'>('balance');
  const [selectedMethod, setSelectedMethod] = useState<string | null>(null);
  const [selectedSubOption, setSelectedSubOption] = useState<string | null>(null);
  const [submitError, setSubmitError] = useState<string | null>(null);

  // Auto-select first tariff, period, method on config load
  useEffect(() => {
    if (config.tariffs.length > 0 && selectedTariffId === null) {
      const firstTariff = config.tariffs[0];
      setSelectedTariffId(firstTariff.id);
      if (firstTariff.periods.length > 0 && selectedPeriodDays === null) {
        setSelectedPeriodDays(firstTariff.periods[0].days);
      }
    }

    if (config.payment_methods.length > 0 && selectedMethod === null) {
      const firstMethod = config.payment_methods[0];
      setSelectedMethod(firstMethod.method_id);
      if (firstMethod.sub_options && firstMethod.sub_options.length >= 1) {
        setSelectedSubOption(firstMethod.sub_options[0].id);
      } else {
        setSelectedSubOption(null);
      }
    }
  }, [config, selectedTariffId, selectedPeriodDays, selectedMethod]);

  // When tariff changes, auto-select its first period
  useEffect(() => {
    if (!selectedTariffId) return;
    const tariff = config.tariffs.find((t) => t.id === selectedTariffId);
    if (tariff && tariff.periods.length > 0) {
      const hasCurrent = tariff.periods.some((p) => p.days === selectedPeriodDays);
      if (!hasCurrent) {
        setSelectedPeriodDays(tariff.periods[0].days);
      }
    }
  }, [selectedTariffId, config.tariffs, selectedPeriodDays]);

  // Derived data
  const selectedTariff = useMemo(
    () => config.tariffs.find((tr) => tr.id === selectedTariffId),
    [config.tariffs, selectedTariffId],
  );

  const selectedPeriod = useMemo(
    () => selectedTariff?.periods.find((p) => p.days === selectedPeriodDays),
    [selectedTariff, selectedPeriodDays],
  );

  const currentPrice = selectedPeriod?.price_kopeks ?? 0;

  const insufficientBalance = paymentMode === 'balance' && config.balance_kopeks < currentPrice;

  // Validation
  const canSubmit = useMemo(() => {
    if (!selectedTariffId || !selectedPeriodDays) return false;
    if (paymentMode === 'gateway' && !selectedMethod) return false;
    if (insufficientBalance) return false;
    return true;
  }, [selectedTariffId, selectedPeriodDays, paymentMode, selectedMethod, insufficientBalance]);

  // Purchase mutation
  const purchaseMutation = useMutation({
    mutationFn: (data: GiftPurchaseRequest) => giftApi.createPurchase(data),
    onSuccess: async (result) => {
      if (result.payment_url) {
        // Telegram Stars: open invoice natively instead of redirect
        const isStars = selectedMethod === 'telegram_stars';
        if (isStars && capabilities.hasInvoice) {
          try {
            const status = await openInvoice(result.payment_url);
            if (status === 'paid') {
              haptic.notification('success');
              queryClient.invalidateQueries({ queryKey: ['balance'] });
              queryClient.invalidateQueries({ queryKey: ['gift-config'] });
              queryClient.invalidateQueries({ queryKey: ['gift-sent'] });
              onPurchaseComplete();
            } else if (status === 'failed') {
              haptic.notification('error');
              setSubmitError(t('gift.failedDesc'));
            }
            // 'cancelled' — user closed the invoice, do nothing
          } catch {
            setSubmitError(t('gift.failedDesc'));
          }
          return;
        }
        // Non-Stars provider (RollyPay/YooKassa/SBP …): open externally inside Telegram so
        // a bank-app hand-off via custom scheme doesn't dead-end in the WebView (#654272).
        openPaymentUrl(result.payment_url, platform, openLink);
      } else {
        // Balance purchase: switch to MyGifts tab so the new code is visible
        queryClient.invalidateQueries({ queryKey: ['balance'] });
        queryClient.invalidateQueries({ queryKey: ['gift-config'] });
        queryClient.invalidateQueries({ queryKey: ['gift-sent'] });
        onPurchaseComplete();
      }
    },
    onError: (err) => {
      const msg = getApiErrorMessage(err, t('gift.failedDesc'));
      setSubmitError(msg);
    },
  });

  // Submit handler
  const handleSubmit = () => {
    if (!selectedTariffId || !selectedPeriodDays || !canSubmit || purchaseMutation.isPending)
      return;

    setSubmitError(null);

    let paymentMethod: string | undefined;
    if (paymentMode === 'gateway' && selectedMethod) {
      paymentMethod = selectedMethod;
      if (selectedSubOption) {
        paymentMethod = `${paymentMethod}_${selectedSubOption}`;
      }
    }

    const data: GiftPurchaseRequest = {
      tariff_id: selectedTariffId,
      period_days: selectedPeriodDays,
      payment_mode: paymentMode,
      payment_method: paymentMethod,
    };

    purchaseMutation.mutate(data);
  };

  // Balance label with current amount
  const balanceLabel = useMemo(() => {
    return `${t('gift.fromBalance')} (${formatPrice(config.balance_kopeks)})`;
  }, [config, t]);

  const showTariffCards = config.tariffs.length > 1;

  // Periods for the selected tariff (for period cards)
  const periodsForDisplay = useMemo(() => {
    if (selectedTariff) {
      return [...selectedTariff.periods].sort((a, b) => a.days - b.days);
    }
    return [];
  }, [selectedTariff]);

  return (
    <div className="space-y-6">
      {/* Tariff selection */}
      {showTariffCards && (
        <div>
          <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
            {t('gift.selectTariff')}
          </h2>
          <div role="radiogroup" aria-label={t('gift.chooseTariff')} className="space-y-2">
            {config.tariffs.map((tariff) => (
              <TariffCard
                key={tariff.id}
                tariff={tariff}
                isSelected={tariff.id === selectedTariffId}
                onSelect={() => setSelectedTariffId(tariff.id)}
              />
            ))}
          </div>
        </div>
      )}

      {/* Selected tariff description */}
      {selectedTariff?.description && (
        <div className="rounded-xl border border-dark-800/30 bg-dark-800/20 px-4 py-3">
          <p className="text-sm text-dark-300">{selectedTariff.description}</p>
        </div>
      )}

      {/* Promo group banner */}
      {config.promo_group_name && (
        <div className="flex items-center gap-3 rounded-xl border border-success-500/30 bg-success-500/10 p-3">
          <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-success-500/20">
            <SparklesIcon className="h-4 w-4 text-success-400" />
          </div>
          <div>
            <div className="text-sm font-medium text-success-400">
              {t('subscription.promoGroup.yourGroup', { name: config.promo_group_name })}
            </div>
            <div className="text-xs text-dark-400">
              {t('subscription.promoGroup.personalDiscountsApplied')}
            </div>
          </div>
        </div>
      )}

      {/* Active discount banner */}
      {config.active_discount_percent != null && config.active_discount_percent > 0 && (
        <div className="flex items-center gap-3 rounded-xl border border-warning-500/30 bg-warning-500/10 p-3">
          <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-warning-500/20">
            <SparklesIcon className="h-4 w-4 text-warning-400" />
          </div>
          <div className="text-sm font-medium text-warning-400">
            {t('promo.discountApplied')} -{config.active_discount_percent}%
          </div>
        </div>
      )}

      {/* Period selection */}
      {periodsForDisplay.length > 0 && (
        <div>
          <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
            {t('gift.selectPeriod')}
          </h2>
          <div className="space-y-2">
            {periodsForDisplay.map((period) => (
              <PeriodCard
                key={period.days}
                period={period}
                isSelected={period.days === selectedPeriodDays}
                onSelect={() => setSelectedPeriodDays(period.days)}
              />
            ))}
          </div>
        </div>
      )}

      {/* Payment mode toggle */}
      <div>
        <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
          {t('gift.paymentMode')}
        </h2>
        <PaymentModeToggle
          mode={paymentMode}
          onToggle={setPaymentMode}
          balanceLabel={balanceLabel}
        />
      </div>

      {/* Payment method cards (gateway mode only) */}
      <AnimatePresence mode="wait">
        {paymentMode === 'gateway' && config.payment_methods.length > 0 && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.2 }}
            className="overflow-hidden"
          >
            <div role="radiogroup" aria-label={t('gift.paymentMethod')} className="space-y-2">
              {config.payment_methods.map((method) => (
                <PaymentMethodCard
                  key={method.method_id}
                  method={method}
                  isSelected={method.method_id === selectedMethod}
                  selectedSubOption={method.method_id === selectedMethod ? selectedSubOption : null}
                  onSelect={() => {
                    setSelectedMethod(method.method_id);
                    if (method.sub_options && method.sub_options.length >= 1) {
                      setSelectedSubOption(method.sub_options[0].id);
                    } else {
                      setSelectedSubOption(null);
                    }
                  }}
                  onSelectSubOption={setSelectedSubOption}
                />
              ))}
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Summary / Balance info */}
      {paymentMode === 'balance' && (
        <div className="rounded-2xl border border-dark-800/50 bg-dark-900/50 p-4">
          <div className="flex items-center justify-between">
            <span className="text-sm text-dark-400">{t('gift.yourBalance')}</span>
            <span className="text-sm font-semibold text-dark-200">
              {formatPrice(config.balance_kopeks)}
            </span>
          </div>
        </div>
      )}

      {/* Insufficient balance warning */}
      <AnimatePresence>
        {insufficientBalance && (
          <motion.div
            initial={{ opacity: 0, y: -8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -8 }}
            className="rounded-xl border border-warning-500/20 bg-warning-500/5 p-3"
          >
            <p className="text-sm text-warning-400">
              {t('gift.insufficientBalance')}{' '}
              <Link
                to="/balance"
                className="font-medium text-accent-400 underline underline-offset-2"
              >
                {t('gift.topUpBalance')}
              </Link>
            </p>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Error */}
      <AnimatePresence>
        {submitError && (
          <motion.div
            initial={{ opacity: 0, y: -8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -8 }}
            className="rounded-xl border border-error-500/20 bg-error-500/5 p-3"
          >
            <p className="text-sm text-error-400">{submitError}</p>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Submit button */}
      <button
        type="button"
        onClick={handleSubmit}
        disabled={!canSubmit || purchaseMutation.isPending}
        className={cn(
          'flex w-full items-center justify-center gap-2 rounded-2xl px-6 py-4 text-base font-semibold transition-all duration-200',
          canSubmit && !purchaseMutation.isPending
            ? 'bg-accent-500 text-on-accent shadow-lg shadow-accent-500/25 hover:bg-accent-400 hover:shadow-accent-500/40 active:scale-[0.98]'
            : 'cursor-not-allowed bg-dark-800 text-dark-500',
        )}
      >
        {purchaseMutation.isPending ? (
          <div className="h-5 w-5 animate-spin rounded-full border-2 border-white/30 border-t-white" />
        ) : (
          <>
            {t('gift.giftButton')} {currentPrice > 0 ? formatPrice(currentPrice) : ''}
          </>
        )}
      </button>
    </div>
  );
}

function ActivateTabContent({ initialCode }: { initialCode?: string | null }) {
  const { t } = useTranslation();
  const queryClient = useQueryClient();
  const [code, setCode] = useState(initialCode ?? '');

  // Sync when initialCode changes (e.g. URL param update while tab is active)
  useEffect(() => {
    if (initialCode) setCode(initialCode);
  }, [initialCode]);
  const [activateError, setActivateError] = useState<string | null>(null);

  const activateMutation = useMutation({
    mutationFn: (giftCode: string) => giftApi.activateGiftCode(giftCode),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['gift-received'] });
      queryClient.invalidateQueries({ queryKey: ['gift-sent'] });
      queryClient.invalidateQueries({ queryKey: ['balance'] });
    },
    onError: (err) => {
      const raw = getApiErrorMessage(err, '');
      const msg =
        raw === 'Cannot activate your own gift'
          ? t('gift.activateSelfError')
          : raw || t('gift.activateError');
      setActivateError(msg);
    },
  });

  const handleActivate = () => {
    const trimmed = code.trim();
    if (!trimmed || activateMutation.isPending) return;
    setActivateError(null);
    activateMutation.mutate(trimmed);
  };

  if (activateMutation.isSuccess && activateMutation.data) {
    const result = activateMutation.data;
    return (
      <div className="flex flex-col items-center gap-4 py-12 text-center">
        <div className="flex h-16 w-16 items-center justify-center rounded-full bg-accent-500/20">
          <CheckCircleIcon className="h-8 w-8 text-accent-400" />
        </div>
        <h2 className="text-xl font-bold text-dark-50">{t('gift.activateSuccess')}</h2>
        <p className="text-sm text-dark-300">
          {t('gift.activateSuccessDesc', {
            tariff: result.tariff_name ?? '',
            days: result.period_days ?? 0,
          })}
        </p>
      </div>
    );
  }

  return (
    <div className="flex flex-col items-center gap-6 py-8">
      {/* Icon + title */}
      <div className="flex flex-col items-center gap-3 text-center">
        <div className="flex h-16 w-16 items-center justify-center rounded-full bg-accent-500/20">
          <KeyIcon className="h-8 w-8 text-accent-400" />
        </div>
        <h2 className="text-xl font-bold text-dark-50">{t('gift.activateTitle')}</h2>
        <p className="max-w-xs text-sm text-dark-400">{t('gift.activateDescription')}</p>
      </div>

      {/* Code input */}
      <div className="w-full max-w-sm">
        <input
          type="text"
          value={code}
          onChange={(e) => {
            setCode(e.target.value);
            setActivateError(null);
          }}
          placeholder={t('gift.activateCodePlaceholder')}
          className="w-full rounded-2xl border border-dark-700/50 bg-dark-800/50 px-6 py-4 text-center font-mono text-sm text-dark-50 placeholder-dark-500 outline-none transition-colors focus:border-accent-500/50 focus:ring-1 focus:ring-accent-500/25"
          aria-label={t('gift.activateTitle')}
        />
      </div>

      {/* Error */}
      <AnimatePresence>
        {activateError && (
          <motion.div
            initial={{ opacity: 0, y: -8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -8 }}
            className="w-full max-w-sm rounded-xl border border-error-500/20 bg-error-500/5 p-3"
          >
            <p className="text-center text-sm text-error-400">{activateError}</p>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Submit */}
      <button
        type="button"
        onClick={handleActivate}
        disabled={!code.trim() || activateMutation.isPending}
        className={cn(
          'w-full max-w-sm rounded-2xl px-6 py-4 text-base font-semibold transition-all duration-200',
          code.trim() && !activateMutation.isPending
            ? 'bg-accent-500 text-on-accent shadow-lg shadow-accent-500/25 hover:bg-accent-400 active:scale-[0.98]'
            : 'cursor-not-allowed bg-dark-800 text-dark-500',
        )}
      >
        {activateMutation.isPending ? (
          <div className="mx-auto h-5 w-5 animate-spin rounded-full border-2 border-white/30 border-t-white" />
        ) : (
          t('gift.activateButton')
        )}
      </button>
    </div>
  );
}

function CopiedToast({ onDismiss }: { onDismiss: () => void }) {
  const { t } = useTranslation();

  useEffect(() => {
    const timer = setTimeout(onDismiss, 2000);
    return () => clearTimeout(timer);
  }, [onDismiss]);

  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: 20 }}
      transition={{ duration: 0.2, ease: 'easeOut' }}
      className="fixed inset-x-0 bottom-6 z-50 flex justify-center"
    >
      <div className="flex items-center gap-2 rounded-full border border-dark-700/50 bg-dark-900/95 px-5 py-2.5 shadow-2xl shadow-black/40 backdrop-blur-md">
        <CheckIcon className="h-4 w-4 text-success-400" />
        <span className="text-sm font-medium text-success-400">{t('gift.shareToastCopied')}</span>
      </div>
    </motion.div>
  );
}

function SentGiftCard({ gift }: { gift: SentGift }) {
  const { t } = useTranslation();
  const [showToast, setShowToast] = useState(false);

  // Runtime bot username (env var is only a fallback) so the "activate via bot"
  // line never silently disappears when VITE_TELEGRAM_BOT_USERNAME is unset.
  const { data: widgetConfig } = useQuery<TelegramWidgetConfig>({
    queryKey: ['telegram-widget-config'],
    queryFn: brandingApi.getTelegramWidgetConfig,
    staleTime: 60000,
  });
  const botUsername =
    widgetConfig?.bot_username || import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '';

  const shortCode = gift.token.slice(0, 12);
  const giftCode = `GIFT-${shortCode}`;
  const isActivated = isGiftActivated(gift);
  const isAvailable = !isActivated && isGiftAvailable(gift.status);

  const statusText = isActivated
    ? t('gift.statusActivated')
    : isAvailable
      ? t('gift.statusAvailable')
      : t(getGiftStatusKey(gift.status));

  const buildShareMessage = useCallback(() => {
    // Literal "GIFT_" prefix: Telegram forwards the start param to the bot
    // verbatim (no URL-decoding), so the previously-encoded "%5F" never matched
    // the bot's `start_parameter.startswith('GIFT_')` handler.
    const botLink = botUsername ? `https://t.me/${botUsername}?start=GIFT_${shortCode}` : null;
    const cabinetLink = `${window.location.origin}/gift?tab=activate&code=${encodeURIComponent(shortCode)}`;
    return [
      t('gift.shareText'),
      '',
      botLink ? `${t('gift.shareModalActivateVia')} ${botLink}` : null,
      `${t('gift.shareModalActivateViaCabinet')} ${cabinetLink}`,
    ]
      .filter(Boolean)
      .join('\n');
  }, [shortCode, botUsername, t]);

  const handleShare = useCallback(async () => {
    const message = buildShareMessage();
    await copyToClipboard(message);
    setShowToast(true);
  }, [buildShareMessage]);

  const handleDismissToast = useCallback(() => setShowToast(false), []);

  return (
    <div className="rounded-2xl border border-dark-800/50 bg-dark-900/50 p-4">
      {/* Header: tariff name + status badge */}
      <div className="mb-3 flex items-start justify-between">
        <h3 className="text-base font-bold text-dark-50">{gift.tariff_name ?? t('gift.tariff')}</h3>
        <span
          className={cn(
            'rounded-lg px-2.5 py-1 text-xs font-bold',
            isActivated
              ? 'bg-dark-700 text-dark-400'
              : isAvailable
                ? 'bg-success-500/20 text-success-400'
                : 'bg-dark-700 text-dark-400',
          )}
        >
          {statusText}
        </span>
      </div>

      {/* Info line */}
      <p className="mb-3 text-xs text-dark-400">
        {formatGiftDate(gift.created_at)}
        {' \u2022 '}
        {gift.period_days} {t('gift.daysShort')}
        {' \u2022 '}
        {gift.device_limit} {t('gift.devicesShort', { count: gift.device_limit })}
      </p>

      {/* Gift code + actions (only when not activated) */}
      {!isActivated && (
        <>
          {/* Gift code display */}
          <div className="mb-3 rounded-xl bg-dark-800/80 px-4 py-4 text-center">
            <p className="font-mono text-base font-bold tracking-[0.15em] text-accent-400">
              {giftCode}
            </p>
          </div>

          {/* Share button — copies message and shows toast */}
          <button
            type="button"
            onClick={handleShare}
            className="flex w-full items-center justify-center gap-2 rounded-xl bg-accent-500 px-4 py-3 text-sm font-bold uppercase tracking-wider text-on-accent transition-colors hover:bg-accent-400 active:scale-[0.98]"
          >
            <ExportIcon className="h-4 w-4" />
            {t('gift.shareGift')}
          </button>
        </>
      )}

      {/* Activated by */}
      {isActivated && gift.activated_by_username && (
        <p className="mt-2 text-xs text-dark-500">
          {t('gift.activatedBy', { username: gift.activated_by_username })}
        </p>
      )}

      {/* Sent to */}
      {gift.gift_recipient_value && (
        <p className="mt-1 text-xs text-dark-500">
          {t('gift.sentTo', { recipient: gift.gift_recipient_value })}
        </p>
      )}

      {/* Copied toast — portal to escape motion.div transform context */}
      {createPortal(
        <AnimatePresence>
          {showToast && <CopiedToast onDismiss={handleDismissToast} />}
        </AnimatePresence>,
        document.body,
      )}
    </div>
  );
}

function ReceivedGiftCard({ gift }: { gift: ReceivedGift }) {
  const { t } = useTranslation();

  const statusKey = getGiftStatusKey(gift.status);
  const statusText = t(statusKey);

  return (
    <div className="rounded-2xl border border-dark-800/50 bg-dark-900/50 p-4">
      {/* Header */}
      <div className="mb-3 flex items-start justify-between">
        <h3 className="text-base font-bold text-dark-50">{gift.tariff_name ?? t('gift.tariff')}</h3>
        <span className="rounded-lg bg-dark-700 px-2.5 py-1 text-xs font-bold text-dark-400">
          {statusText}
        </span>
      </div>

      {/* Info line */}
      <p className="mb-2 text-xs text-dark-400">
        {formatGiftDate(gift.created_at)}
        {' \u2022 '}
        {gift.period_days} {t('gift.daysShort')}
        {' \u2022 '}
        {gift.device_limit} {t('gift.devicesShort', { count: gift.device_limit })}
      </p>

      {/* Sender */}
      {gift.sender_display && (
        <p className="text-xs text-dark-500">
          {t('gift.pending.from', { sender: gift.sender_display })}
        </p>
      )}

      {/* Gift message */}
      {gift.gift_message && (
        <div className="mt-2 rounded-xl bg-dark-800/50 p-3">
          <p className="text-xs italic text-dark-300">{gift.gift_message}</p>
        </div>
      )}
    </div>
  );
}

function MyGiftsTabContent() {
  const { t } = useTranslation();

  const {
    data: sentGifts,
    isLoading: sentLoading,
    error: sentError,
  } = useQuery({
    queryKey: ['gift-sent'],
    queryFn: giftApi.getSentGifts,
    staleTime: 30_000,
  });

  const {
    data: receivedGifts,
    isLoading: receivedLoading,
    error: receivedError,
  } = useQuery({
    queryKey: ['gift-received'],
    queryFn: giftApi.getReceivedGifts,
    staleTime: 30_000,
  });

  const isLoading = sentLoading || receivedLoading;

  // Split sent gifts into active (awaiting activation) and activated
  const activeGifts = useMemo(
    () => (sentGifts ?? []).filter((g) => !isGiftActivated(g)),
    [sentGifts],
  );
  const activatedGifts = useMemo(
    () => (sentGifts ?? []).filter((g) => isGiftActivated(g)),
    [sentGifts],
  );

  const hasActive = activeGifts.length > 0;
  const hasActivated = activatedGifts.length > 0;
  const hasReceived = receivedGifts && receivedGifts.length > 0;
  const isEmpty = !hasActive && !hasActivated && !hasReceived;

  if (isLoading) {
    return (
      <div className="flex items-center justify-center py-16">
        <div className="h-8 w-8 animate-spin rounded-full border-2 border-dark-600 border-t-accent-500" />
      </div>
    );
  }

  if (sentError || receivedError) {
    return (
      <div className="flex flex-col items-center gap-4 py-12 text-center">
        <p className="text-sm text-error-400">{t('gift.failedDesc')}</p>
      </div>
    );
  }

  if (isEmpty) {
    return (
      <div className="flex flex-col items-center gap-4 py-12 text-center">
        <div className="flex h-16 w-16 items-center justify-center rounded-full bg-dark-800/50">
          <InboxIcon className="h-8 w-8 text-dark-400" />
        </div>
        <h2 className="text-lg font-semibold text-dark-200">{t('gift.myGiftsEmpty')}</h2>
        <p className="max-w-xs text-sm text-dark-400">{t('gift.myGiftsEmptyDesc')}</p>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Active gifts (awaiting activation) */}
      {hasActive && (
        <div>
          <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
            {t('gift.activeGiftsTitle')}
          </h2>
          <div className="space-y-3">
            {activeGifts.map((gift) => (
              <SentGiftCard key={gift.token} gift={gift} />
            ))}
          </div>
        </div>
      )}

      {/* Activated gifts */}
      {hasActivated && (
        <div>
          <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
            {t('gift.activatedGiftsTitle')}
          </h2>
          <div className="space-y-3">
            {activatedGifts.map((gift) => (
              <SentGiftCard key={gift.token} gift={gift} />
            ))}
          </div>
        </div>
      )}

      {/* Received gifts */}
      {hasReceived && (
        <div>
          <h2 className="mb-3 text-xs font-semibold uppercase tracking-wider text-dark-400">
            {t('gift.receivedGiftsTitle')}
          </h2>
          <div className="space-y-3">
            {receivedGifts!.map((gift) => (
              <ReceivedGiftCard key={gift.token} gift={gift} />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

const tabContentVariants = {
  initial: { opacity: 0, x: 20 },
  animate: { opacity: 1, x: 0 },
  exit: { opacity: 0, x: -20 },
};

export default function GiftSubscription() {
  const { t } = useTranslation();
  const [searchParams] = useSearchParams();

  // Прогреваем кэш курсов валют для formatPrice (см. QuickPurchase).
  useCurrency();

  // URL params: ?tab=activate&code=TOKEN for auto-activation
  const urlTab = searchParams.get('tab') as TabId | null;
  const rawCode = searchParams.get('code');
  // Strip GIFT- or GIFT_ prefix if user pasted the full display code
  const urlCode = rawCode?.replace(/^GIFT[-_]/i, '') ?? rawCode;
  const [activeTab, setActiveTab] = useState<TabId>(
    urlTab === 'activate' || urlTab === 'myGifts' ? urlTab : 'buy',
  );

  // Fetch config
  const {
    data: config,
    isLoading,
    error,
  } = useQuery({
    queryKey: ['gift-config'],
    queryFn: giftApi.getConfig,
    staleTime: 30_000,
  });

  // Loading state
  if (isLoading) {
    return <LoadingSkeleton />;
  }

  // Error state
  if (error || !config) {
    const errMsg = getApiErrorMessage(error, t('gift.notFound'));
    return <ErrorState message={errMsg} />;
  }

  // Disabled state
  if (!config.is_enabled) {
    return <DisabledState />;
  }

  const tabs: { id: TabId; label: string }[] = [
    { id: 'buy', label: t('gift.tabBuy') },
    { id: 'activate', label: t('gift.tabActivate') },
    { id: 'myGifts', label: t('gift.tabMyGifts') },
  ];

  return (
    <div className="min-h-dvh">
      <div className="mx-auto max-w-2xl px-4 py-6">
        {/* Header */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.4 }}
          className="mb-6 flex items-center gap-3"
        >
          <div className="flex h-10 w-10 items-center justify-center rounded-full bg-accent-500/20">
            <GiftIcon className="h-5 w-5 text-accent-400" />
          </div>
          <h1 className="text-2xl font-bold text-dark-50">{t('gift.pageTitle')}</h1>
        </motion.div>

        {/* Tab bar */}
        <motion.div
          initial={{ opacity: 0, y: 10 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.4, delay: 0.05 }}
          className="mb-6 rounded-2xl bg-dark-800/50 p-1"
        >
          <div className="flex" role="tablist" aria-label={t('gift.pageTitle')}>
            {tabs.map((tab) => (
              <button
                key={tab.id}
                type="button"
                role="tab"
                id={`tab-${tab.id}`}
                aria-selected={activeTab === tab.id}
                aria-controls={`tabpanel-${tab.id}`}
                onClick={() => setActiveTab(tab.id)}
                className={cn(
                  'flex-1 rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-200',
                  activeTab === tab.id
                    ? 'bg-accent-500 text-on-accent shadow-sm'
                    : 'text-dark-400 hover:text-dark-200',
                )}
              >
                {tab.label}
              </button>
            ))}
          </div>
        </motion.div>

        {/* Tab content */}
        <AnimatePresence mode="wait">
          <motion.div
            key={activeTab}
            variants={tabContentVariants}
            initial="initial"
            animate="animate"
            exit="exit"
            transition={{ duration: 0.2 }}
            role="tabpanel"
            id={`tabpanel-${activeTab}`}
            aria-labelledby={`tab-${activeTab}`}
          >
            {activeTab === 'buy' && (
              <BuyTabContent config={config} onPurchaseComplete={() => setActiveTab('myGifts')} />
            )}
            {activeTab === 'activate' && <ActivateTabContent initialCode={urlCode} />}
            {activeTab === 'myGifts' && <MyGiftsTabContent />}
          </motion.div>
        </AnimatePresence>
      </div>
    </div>
  );
}
