/**
 * Pure precedence resolvers for the portfolio-list property descriptors.
 *
 * Mirrors the provenance.ts split: the DB query gathers the raw inputs (landlord
 * override snapshot, lodged/predicted EPC values, legacy property-row value) and
 * these functions own the precedence + RdSAP code→label mapping. The SQL twins
 * in epcSources.ts (resolvedPropertyTypeSql, …) mirror this logic — keep in sync.
 *
 * Precedence (ADR-0008, extended to the list by ADR-0012):
 *   landlord override → EPC-derived (lodged over predicted, codes mapped;
 *   legacy property-row fallback) → 'Unknown'  (never null)
 */
import {
  BUILT_FORM_LABELS,
  CONSTRUCTION_AGE_BANDS,
  PROPERTY_TYPE_LABELS,
} from "./epcSources";

export interface PropertyTypeInputs {
  /** property_overrides.override_value for component 'property_type', building_part 0. */
  override: string | null;
  /** Whether the property reads the new EPC graph (updated_at >= cutoff). */
  isNewApproach: boolean;
  /** epc_property(source='lodged').property_type — RdSAP code or text. */
  lodgedPropertyType: string | null;
  /** epc_property(source='lodged').dwelling_type — fallback when property_type is absent. */
  lodgedDwellingType: string | null;
  predictedPropertyType: string | null;
  predictedDwellingType: string | null;
  /** property.property_type — the legacy row value. */
  legacyPropertyType: string | null;
}

export function resolvePropertyType(i: PropertyTypeInputs): string {
  if (i.override) return i.override;
  const derived = i.isNewApproach
    ? epcPropertyType(i.lodgedPropertyType, i.lodgedDwellingType) ??
      epcPropertyType(i.predictedPropertyType, i.predictedDwellingType)
    : i.legacyPropertyType;
  return derived ?? "Unknown";
}

/**
 * One EPC source's property-type label: the RdSAP code mapped to a label (text
 * passes through), falling back to dwelling_type. Mirrors epcPropertyTypeSql.
 */
function epcPropertyType(
  propertyType: string | null,
  dwellingType: string | null,
): string | null {
  if (propertyType != null) return PROPERTY_TYPE_LABELS[propertyType] ?? propertyType;
  return dwellingType;
}

export interface BuiltFormInputs {
  /** property_overrides.override_value for component 'built_form_type', building_part 0. */
  override: string | null;
  isNewApproach: boolean;
  /** epc_property(source='lodged').built_form — RdSAP code or text. */
  lodgedBuiltForm: string | null;
  predictedBuiltForm: string | null;
  /** property.built_form — the legacy row value. */
  legacyBuiltForm: string | null;
}

export function resolveBuiltForm(i: BuiltFormInputs): string {
  if (i.override) return i.override;
  const derived = i.isNewApproach
    ? epcBuiltForm(i.lodgedBuiltForm) ?? epcBuiltForm(i.predictedBuiltForm)
    : i.legacyBuiltForm;
  return derived ?? "Unknown";
}

/** One EPC source's built-form label: RdSAP code → label, text passes through. Mirrors builtFormTypeSql. */
function epcBuiltForm(builtForm: string | null): string | null {
  if (builtForm == null) return null;
  return BUILT_FORM_LABELS[builtForm] ?? builtForm;
}

export interface ConstructionAgeInputs {
  /** property_overrides.override_value for 'construction_age_band', building_part 0 — an RdSAP letter (A..M) or "Unknown". */
  override: string | null;
  isNewApproach: boolean;
  /** The main building part's epc_building_part.construction_age_band — an RdSAP letter. */
  lodgedAgeBand: string | null;
  predictedAgeBand: string | null;
  /** property.year_built — a raw year string (legacy). Bucketed into a band. */
  legacyYearBuilt: string | null;
}

