/**
 * Pricing Service
 *
 * Handles fetching and caching of Stripe pricing data to ensure fast page loads
 * and reduced API calls.
 *
 * Features:
 * - Stripe product/price fetching with caching
 * - Multi-currency localized pricing
 * - Exchange rate conversion from GBP base currency
 */
import { stripe } from '@/lib/stripe';
import { cache } from '@/lib/cache';
import { logger } from '@/lib/logger';
import type { StripeProduct } from '@/lib/types/stripe';
import type { TierId, SerializableStripeProduct, Currency } from '@/lib/types/pricing';
import { getExchangeRates } from './exchange-rate.service';
import { CURRENCY_CONFIG, formatCurrencyAmount } from '@/lib/config/currencies.config';

const PRICING_CACHE_KEY = 'stripe:pricing:products:v1';
const PRICING_CACHE_TTL = 3600; // 1 hour

/**
 * Fetch Stripe products with caching
 */
async function fetchStripeProductsRaw(): Promise<Map<string, StripeProduct>> {
  try {
    // Fetch all products with their prices
    const [products, prices] = await Promise.all([
      stripe.products.list({
        active: true,
        limit: 100,
        expand: ['data.default_price'],
      }),
      stripe.prices.list({
        active: true,
        limit: 100,
        expand: ['data.product'],
      }),
    ]);

    // Organize products by tier, excluding setup_fee products
    const tierProducts = new Map<string, StripeProduct>();

    products.data.forEach(product => {
      const tierId = product.metadata?.tier_id;
      const productType = product.metadata?.type;

      // Only include products with tier_id AND without type="setup_fee"
      if (tierId && productType !== 'setup_fee') {
        tierProducts.set(tierId, {
          ...product,
          prices: prices.data.filter(price =>
            typeof price.product === 'object' && price.product.id === product.id
          ),
        } as unknown as StripeProduct);
      }
    });

    return tierProducts;
  } catch (error) {
    logger.error('Error fetching Stripe products', error);
    return new Map();
  }
}

/**
 * Get serializable pricing products (Cached)
 */
export async function getPricingProducts(): Promise<SerializableStripeProduct[]> {
  return cache.getOrSet(
    PRICING_CACHE_KEY,
    async () => {
      const stripeProducts = await fetchStripeProductsRaw();

      // Convert to serializable format and enforce correct tier order
      const productsData: SerializableStripeProduct[] = Array.from(stripeProducts.entries())
        .map(([tierId, product]) => ({
          tierId: tierId as TierId,
          stripeProductId: product.id,
          name: product.name,
          description: product.description,
          features: product.metadata?.features ? JSON.parse(product.metadata.features) : [],
          prices: product.prices.map((price) => {
            // Handle both Stripe API naming (unit_amount) and our StripePrice type (unitAmount)
            const stripePrice = price as unknown as {
              id: string;
              currency: string;
              unit_amount: number | null;
              unitAmount?: number | null;
              recurring?: { interval: string; interval_count: number } | null;
            };
            return {
              id: stripePrice.id,
              currency: stripePrice.currency,
              unitAmount: stripePrice.unit_amount ?? stripePrice.unitAmount ?? null,
              interval: (stripePrice.recurring?.interval as 'month' | 'year') || null,
              intervalCount: stripePrice.recurring?.interval_count || null,
            };
          }),
        }))
        // Explicit tier ordering: tier1 → tier2 → tier3 → tier4 → tier5
        .sort((a, b) => {
          const tierOrder: TierId[] = ['tier1', 'tier2', 'tier3', 'tier4', 'tier5'];
          return tierOrder.indexOf(a.tierId) - tierOrder.indexOf(b.tierId);
        });

      return productsData;
    },
    PRICING_CACHE_TTL
  );
}

/**
 * Localized price information
 */
export interface LocalizedPrice {
  amount: number;
  formatted: string;
  currency: Currency;
  symbol: string;
  interval: 'month' | 'year' | null;
  originalGBP: number;
}

/**
 * Localized product with converted prices
 */
export interface LocalizedProduct {
  tierId: TierId;
  stripeProductId: string;
  name: string;
  description: string | null;
  features: string[];
  prices: LocalizedPrice[];
}

/**
 * Full localized pricing response
 */
export interface LocalizedPricingResponse {
  products: LocalizedProduct[];
  currency: Currency;
  currencyConfig: typeof CURRENCY_CONFIG[Currency];
  exchangeRate: number;
  ratesSource: 'api' | 'cache' | 'fallback';
  fetchedAt: string;
}

/**
 * Get localized pricing for a specific currency
 *
 * @param currency - Target currency (defaults to GBP)
 * @returns Localized pricing products with converted amounts
 */
export async function getLocalizedPricing(
  currency: Currency = 'GBP'
): Promise<LocalizedPricingResponse> {
  // Fetch base products and exchange rates in parallel
  const [products, ratesData] = await Promise.all([
    getPricingProducts(),
    getExchangeRates(),
  ]);

  // Get exchange rate for target currency
  const exchangeRate = currency === 'GBP'
    ? 1
    : (ratesData.rates[currency.toLowerCase()] || 1);

  // Convert all prices to target currency
  const localizedProducts: LocalizedProduct[] = products.map((product) => ({
    tierId: product.tierId,
    stripeProductId: product.stripeProductId,
    name: product.name,
    description: product.description,
    features: product.features,
    prices: product.prices.map((price) => {
      // Original GBP amount (prices from Stripe are in pence)
      const originalGBP = price.unitAmount ? price.unitAmount / 100 : 0;
      // Convert to target currency
      const convertedAmount = originalGBP * exchangeRate;
      // Round to 2 decimal places
      const roundedAmount = Math.round(convertedAmount * 100) / 100;

      return {
        amount: roundedAmount,
        formatted: formatCurrencyAmount(roundedAmount, currency),
        currency,
        symbol: CURRENCY_CONFIG[currency].symbol,
        interval: price.interval,
        originalGBP,
      };
    }),
  }));

  return {
    products: localizedProducts,
    currency,
    currencyConfig: CURRENCY_CONFIG[currency],
    exchangeRate,
    ratesSource: ratesData.source,
    fetchedAt: ratesData.fetchedAt,
  };
}

/**
 * Get a single tier's localized pricing
 *
 * @param tierId - The tier to get pricing for
 * @param currency - Target currency
 * @returns Localized product or null if not found
 */
export async function getTierLocalizedPricing(
  tierId: TierId,
  currency: Currency = 'GBP'
): Promise<LocalizedProduct | null> {
  const { products } = await getLocalizedPricing(currency);
  return products.find((p) => p.tierId === tierId) || null;
}
