// @ts-check
/**
 * Diary Markdown/CSV and body-metrics CSV exporters — shapes aligned with
 * Android DiaryExporter / BodyMetricsExporter (not golden-byte-identical for MD
 * tables; CSV column order matches).
 */

const MICRO_CSV = [
  ["sugarG", "sugar_g"],
  ["addedSugarG", "added_sugar_g"],
  ["fiberG", "fiber_g"],
  ["saturatedFatG", "saturated_fat_g"],
  ["monounsaturatedFatG", "monounsaturated_fat_g"],
  ["polyunsaturatedFatG", "polyunsaturated_fat_g"],
  ["cholesterolMg", "cholesterol_mg"],
  ["sodiumMg", "sodium_mg"],
  ["potassiumMg", "potassium_mg"],
  ["transFatG", "trans_fat_g"],
  ["calciumMg", "calcium_mg"],
  ["ironMg", "iron_mg"],
  ["magnesiumMg", "magnesium_mg"],
  ["zincMg", "zinc_mg"],
  ["vitaminAMcg", "vitamin_a_mcg"],
  ["vitaminCMg", "vitamin_c_mg"],
  ["vitaminDMcg", "vitamin_d_mcg"],
  ["vitaminB12Mcg", "vitamin_b12_mcg"],
  ["vitaminEMg", "vitamin_e_mg"],
  ["vitaminKMcg", "vitamin_k_mcg"],
  ["folateMcg", "folate_mcg"],
  ["omega3G", "omega3_g"],
  ["caffeineMg", "caffeine_mg"],
];

const MEAL_ORDER = ["breakfast", "lunch", "dinner", "snack"];
const MEAL_LABELS = { breakfast: "Breakfast", lunch: "Lunch", dinner: "Dinner", snack: "Snack" };

function r1(n) {
  if (n == null || Number.isNaN(n)) return "";
  return String(Math.round(n * 10) / 10);
}

function sourceLabel(source) {
  if (source === "manual") return "manually_edited";
  return source || "manually_edited";
}

