// src/utils/unitConverter.ts
import type { UnitPrice } from '../types';

const CONVERSIONS = {
  metric: {
    g: { to: 'oz', factor: 0.035274 },
    kg: { to: 'lb', factor: 2.20462 },
    ml: { to: 'fl oz', factor: 0.033814 },
    l: { to: 'fl oz', factor: 33.814 },
  },
  imperial: {
    oz: { to: 'g', factor: 28.3495 },
    lb: { to: 'kg', factor: 0.453592 },
    'fl oz': { to: 'ml', factor: 29.5735 },
  },
};

const METRIC_UNITS = Object.keys(CONVERSIONS.metric);
const IMPERIAL_UNITS = Object.keys(CONVERSIONS.imperial);

interface FormattedPrice {
  price: string;
  unit: string | null;
}

/**
 * Internal helper to convert a unit price to a target system if necessary.
 * @param unitPrice The unit price to potentially convert.
 * @param targetSystem The desired unit system.
 * @returns A new UnitPrice object if converted, otherwise the original object.
 */
const convertUnitPrice = (unitPrice: UnitPrice, targetSystem: 'metric' | 'imperial'): UnitPrice => {
  const { value, unit } = unitPrice;
  const isMetric = METRIC_UNITS.includes(unit);
  const isImperial = IMPERIAL_UNITS.includes(unit);

  let needsConversion = false;
  if (targetSystem === 'imperial' && isMetric) {
    needsConversion = true;
  } else if (targetSystem === 'metric' && isImperial) {
    needsConversion = true;
  }

  if (!needsConversion) {
    return unitPrice; // Return original object if no conversion is needed
  }

  // The source system is determined by which list the unit belongs to.
  // We access the specific map based on the detected system to satisfy TypeScript.
  const conversion = isMetric
    ? CONVERSIONS.metric[unit as keyof typeof CONVERSIONS.metric]
    : CONVERSIONS.imperial[unit as keyof typeof CONVERSIONS.imperial];

  // When converting price per unit, the factor logic is inverted.
  // e.g., to get price per lb from price per kg, you divide by the kg-to-lb factor.
  // Price/kg * (1 kg / 2.20462 lb) = (Price / 2.20462) per lb.
  const convertedValue = value / conversion.factor;

  return {
    value: convertedValue,
    unit: conversion.to,
  };
};

/**
 * Converts a unit price to the target system and formats it for display.
 * @param unitPrice The structured unit price object from the database.
 * @param system The target system ('metric' or 'imperial').
 * @returns An object with formatted price and unit strings.
 */
export const formatUnitPrice = (
  unitPrice: UnitPrice | null | undefined,
  system: 'metric' | 'imperial',
): FormattedPrice => {
  if (!unitPrice || typeof unitPrice.value !== 'number' || !unitPrice.unit) {
    return { price: '—', unit: null };
  }

  // First, convert the unit price to the correct system.
  const convertedUnitPrice = convertUnitPrice(unitPrice, system);

  const displayValue = convertedUnitPrice.value;
  const displayUnit = convertedUnitPrice.unit;

  // Convert the final calculated value (which is in cents) to dollars for formatting.
  const valueInDollars = displayValue / 100;

  // Smart formatting for price
  const formattedPrice =
    valueInDollars < 0.1
      ? valueInDollars.toLocaleString('en-US', {
          style: 'currency',
          currency: 'USD',
          minimumFractionDigits: 3,
          maximumFractionDigits: 3,
        })
      : valueInDollars.toLocaleString('en-US', {
          style: 'currency',
          currency: 'USD',
          minimumFractionDigits: 2,
          maximumFractionDigits: 2,
        });

  // Always show a unit if one exists for clarity
  if (displayUnit === 'each') {
    return { price: formattedPrice, unit: '/each' };
  }

  return { price: formattedPrice, unit: `/${displayUnit}` };
};

/**
 * Converts an imperial unit price to its metric equivalent for database storage.
 * This is used to standardize units before saving them.
 * @param unitPrice The structured unit price object, potentially in imperial units.
 * @returns A unit price object with metric units, or the original if already metric or not applicable.
 */
export const convertToMetric = (
  unitPrice: UnitPrice | null | undefined,
): UnitPrice | null | undefined => {
  if (!unitPrice || typeof unitPrice.value !== 'number' || !unitPrice.unit) {
    return unitPrice;
  }

  // The logic is now simply a call to the centralized converter.
  return convertUnitPrice(unitPrice, 'metric');
};
