/**
 * useDynamicPricing Hook
 *
 * Extracts and centralizes all dynamic pricing related calculations
 * for the checkout summary component.
 *
 * Final Freeze Architecture: Quotes are created at Submit time only.
 * This hook handles preview-time display calculations.
 */

import { useMemo } from 'react';
import type { TCheckoutSession, TLineItemExpanded, TPaymentCurrency, TPaymentIntent } from '@blocklet/payment-types';
import { BN, fromTokenToUnit, fromUnitToToken } from '@ocap/util';
import {
  formatDateTime,
  formatDynamicPrice,
  formatExchangeRate,
  formatUsdAmount,
  getUsdAmountFromTokenUnits,
} from '../libs/util';

export interface LiveRateInfo {
  rate?: string;
  provider_id?: string;
  provider_name?: string;
  provider_display?: string; // Human-readable: "CoinGecko" or "CoinGecko (2 sources)"
  base_currency?: string;
  timestamp_ms?: number;
  fetched_at?: number; // When we fetched the data (updates every 30s)
}

export interface LiveQuoteSnapshot {
  id: string;
  quoted_amount: string;
  exchange_rate: string;
  expires_at: number;
  rate_timestamp_ms?: number | null;
  renewed?: boolean;
}

export interface DiscountInfo {
  promotion_code?: string;
  coupon?: string;
  discount_amount?: string;
  coupon_details?: {
    percent_off?: number;
    amount_off?: string;
    currency_id?: string;
    currency_options?: Record<string, { amount_off?: string }>;
  };
}

export interface DynamicPricingOptions {
  items: TLineItemExpanded[];
  currency: TPaymentCurrency;
  liveRate?: LiveRateInfo;
  liveQuoteSnapshot?: LiveQuoteSnapshot;
  checkoutSession?: TCheckoutSession;
  paymentIntent?: TPaymentIntent | null;
  locale?: string;
  isStripePayment?: boolean;
  isSubscription?: boolean;
  slippageConfig?: {
    mode?: 'percent' | 'rate';
    percent?: number | null;
    min_acceptable_rate?: string;
    base_currency?: string;
  };
  trialInDays?: number;
  trialEnd?: number;
  discounts?: DiscountInfo[];
}

export interface RateInfo {
  exchangeRate: string | null;
  baseCurrency: string;
  providerName: string | null;
  providerId: string | null;
  timestampMs: number | null;
  fetchedAt: number | null; // When we fetched (for display)
}

export interface QuoteMeta {
  exchangeRate: string | null;
  baseCurrency: string;
  expiresAt: number | null;
  providerName: string | null;
  providerId: string | null;
  rateTimestampMs: number | null;
  slippagePercent: number | null;
}

/**
 * Extract quote metadata from line items
 */
function extractQuoteMeta(items: TLineItemExpanded[]): QuoteMeta | null {
  if (!items?.length) {
    return null;
  }

  let exchangeRate: string | null = null;
  let baseCurrency: string | null = null;
  let expiresAt: number | null = null;
  let providerName: string | null = null;
  let providerId: string | null = null;
  let rateTimestampMs: number | null = null;
  let slippagePercent: number | null = null;

  items.forEach((item) => {
    const price = item.upsell_price || item.price;
    const rate = (item as any)?.exchange_rate;
    if (!exchangeRate && rate) {
      exchangeRate = rate;
    }
    const base = (price as any)?.base_currency;
    if (!baseCurrency && base) {
      baseCurrency = base;
    }
    const expires = (item as any)?.expires_at;
    if (expires) {
      expiresAt = expiresAt === null ? expires : Math.min(expiresAt, expires);
    }
    const itemProviderName = (item as any)?.rate_provider_name;
    if (!providerName && itemProviderName) {
      providerName = itemProviderName;
    }
    const itemProviderId = (item as any)?.rate_provider_id;
    if (!providerId && itemProviderId) {
      providerId = itemProviderId;
    }
    const itemRateTimestamp = (item as any)?.rate_timestamp_ms;
    if (!rateTimestampMs && Number.isFinite(Number(itemRateTimestamp))) {
      rateTimestampMs = Number(itemRateTimestamp);
    }
    const itemSlippage = (item as any)?.slippage_percent;
    if (slippagePercent === null && Number.isFinite(Number(itemSlippage))) {
      slippagePercent = Number(itemSlippage);
    }
  });

  return {
    exchangeRate,
    baseCurrency: baseCurrency || 'USD',
    expiresAt,
    providerName,
    providerId,
    rateTimestampMs,
    slippagePercent,
  };
}