function csvEscape(field) {
  const s = String(field ?? "");
  if (/[",\n]/.test(s)) return `"${s.replace(/"/g, '""')}"`;
  return s;
}

/**
 * @param {import('./models.js').FoodEntry[]} entries
 * @param {{start: string, end: string}} dateRange
 * @param {{calories: number, proteinG: number, carbsG: number, fatG: number}|null} [targets]
 * @param {Array<{date: string, text: string}>} [notes]
 */
export function exportDiaryMarkdown(entries, dateRange, targets = null, notes = []) {
  /** @type {Map<string, import('./models.js').FoodEntry[]>} */
  const byDay = new Map();
  for (const e of entries) {
    if (!byDay.has(e.date)) byDay.set(e.date, []);
    byDay.get(e.date).push(e);
  }
  const noteByDate = new Map(notes.map((n) => [n.date, n.text]));
  const days = [...new Set([...byDay.keys(), ...noteByDate.keys()])].sort();
  const lines = [
    "# Food diary export",
    `Date range: ${dateRange.start} to ${dateRange.end}`,
    "Generated by Chompass",
  ];
  for (const date of days) {
    const dayEntries = byDay.get(date) || [];
    const tot = dayEntries.reduce(
      (a, e) => {
        a.calories += e.calories;
        a.proteinG += e.proteinG;
        a.carbsG += e.carbsG;
        a.fatG += e.fatG;
        return a;
      },
      { calories: 0, proteinG: 0, carbsG: 0, fatG: 0 }
    );
    lines.push("", `## ${date}`, "Totals:");
    const tc = targets?.calories ?? "—";
    const tp = targets?.proteinG != null ? Math.round(targets.proteinG) : "—";
    const tcarb = targets?.carbsG != null ? Math.round(targets.carbsG) : "—";
    const tf = targets?.fatG != null ? Math.round(targets.fatG) : "—";
    lines.push(`- Calories: ${Math.round(tot.calories)} / ${tc} kcal`);
    lines.push(`- Protein: ${r1(tot.proteinG)} / ${tp} g`);
    lines.push(`- Carbs: ${r1(tot.carbsG)} / ${tcarb} g`);
    lines.push(`- Fat: ${r1(tot.fatG)} / ${tf} g`);
    const note = noteByDate.get(date);
    if (note) lines.push("", `> ${note.replace(/\n/g, "\n> ")}`);
    for (const mt of MEAL_ORDER) {
      const items = dayEntries.filter((e) => e.mealType === mt);
      if (!items.length) continue;
      lines.push(`### ${MEAL_LABELS[mt]}`);
      lines.push(
        "| Time | Food | Weight | Calories | Protein (g) | Carbs (g) | Fat (g) | Fiber (g) | Source |"
      );
      lines.push("|---|---|---:|---:|---:|---:|---:|---:|---|");
      for (const e of items) {
        const weight = e.quantityG != null ? `${Math.round(e.quantityG)} g` : "-";
        const food = String(e.name).replace(/\|/g, "/");
        lines.push(
          `| ${e.time} | ${food} | ${weight} | ${e.calories} | ${r1(e.proteinG)} | ${r1(e.carbsG)} | ${r1(e.fatG)} | ${r1(e.fiberG) || "-"} | ${sourceLabel(e.source)} |`
        );
      }
    }
  }
  return lines.join("\n") + "\n";
}

/**
 * @param {import('./models.js').FoodEntry[]} entries
 */
export function exportDiaryCsv(entries) {
  const header = [
    "date",
    "meal",
    "time",
    "food",
    "weight_g",
    "calories",
    "protein_g",
    "carbs_g",
    "fat_g",
    ...MICRO_CSV.map(([, wire]) => wire),
    "source",
    "note",
  ].join(",");
  const rows = [header];
  const sorted = entries.slice().sort((a, b) => `${a.date}T${a.time}`.localeCompare(`${b.date}T${b.time}`));
  for (const e of sorted) {
    const cols = [
      e.date,
      e.mealType,
      e.time,
      e.name,
      e.quantityG != null ? String(Math.round(e.quantityG)) : "",
      String(e.calories),
      r1(e.proteinG),
      r1(e.carbsG),
      r1(e.fatG),
      ...MICRO_CSV.map(([field]) => r1(/** @type {any} */ (e)[field])),
      sourceLabel(e.source),
      e.note ?? "",
    ];
    rows.push(cols.map(csvEscape).join(","));
  }
  return rows.join("\n") + "\n";
}

/**
 * @param {{weights: import('./models.js').WeightEntry[], bodyFat: import('./models.js').BodyFatEntry[], measurements: import('./models.js').BodyMeasurement[]}} data
 */
export function exportBodyMetricsCsv(data) {
  const rows = ["metric,timestamp,value,unit"];
  const fmt = (n) => (Math.round(n * 100) / 100).toFixed(2);
  for (const w of data.weights.slice().sort((a, b) => a.date.localeCompare(b.date))) {
    rows.push(["weight", csvEscape(w.date), fmt(w.weightKg), "kg"].join(","));
  }
  for (const bf of data.bodyFat.slice().sort((a, b) => a.date.localeCompare(b.date))) {
    // stored as fraction 0-1 internally in some paths; body-metrics-format uses percent on wire
    const pct = bf.bodyFatPercent > 1 ? bf.bodyFatPercent : bf.bodyFatPercent * 100;
    rows.push(["body_fat", csvEscape(bf.date), fmt(pct), "percent"].join(","));
  }
  const sites = [
    ["neckCm", "neck"],
    ["waistCm", "waist"],
    ["hipsCm", "hips"],
    ["chestCm", "chest"],
    ["upperArmCm", "upper_arm"],
    ["thighCm", "thigh"],
    ["calfCm", "calf"],
    ["wristCm", "wrist"],
  ];
  for (const m of data.measurements.slice().sort((a, b) => a.date.localeCompare(b.date))) {
    for (const [field, metric] of sites) {
      const v = /** @type {any} */ (m)[field];
      if (v != null) rows.push([metric, csvEscape(m.date), fmt(v), "cm"].join(","));
    }
  }
  return rows.join("\n") + "\n";
}

/**
 * Filter entries to a named range relative to `todayIso`.
 * @param {import('./models.js').FoodEntry[]} entries
 * @param {"today"|"week"|"month"|"all"} range
 * @param {string} [todayIso]
 */
export function filterDiaryRange(entries, range, todayIso = new Date().toISOString().slice(0, 10)) {
  if (range === "all") return entries;
  const [y, m, d] = todayIso.split("-").map(Number);
  const today = new Date(y, m - 1, d);
  const start = new Date(today);
  if (range === "week") {
    start.setDate(today.getDate() - 6);
  } else if (range === "month") {
    start.setDate(today.getDate() - 29);
  }
  const pad = (n) => String(n).padStart(2, "0");
  const startIso = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}`;
  return entries.filter((e) => e.date >= startIso && e.date <= todayIso);
}
