// src/jepx.ts
var JEPX_AREAS = [
  "hokkaido",
  "tohoku",
  "tokyo",
  "chubu",
  "hokuriku",
  "kansai",
  "chugoku",
  "shikoku",
  "kyushu"
];
var JEPX_AREA_LABELS = {
  hokkaido: "\u5317\u6D77\u9053",
  tohoku: "\u6771\u5317",
  tokyo: "\u6771\u4EAC",
  chubu: "\u4E2D\u90E8",
  hokuriku: "\u5317\u9678",
  kansai: "\u95A2\u897F",
  chugoku: "\u4E2D\u56FD",
  shikoku: "\u56DB\u56FD",
  kyushu: "\u4E5D\u5DDE"
};
var EXPECTED = {
  date: "\u5E74\u6708\u65E5",
  slot: "\u6642\u523B\u30B3\u30FC\u30C9",
  volume: "\u7D04\u5B9A\u7DCF\u91CF(kWh)",
  system: "\u30B7\u30B9\u30C6\u30E0\u30D7\u30E9\u30A4\u30B9(\u5186/kWh)"
};
var areaColumnName = (area) => `\u30A8\u30EA\u30A2\u30D7\u30E9\u30A4\u30B9${JEPX_AREA_LABELS[area]}(\u5186/kWh)`;
function parseJepxCsv(text, { cutoffDate = null } = {}) {
  const lines = text.split(/\r?\n/);
  const header = (lines[0] || "").split(",").map((s) => s.trim());
  const findCol = (name) => {
    const i = header.indexOf(name);
    if (i < 0) throw new Error(`JEPX CSV \u30D8\u30C3\u30C0\u306B\u300C${name}\u300D\u304C\u3042\u308A\u307E\u305B\u3093\uFF08\u5F62\u5F0F\u5909\u66F4\u306E\u53EF\u80FD\u6027\uFF09`);
    return i;
  };
  const col = {
    date: findCol(EXPECTED.date),
    slot: findCol(EXPECTED.slot),
    volume: findCol(EXPECTED.volume),
    system: findCol(EXPECTED.system)
  };
  const areaCol = Object.fromEntries(JEPX_AREAS.map((a) => [a, findCol(areaColumnName(a))]));
  const out = [];
  let skipped = 0;
  const num = (v) => {
    const t = (v ?? "").trim();
    if (t === "") return null;
    const n = Number(t);
    return Number.isFinite(n) ? n : null;
  };
  for (const line of lines.slice(1)) {
    if (!line.trim()) continue;
    const c = line.split(",");
    const date = (c[col.date] || "").trim();
    if (!/^\d{4}\/\d{2}\/\d{2}$/.test(date)) {
      skipped++;
      continue;
    }
    if (cutoffDate && date.replaceAll("/", "-") < cutoffDate) continue;
    const slot = num(c[col.slot]);
    const system = num(c[col.system]);
    const vol = num(c[col.volume]);
    if (slot == null || !Number.isInteger(slot) || slot < 1 || slot > 48 || system == null) {
      skipped++;
      continue;
    }
    const prices = {};
    let priceOk = true;
    for (const area of JEPX_AREAS) {
      const p = num(c[areaCol[area]]);
      if (p == null || p <= 0) {
        priceOk = false;
        break;
      }
      prices[area] = p;
    }
    if (!priceOk) {
      skipped++;
      continue;
    }
    out.push({ date, slot, system, vol, prices });
  }
  return { rows: out, skipped };
}
function toAreaSeries(rows) {
  const byDate = /* @__PURE__ */ new Map();
  for (const r of rows) {
    const iso = r.date.replaceAll("/", "-");
    let slots = byDate.get(iso);
    if (!slots) {
      slots = /* @__PURE__ */ new Map();
      byDate.set(iso, slots);
    }
    slots.set(r.slot, r);
  }
  const series = Object.fromEntries(JEPX_AREAS.map((a) => [a, {}]));
  const incompleteDates = [];
  for (const date of [...byDate.keys()].sort()) {
    const slots = byDate.get(date);
    if (!slots) continue;
    if (slots.size !== 48) {
      incompleteDates.push(date);
      continue;
    }
    for (const area of JEPX_AREAS) {
      const arr = [];
      for (let s = 1; s <= 48; s++) {
        const row = slots.get(s);
        if (row) arr.push(row.prices[area]);
      }
      series[area][date] = arr;
    }
  }
  return { series, incompleteDates };
}
var DAY_MS = 864e5;
var utcOf = (iso) => {
  const t = Date.parse(`${iso}T00:00:00Z`);
  if (!Number.isFinite(t)) throw new Error(`\u4E0D\u6B63\u306A\u65E5\u4ED8\u30AD\u30FC: ${iso}`);
  return t;
};
function assertValidAreaSeries(series, { minDays = 1, maxPriceYen = 999 } = {}) {
  const dates = Object.keys(series[JEPX_AREAS[0]] ?? {}).sort();
  if (dates.length < minDays) throw new Error(`\u65E5\u6570\u4E0D\u8DB3: ${dates.length} \u65E5 < ${minDays} \u65E5`);
  for (const area of JEPX_AREAS) {
    const areaDates = Object.keys(series[area] ?? {}).sort();
    if (areaDates.length !== dates.length || areaDates.some((d, i) => d !== dates[i]))
      throw new Error(`\u30A8\u30EA\u30A2\u9593\u3067\u65E5\u4ED8\u96C6\u5408\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093: ${area}`);
    for (const date of areaDates) {
      const prices = series[area][date];
      if (!Array.isArray(prices) || prices.length !== 48)
        throw new Error(
          `${area} ${date}: \u30B3\u30DE\u6570\u304C 48 \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08${Array.isArray(prices) ? prices.length : 0}\uFF09`
        );
      for (const p of prices) {
        if (typeof p !== "number" || !Number.isFinite(p) || p <= 0 || p > maxPriceYen)
          throw new Error(`${area} ${date}: \u4FA1\u683C\u304C\u7BC4\u56F2\u5916\u3067\u3059\uFF08${p}\u30020 < p <= ${maxPriceYen} \u5186\uFF09`);
      }
    }
  }
  for (let i = 1; i < dates.length; i++) {
    if (utcOf(dates[i]) - utcOf(dates[i - 1]) !== DAY_MS)
      throw new Error(`\u65E5\u4ED8\u304C\u9023\u7D9A\u3057\u3066\u3044\u307E\u305B\u3093: ${dates[i - 1]} \u2192 ${dates[i]}`);
  }
}
export {
  JEPX_AREAS,
  JEPX_AREA_LABELS,
  areaColumnName,
  assertValidAreaSeries,
  parseJepxCsv,
  toAreaSeries
};
//# sourceMappingURL=index.js.map