/**
 * Calculate token amount from USD base amount and exchange rate
 * @param items - Line items to calculate
 * @param rate - Exchange rate
 * @param currencyDecimal - Currency decimal places
 * @param trialing - Whether currently in trial period (skips recurring items if true)
 */
function calculateTokenAmount(
  items: TLineItemExpanded[],
  rate: string,
  currencyDecimal: number,
  trialing: boolean = false
): string | null {
  // Get USD base amount from items
  const usdTotal = items.reduce((acc, item) => {
    const price = item.upsell_price || item.price;
    // Skip recurring items when in trial period (they don't need payment during trial)
    if (trialing && price?.type === 'recurring') {
      return acc;
    }
    // Skip metered items (pay-as-you-go, no upfront payment)
    if (price?.type === 'recurring' && price?.recurring?.usage_type === 'metered') {
      return acc;
    }
    const baseAmount = (price as any)?.base_amount;
    if (baseAmount) {
      const quantity = item.quantity || 1;
      return acc + Number(baseAmount) * quantity;
    }
    return acc;
  }, 0);

  if (usdTotal <= 0) {
    return null;
  }

  const rateNum = Number(rate);
  if (rateNum <= 0) {
    return null;
  }

  // Calculate token amount: usdTotal / exchangeRate
  const tokenAmount = usdTotal / rateNum;
  const tokenAmountUnit = fromTokenToUnit(tokenAmount.toFixed(currencyDecimal || 8), currencyDecimal || 8);
  return tokenAmountUnit.toString();
}

/**
 * Extract quote lock timestamp from various sources
 */
function extractQuoteLockedAt(
  paymentIntent?: TPaymentIntent | null,
  checkoutSession?: TCheckoutSession
): number | null {
  // @ts-ignore
  if (paymentIntent?.quote_locked_at) {
    // @ts-ignore
    const lockedAtMs = new Date(paymentIntent.quote_locked_at).getTime();
    if (Number.isFinite(lockedAtMs)) {
      return Math.floor(lockedAtMs / 1000);
    }
  }
  const metaLockedAt = checkoutSession?.metadata?.quote_locked_at;
  if (typeof metaLockedAt === 'number') {
    return metaLockedAt;
  }
  if (typeof metaLockedAt === 'string') {
    const parsed = Number(metaLockedAt);
    if (Number.isFinite(parsed)) {
      return parsed;
    }
  }
  return null;
}

/**
 * Custom hook for dynamic pricing calculations
 */
