import { loadCalculators, type CalculatorDef } from './calculatorLoader';

const domainDefinitions: Record<string, { summary: string; context: string }> = {
  length: {
    summary: 'describes the linear or spatial distance between two points.',
    context: 'Use it when explaining how far apart objects are, how long surfaces run, or how tall structures rise.',
  },
  weight: {
    summary: 'quantifies mass or the gravitational force exerted on an object.',
    context: 'Apply these units when comparing loads, weights on a scale, or quantities of matter.',
  },
  temperature: {
    summary: 'captures thermal energy along a fixed scale such as Celsius, Fahrenheit, or Kelvin.',
    context: 'They help compare how warm or cold something is and how heat flows between systems.',
  },
  volume: {
    summary: 'measures three-dimensional capacity inside containers or spaces.',
    context: 'Useful for describing liquids, gases, or any amount of space you can fill.',
  },
  fluids: {
    summary: 'captures flow rates, flux densities, and transport-related metrics.',
    context: 'Reach for these units when comparing pumps, pipes, or any movement of liquids, gases, or charged particles across an area.',
  },
  area: {
    summary: 'tracks two-dimensional surface coverage.',
    context: 'Helpful when sizing plots of land, floor space, or sheets of material.',
  },
  speed: {
    summary: 'expresses how quickly an object changes position relative to time.',
    context: 'Speed and velocity units tell you how fast you are moving across a distance or around a path.',
  },
  pressure: {
    summary: 'quantifies the amount of force applied over a specific unit of area.',
    context: 'Essential when working with gases, fluids, or loads pressing onto a surface.',
  },
  energy: {
    summary: 'represents the capacity to do work or release heat.',
    context: 'Energy units compare calories, joules, or BTUs when tracking work, heat, or stored energy.',
  },
  magnetism: {
    summary: 'captures magnetic intensity, flux, and field density.',
    context: 'Use these units when comparing magnets, coils, or magnetic flux through areas and materials.',
  },
  power: {
    summary: 'measures the rate at which energy is transferred or converted.',
    context: 'Use them to compare engines, appliances, or systems that deliver energy over time.',
  },
  data: {
    summary: 'counts digital information in bytes, bits, or other storage grains.',
    context: 'Data units help you compare file sizes, bandwidth, and storage capacity.',
  },
  time: {
    summary: 'denotes duration, intervals, or counts of ticks such as seconds, minutes, and hours.',
    context: 'These units frame anything that changes over time or requires scheduling.',
  },
  angle: {
    summary: 'measures rotation between two rays or the sweep of a circle.',
    context: 'Angle units are useful for describing turns, sweeps, bearings, and geometric layouts.',
  },
  'number-systems': {
    summary: 'names positional numeral systems such as binary, decimal, or hexadecimal.',
    context: 'They specify the base used to represent integers and guide how digits are interpreted.',
  },
  radiation: {
    summary: 'records ionizing emissions or the absorbed dose from radioactive sources.',
    context: 'Radiation units show how much exposure, decay, or energy deposition is happening.',
  },
  electrical: {
    summary: 'covers electrical quantities like current, voltage, resistance, and capacitance.',
    context: 'They keep circuit values aligned so you can balance loads, voltages, and energy transfer.',
  },
  force: {
    summary: 'describes pushes, pulls, or torque applied to an object.',
    context: 'Force units help you quantify mechanical effort, tension, or rotation in machines and systems.',
  },
  light: {
    summary: 'relates to luminous intensity, illuminance, or optical flux.',
    context: 'Use them to describe brightness, how much light reaches a surface, or the output of a lamp.',
  },
  other: {
    summary: 'covers specialized metrics that do not neatly fit the standard categories.',
    context: 'That includes frequencies, compound conversions, and other domain-specific scales such as RPM or wavelength.',
  },
};

const canonicalLabelAliases: Record<string, string> = {
  pascals: 'Pascal',
  gills: 'Gill',
  newtons: 'Newton',
};

const normalizeLabel = (label?: string): string | undefined => {
  if (!label) return undefined;
  const trimmed = label.trim();
  if (!trimmed) return undefined;
  const alias = canonicalLabelAliases[trimmed.toLowerCase()];
  return alias ?? trimmed;
};

const categoryPriority = [...Object.keys(domainDefinitions)];

const buildDefinition = (label: string, categoryKey: string): string => {
  if (!label) return '';
  const domain = domainDefinitions[categoryKey] || domainDefinitions.other;
  const description = [domain.summary, domain.context].filter(Boolean).join(' ');
  return `${label} ${description}`;
};

// Lazily built definitions cache
let definitions: Record<string, Record<string, string>> | null = null;
let buildPromise: Promise<void> | null = null;

async function ensureBuilt(): Promise<Record<string, Record<string, string>>> {
  if (definitions) return definitions;
  if (buildPromise) {
    await buildPromise;
    return definitions!;
  }

  buildPromise = loadCalculators().then(calculators => {
    const defs: Record<string, Record<string, string>> = {};
    calculators.forEach(calc => {
      const { category, labels } = calc;
      Object.values(labels).forEach(label => {
        const normalized = normalizeLabel(label);
        if (!normalized) return;
        const bucket = defs[normalized] || {};
        const text = buildDefinition(normalized, category);
        bucket[category] = text;
        defs[normalized] = bucket;
      });
    });
    definitions = defs;
  });

  await buildPromise;
  return definitions!;
}

const findByPriority = (entries: Record<string, string>, preferred?: string): string | undefined => {
  if (!entries) return undefined;
  if (preferred && entries[preferred]) return entries[preferred];
  for (const category of categoryPriority) {
    if (entries[category]) return entries[category];
  }
  const fallback = Object.values(entries).filter(Boolean);
  return fallback.length ? fallback[0] : undefined;
};

export async function getDefinition(label: string, category?: string): Promise<string | undefined> {
  const normalized = normalizeLabel(label);
  if (!normalized) return undefined;
  const defs = await ensureBuilt();
  const entries = defs[normalized];
  return findByPriority(entries, category);
}