/**
 * Construction age as an RdSAP band LABEL ("before 1900" … "2023 onwards",
 * "Unknown") — the band is the underlying datum, never a single year (ADR-0012).
 * Override and EPC both store the band letter; legacy year_built is bucketed into
 * a band. Mirrors resolvedConstructionAgeSql.
 */
export function resolveConstructionAge(i: ConstructionAgeInputs): string {
  if (i.override) return bandLetterToLabel(i.override);
  const band = i.isNewApproach
    ? i.lodgedAgeBand ?? i.predictedAgeBand
    : bucketLegacyYearToBand(i.legacyYearBuilt);
  return band != null ? bandLetterToLabel(band) : "Unknown";
}

/** RdSAP band letter → band label; unrecognised values (e.g. "Unknown") pass through. */
function bandLetterToLabel(letter: string): string {
  return CONSTRUCTION_AGE_BANDS[letter]?.label ?? letter;
}

/**
 * Bucket a legacy raw year_built into its RdSAP band letter: the band with the
 * greatest start year not after the given year. Non-numeric text → null (Unknown);
 * a year below the earliest start is pre-1900 → band A. Mirrors the legacy branch
 * of resolvedConstructionAgeSql.
 */
export interface OverrideDescriptorInputs {
  /** property_overrides.override_value for this component, building_part 0. */
  override: string | null;
  isNewApproach: boolean;
  /** epc_energy_element.description for the element on the lodged EPC. */
  lodgedDescription: string | null;
  predictedDescription: string | null;
  /** The legacy property_details_epc column (walls / roof / heating). */
  legacyDescription: string | null;
}

/**
 * Free-text component descriptor (wall type / roof type / heating system):
 * override → EPC-derived (lodged over predicted, from epc_energy_element) →
 * legacy property_details_epc column → 'Unknown'. No code→label mapping — the
 * value is the description string itself. Mirrors resolvedWallTypeSql etc.
 */
export function resolveOverrideDescriptor(i: OverrideDescriptorInputs): string {
  if (i.override) return i.override;
  const derived = i.isNewApproach
    ? i.lodgedDescription ?? i.predictedDescription
    : i.legacyDescription;
  return derived ?? "Unknown";
}

export interface MainFuelInputs {
  /** property_overrides.override_value for component 'main_fuel', building_part 0. */
  override: string | null;
  isNewApproach: boolean;
  /** epc_main_heating_detail.main_fuel_type scalar on the lodged EPC — an RdSAP code ("26") or an EPR string ("Mains gas"). */
  lodgedMainFuel: string | null;
  predictedMainFuel: string | null;
  /** property_details_epc.mainfuel — the raw legacy EPC string. */
  legacyMainFuel: string | null;
}

/**
 * Main fuel raw value (folded to a label by the cell's resolveEnumLabel):
 * override → EPC-derived (new: epc_main_heating_detail scalar, lodged over
 * predicted; legacy: the property_details_epc string) → 'Unknown'. The
 * new-approach scalar is an RdSAP `main_fuel` code or an EPR string; both fold
 * into MAINFUEL_OPTIONS. Never null → 'Unknown'. Mirrors resolvedMainFuelSql.
 */
export function resolveMainFuel(i: MainFuelInputs): string {
  if (i.override) return i.override;
  const derived = i.isNewApproach
    ? i.lodgedMainFuel ?? i.predictedMainFuel
    : i.legacyMainFuel;
  return derived ?? "Unknown";
}

function bucketLegacyYearToBand(yearBuilt: string | null): string | null {
  if (yearBuilt == null || !/^[0-9]+$/.test(yearBuilt)) return null;
  const year = parseInt(yearBuilt, 10);
  let best: string | null = null;
  let bestStart = -Infinity;
  for (const [letter, { year: start }] of Object.entries(CONSTRUCTION_AGE_BANDS)) {
    if (start <= year && start > bestStart) {
      best = letter;
      bestStart = start;
    }
  }
  return best ?? "A";
}