export function useDynamicPricing(options: DynamicPricingOptions) {
  const {
    items,
    currency,
    liveRate,
    liveQuoteSnapshot,
    checkoutSession,
    paymentIntent,
    locale = 'en',
    isStripePayment = false,
    isSubscription = checkoutSession?.mode === 'subscription' || checkoutSession?.mode === 'setup',
    slippageConfig,
    trialInDays = 0,
    trialEnd = 0,
    discounts,
  } = options;

  // Check if currently in trial period
  const currentTime = Math.floor(Date.now() / 1000);
  const trialing = trialInDays > 0 || trialEnd > currentTime;

  // Check if any items have dynamic pricing
  const hasDynamicPricing = useMemo(
    () => items.some((item) => ((item.upsell_price || item.price) as any)?.pricing_type === 'dynamic'),
    [items]
  );

  // Extract quote metadata from line items
  const quoteMeta = useMemo(() => (hasDynamicPricing ? extractQuoteMeta(items) : null), [items, hasDynamicPricing]);

  // Combine live rate with quote metadata
  const rateInfo = useMemo<RateInfo>(
    () => ({
      exchangeRate: isStripePayment ? null : liveRate?.rate || quoteMeta?.exchangeRate || null,
      baseCurrency: liveRate?.base_currency || quoteMeta?.baseCurrency || 'USD',
      providerName: isStripePayment ? null : liveRate?.provider_name || quoteMeta?.providerName || null,
      providerId: isStripePayment ? null : liveRate?.provider_id || quoteMeta?.providerId || null,
      timestampMs: isStripePayment ? null : liveRate?.timestamp_ms || quoteMeta?.rateTimestampMs || null,
      fetchedAt: isStripePayment ? null : liveRate?.fetched_at || null, // When we fetched the data
    }),
    [liveRate, quoteMeta, isStripePayment]
  );

  // Calculate token amount from live rate (Final Freeze: no quote during preview)
  // When trialing, skip recurring items as they don't need payment during trial
  const calculatedTokenAmount = useMemo(() => {
    if (isStripePayment || !hasDynamicPricing || !liveRate?.rate) {
      return null;
    }
    return calculateTokenAmount(items, liveRate.rate, currency.decimal, trialing);
  }, [hasDynamicPricing, liveRate?.rate, items, currency.decimal, isStripePayment, trialing]);

  // Calculate discount amount dynamically when rate changes
  // For percent_off: subtotal * percent_off / 100
  // For amount_off: min(amount_off, subtotal) - because discount can't exceed subtotal
  const calculatedDiscountAmount = useMemo(() => {
    if (!discounts?.length) {
      return null;
    }

    const couponDetails = discounts[0]?.coupon_details;
    if (!couponDetails) {
      return null;
    }

    // Stripe payment: calculate discount from USD amounts
    if (isStripePayment) {
      // Calculate discountable subtotal in cents (USD smallest unit)
      const discountableSubtotalCents = items.reduce((sum, item) => {
        // Only include discountable items
        if (!(item as any).discountable) {
          return sum;
        }

        const price = item.upsell_price || item.price;

        // Skip recurring items during trial
        if (trialing && price?.type === 'recurring') {
          return sum;
        }

        // Skip metered items
        if (price?.type === 'recurring' && price?.recurring?.usage_type === 'metered') {
          return sum;
        }

        // For dynamic pricing, use base_amount (USD dollars), not unit_amount (token wei)
        const isDynamic = (price as any)?.pricing_type === 'dynamic';
        const baseAmount = (price as any)?.base_amount;

        let amountCents: number;
        if (isDynamic && baseAmount !== undefined && baseAmount !== null) {
          // base_amount is in dollars, convert to cents
          amountCents = Number(baseAmount) * 100;
        } else {
          // For non-dynamic pricing, unit_amount is in cents
          const unitAmount = price?.unit_amount;
          if (!unitAmount) {
            return sum;
          }
          amountCents = Number(unitAmount);
        }

        const quantity = item.quantity || 1;
        return sum + amountCents * quantity;
      }, 0);

      if (discountableSubtotalCents <= 0) {
        return null;
      }

      // Percent off discount: subtotal * percent_off / 100
      if (couponDetails.percent_off && couponDetails.percent_off > 0) {
        const discountCents = Math.round((discountableSubtotalCents * couponDetails.percent_off) / 100);
        return discountCents.toString();
      }

      // Fixed amount discount: min(amount_off, subtotal)
      if (couponDetails.amount_off) {
        // For Stripe, use USD amount_off directly (should be in cents)
        const amountOffCents = Number(couponDetails.amount_off);
        if (Number.isFinite(amountOffCents) && amountOffCents > 0) {
          return Math.min(amountOffCents, discountableSubtotalCents).toString();
        }
      }

      return null;
    }

    // Dynamic pricing: calculate discount from token amounts
    if (!hasDynamicPricing || !liveRate?.rate) {
      return null;
    }

    const rateNum = Number(liveRate.rate);
    if (rateNum <= 0) {
      return null;
    }

    // Calculate discountable subtotal in tokens (shared by both percent_off and amount_off)
    const discountableSubtotal = items.reduce((sum, item) => {
      // Only include discountable items
      if (!(item as any).discountable) {
        return sum;
      }

      const price = item.upsell_price || item.price;

      // Skip recurring items during trial
      if (trialing && price?.type === 'recurring') {
        return sum;
      }

      // Skip metered items
      if (price?.type === 'recurring' && price?.recurring?.usage_type === 'metered') {
        return sum;
      }

      const baseAmount = (price as any)?.base_amount;
      if (!baseAmount) {
        return sum;
      }

      const quantity = item.quantity || 1;
      const tokenAmount = (Number(baseAmount) * quantity) / rateNum;
      return sum + tokenAmount;
    }, 0);

    if (discountableSubtotal <= 0) {
      return null;
    }

    // Percent off discount: subtotal * percent_off / 100
    if (couponDetails.percent_off && couponDetails.percent_off > 0) {
      const discountAmount = (discountableSubtotal * couponDetails.percent_off) / 100;
      const discountAmountUnit = fromTokenToUnit(discountAmount.toFixed(currency.decimal || 8), currency.decimal || 8);
      return discountAmountUnit.toString();
    }

    // Fixed amount discount: min(amount_off, subtotal)
    // When subtotal changes due to rate change, discount may also change
    if (couponDetails.amount_off) {
      // Get amount_off for current currency
      const amountOff =
        couponDetails.currency_id === currency.id
          ? couponDetails.amount_off
          : couponDetails.currency_options?.[currency.id]?.amount_off;

      if (!amountOff) {
        return null;
      }

      // Convert subtotal to unit for comparison
      const subtotalUnit = fromTokenToUnit(discountableSubtotal.toFixed(currency.decimal || 8), currency.decimal || 8);
      const amountOffBN = new BN(amountOff);
      const subtotalBN = new BN(subtotalUnit);

      // Return min(amount_off, subtotal)
      return BN.min(amountOffBN, subtotalBN).toString();
    }

    return null;
  }, [isStripePayment, hasDynamicPricing, liveRate?.rate, discounts, items, trialing, currency.decimal, currency.id]);

  // Format rate for display
  const rateDisplay = useMemo(() => {
    if (!rateInfo.exchangeRate) {
      return null;
    }
    const formattedRate = formatExchangeRate(rateInfo.exchangeRate);
    if (!formattedRate) {
      return null;
    }
    if (rateInfo.baseCurrency === 'USD') {
      return `$${formattedRate}`;
    }
    return `${formattedRate} ${rateInfo.baseCurrency}`;
  }, [rateInfo]);

  // Quote lock status
  const quoteLockedAt = useMemo(
    () => extractQuoteLockedAt(paymentIntent, checkoutSession),
    [paymentIntent, checkoutSession]
  );

  const isPriceLocked = useMemo(() => {
    if (!quoteLockedAt) return false;
    const now = Math.floor(Date.now() / 1000);
    const lockRemaining = quoteLockedAt + 180 - now;
    return lockRemaining > 0;
  }, [quoteLockedAt]);

  const lockExpired = useMemo(() => {
    if (!quoteLockedAt) return false;
    const now = Math.floor(Date.now() / 1000);
    const lockRemaining = quoteLockedAt + 180 - now;
    return lockRemaining <= 0;
  }, [quoteLockedAt]);

  // Current slippage value
  const currentSlippagePercent = useMemo(() => {
    let slippageValue: number | null | undefined = quoteMeta?.slippagePercent;
    if (slippageValue === null || slippageValue === undefined) {
      slippageValue =
        slippageConfig?.percent ??
        (checkoutSession as any)?.metadata?.slippage?.percent ??
        (checkoutSession as any)?.slippage_percent;
    }
    if (slippageValue === null || slippageValue === undefined) {
      slippageValue = 0.5; // default
    }
    return slippageValue as number;
  }, [quoteMeta?.slippagePercent, slippageConfig?.percent, checkoutSession]);

  // Provider display string - use provider_display from backend for human-readable format
  const providerDisplay = useMemo(() => {
    const fallback = '—';
    // Prefer provider_display from liveRate (e.g., "CoinGecko (2 sources)")
    if (liveRate?.provider_display) {
      return liveRate.provider_display;
    }
    // Fallback to provider name/id
    if (!rateInfo.providerName && !rateInfo.providerId) {
      return fallback;
    }
    return rateInfo.providerName || rateInfo.providerId || fallback;
  }, [liveRate?.provider_display, rateInfo.providerName, rateInfo.providerId]);

  // Format total amount for display
  const formatTotalDisplay = (totalAmountValue: string, fallback = '—') => {
    // Final Freeze: Use calculated token amount from live rate
    if (hasDynamicPricing && calculatedTokenAmount) {
      const displayAmount = fromUnitToToken(calculatedTokenAmount, currency.decimal);
      const numericValue = Number(displayAmount);
      if (Number.isFinite(numericValue)) {
        return formatDynamicPrice(displayAmount, true, 6);
      }
    }
    // Fallback to liveQuoteSnapshot if available (for backward compatibility)
    if (hasDynamicPricing && liveQuoteSnapshot?.quoted_amount) {
      const snapshotAmount = fromUnitToToken(liveQuoteSnapshot.quoted_amount, currency.decimal);
      const numericValue = Number(snapshotAmount);
      if (Number.isFinite(numericValue)) {
        return formatDynamicPrice(snapshotAmount, true, 6);
      }
    }
    if (totalAmountValue === null || totalAmountValue === undefined || totalAmountValue === '') {
      return fallback;
    }
    const numericValue = Number(totalAmountValue);
    if (!Number.isFinite(numericValue)) {
      return fallback;
    }
    return formatDynamicPrice(totalAmountValue, hasDynamicPricing, 6);
  };

  // Calculate USD equivalent display
  const calculateUsdDisplay = (totalAmountValue: string) => {
    const fallback = '—';
    if (!hasDynamicPricing || !totalAmountValue || totalAmountValue === fallback) {
      return null;
    }
    const numericValue = Number(totalAmountValue);
    if (!Number.isFinite(numericValue) || numericValue < 0) {
      return null;
    }
    // For zero amount (e.g., free trial), display $0
    if (numericValue === 0) {
      return formatUsdAmount('0', locale);
    }
    const { exchangeRate } = rateInfo;
    if (!exchangeRate) {
      return null;
    }
    const totalAmountInUnits = fromTokenToUnit(totalAmountValue, currency.decimal);
    const totalUsd = getUsdAmountFromTokenUnits(new BN(totalAmountInUnits), currency.decimal, exchangeRate);
    if (!totalUsd) {
      return null;
    }
    return formatUsdAmount(totalUsd, locale);
  };

  // Build quote detail rows for display
  const buildQuoteDetailRows = (t: (key: string) => string) => {
    if (!hasDynamicPricing) {
      return [];
    }

    const rows: Array<{ label: string; value: string | React.ReactNode; isSlippage?: boolean; tooltip?: string }> = [];
    const fallback = '—';

    // Only show provider and updatedAt if we have rate info
    if (rateDisplay) {
      // Use fetched_at (when we fetched) instead of timestamp_ms (provider's data timestamp)
      // This shows users that we're actively fetching, even if provider data hasn't changed
      const displayTimestamp = rateInfo.fetchedAt || rateInfo.timestampMs;
      const updatedAt = displayTimestamp ? formatDateTime(displayTimestamp) : fallback;

      rows.push(
        { label: t('payment.checkout.quote.detailProvider'), value: providerDisplay },
        { label: t('payment.checkout.quote.detailUpdatedAt'), value: updatedAt }
      );
    }

    // Always show slippage for subscriptions with dynamic pricing (even metered without current rate)
    if (isSubscription) {
      let slippageDisplay = `${currentSlippagePercent}%`;
      if (slippageConfig?.mode === 'rate' && slippageConfig.min_acceptable_rate) {
        const formattedRate = formatExchangeRate(slippageConfig.min_acceptable_rate);
        const displayRate = formattedRate || slippageConfig.min_acceptable_rate;
        const displayCurrency = slippageConfig.base_currency || rateInfo.baseCurrency || 'USD';
        const displayText = displayCurrency === 'USD' ? `$${displayRate}` : `${displayRate} ${displayCurrency}`;
        slippageDisplay = `${displayText}`;
      }

      rows.push({
        label: t('payment.checkout.quote.detailSlippage'),
        value: slippageDisplay,
        isSlippage: true,
        tooltip: t('payment.checkout.quote.slippage.tooltip'),
      });
    }

    return rows;
  };

  return {
    // Flags
    hasDynamicPricing,
    isPriceLocked,
    lockExpired,

    // Data
    quoteMeta,
    rateInfo,
    quoteLockedAt,
    calculatedTokenAmount,
    calculatedDiscountAmount,
    currentSlippagePercent,

    // Display helpers
    rateDisplay,
    providerDisplay,
    formatTotalDisplay,
    calculateUsdDisplay,
    buildQuoteDetailRows,
  };
}

export default useDynamicPricing;
