// Single source of truth for BulkUpload column mapping.
//
// The mapping is stored as `field → source CSV header` (one entry per mapped
// internal field). One source header may feed several CLASSIFIER fields (e.g.
// "Property Type" → both property_type and built_form_type) but at most one
// ADDRESS field — see docs/adr/0003-classifier-triggers-as-address-subtask.md
// and docs/wip/landlord-override-frontend-plan.md (Q2.2, Q3).
//
// Classifier field `value`s mirror the Model service's ClassifiableColumn names
// (property_type / built_form_type / wall_type / roof_type) so the mapping can
// be forwarded to the lambda trigger verbatim.

export type InternalFieldKind = "address" | "classifier";

export interface InternalField {
  value: string;
  label: string;
  kind: InternalFieldKind;
  required: boolean;
  // Canonical header written into the address-matching CSV (address fields only).
  outputHeader?: string;
}

export const INTERNAL_FIELDS: InternalField[] = [
  { value: "address_1", label: "Address 1", kind: "address", required: true, outputHeader: "Address 1" },
  { value: "address_2", label: "Address 2", kind: "address", required: false, outputHeader: "Address 2" },
  { value: "address_3", label: "Address 3", kind: "address", required: false, outputHeader: "Address 3" },
  { value: "postcode", label: "Postcode", kind: "address", required: true, outputHeader: "postcode" },
  { value: "internal_reference", label: "Internal Reference", kind: "address", required: false, outputHeader: "Internal Reference" },
  { value: "property_type", label: "Property Type", kind: "classifier", required: false },
  { value: "built_form_type", label: "Built Form", kind: "classifier", required: false },
  { value: "wall_type", label: "Wall Type", kind: "classifier", required: false },
  { value: "roof_type", label: "Roof Type", kind: "classifier", required: false },
  { value: "main_fuel", label: "Main Fuel", kind: "classifier", required: false },
  { value: "glazing", label: "Glazing", kind: "classifier", required: false },
  { value: "construction_age_band", label: "Construction Age Band", kind: "classifier", required: false },
  { value: "water_heating", label: "Hot Water", kind: "classifier", required: false },
  { value: "main_heating_system", label: "Main Heating", kind: "classifier", required: false },
];

export const ADDRESS_FIELDS = INTERNAL_FIELDS.filter((f) => f.kind === "address");
export const CLASSIFIER_FIELDS = INTERNAL_FIELDS.filter((f) => f.kind === "classifier");
export const CLASSIFIER_FIELD_VALUES = CLASSIFIER_FIELDS.map((f) => f.value);
export const REQUIRED_FIELD_VALUES = INTERNAL_FIELDS.filter((f) => f.required).map((f) => f.value);

// Sentinel for an unmapped field in the UI dropdown ("Not provided").
export const NOT_PROVIDED = "";

// header → address field detection. Classifier fields are never auto-detected
// (Q2.1): mapping them is always an explicit user choice.
export function autoDetectField(header: string): string | null {
  const h = header.toLowerCase().replace(/[\s_\-]/g, "");
  if (/^(address|addr)(line)?(1|one)?$/.test(h)) return "address_1";
  if (/^(address|addr)(line)?(2|two)|^street$/.test(h)) return "address_2";
  if (/^(address|addr)(line)?(3|three)|^locality$|^town$|^city$/.test(h)) return "address_3";
  if (/^post(al)?code$|^postcode$|^pcode$/.test(h)) return "postcode";
  if (/^(internal)?ref(erence)?$|^id$/.test(h)) return "internal_reference";
  return null;
}

// Build the initial field→header mapping: keep any existing choices, then
// auto-fill address fields from the headers (first matching header wins).
export function buildInitialMapping(
  sourceHeaders: string[],
  existing?: Record<string, string>,
): Record<string, string> {
  const mapping: Record<string, string> = { ...(existing ?? {}) };
  for (const header of sourceHeaders) {
    const field = autoDetectField(header);
    if (!field) continue;
    if (mapping[field] === undefined) mapping[field] = header;
  }
  return mapping;
}

// Validation shared by the client (live) and the server (authoritative).
// Returns the first problem as a message, or null when the mapping is valid.
export function validateColumnMapping(mapping: Record<string, string>): string | null {
  for (const field of REQUIRED_FIELD_VALUES) {
    if (!mapping[field]) {
      const label = INTERNAL_FIELDS.find((f) => f.value === field)?.label ?? field;
      return `${label} must be mapped to a column.`;
    }
  }
  const usedAddressHeaders = new Set<string>();
  for (const field of ADDRESS_FIELDS) {
    const header = mapping[field.value];
    if (!header) continue;
    if (usedAddressHeaders.has(header)) {
      return `Column "${header}" is mapped to more than one address field.`;
    }
    usedAddressHeaders.add(header);
  }
  return null;
}

// The classifier subset of the mapping (category → source header) that gets
// forwarded to the lambda trigger. Address fields are intentionally excluded.
export function classifierMapping(mapping: Record<string, string>): Record<string, string> {
  const out: Record<string, string> = {};
  for (const field of CLASSIFIER_FIELD_VALUES) {
    if (mapping[field]) out[field] = mapping[field];
  }
  return out;
}
