"use strict";

const test = require("node:test");
const assert = require("node:assert/strict");
const workflow = require("../../Web/legacy-fixed-workflow.js");

const knownAliases = new Map([
  ["s5001", new Set(["5001"])],
  ["s5016", new Set(["5016"])],
  ["s50160", new Set(["50160"])],
  ["s5023", new Set(["5023"])],
  ["s5024", new Set(["5024"])],
  ["s5025", new Set(["5025"])],
  ["s5074", new Set(["5074"])],
  ["s5077", new Set(["5077"])],
  ["s5078", new Set(["5078"])],
  ["s5082", new Set(["5082"])],
  ["s5083", new Set(["5083"])],
  ["s5084", new Set(["5084"])],
  ["s5085", new Set(["5085"])],
  ["s5086", new Set(["5086"])],
  ["s50860", new Set(["50860"])],
  ["s5088", new Set(["5088"])],
  ["s6001", new Set(["6001"])],
  ["s6067", new Set(["6067"])],
  ["s8010", new Set(["8035", "8061", "8040", "8046", "8051", "8056"])],
  ["s8067", new Set(["8067", "5068", "5070", "5072"])]
]);

function action(market, section, label) {
  const result = workflow.actions.find(value =>
    value.market === market && value.section === section && value.label === label);
  assert.ok(result, `${market}/${section}/${label} action is missing`);
  return result;
}

test("runtime overseas, exchange and index assets expose all 328 leaf actions", () => {
  assert.equal(workflow.actions.length, 328);
  assert.equal(workflow.actionsForMarket("overseas").length, 78);
  assert.equal(workflow.actionsForMarket("exchange").length, 46);
  assert.equal(workflow.actionsForMarket("index").length, 204);
  assert.equal(workflow.actions.filter(value => value.available).length, 322);
  assert.equal(workflow.actions.filter(value => !value.available).length, 6);
  assert.equal(new Set(workflow.actions.map(value => value.id)).size, 328);
  assert.equal(workflow.runtimeAssets.overseas.sha256, "DBDC4EAC28BED9FD0DEABED7FE880F21CB49E48503FD3CCB95C0A29128818B2A");
  assert.equal(workflow.runtimeAssets.exchange.sha256, "7A99F8DFB5DD8D8DB514605F232A0A7973545CA6A4A46ABE2D102ADD025B586C");
  assert.equal(workflow.runtimeAssets.index.sha256, "E90ED317FD3ECC1A716AD17DD9BC21FE8B838574E1C20854BD0A886A96BA17E6");
});

test("every available fixed action creates a closed registered builder and alias", () => {
  for (const value of workflow.actions) {
    if (!value.available) {
      assert.throws(() => workflow.createFixedPlaylistEntry(value.id, { id: `entry-${value.id}` }));
      continue;
    }
    const entry = workflow.createFixedPlaylistEntry(value.id, {
      id: `entry-${value.id}`,
      now: new Date(2026, 6, 11, 9, 30, 0)
    });
    assert.equal(entry.operator.source, "legacy-fixed-workflow");
    assert.equal(entry.operator.actionId, value.id);
    assert.ok(knownAliases.get(entry.builderKey)?.has(entry.code), `${entry.builderKey}/${entry.code}`);
    assert.deepEqual(entry.aliases, [entry.code]);
    assert.equal(typeof entry.selection.groupCode, "string");
    assert.equal(typeof entry.selection.subject, "string");
    assert.equal(typeof entry.selection.graphicType, "string");
    assert.equal(typeof entry.selection.subtype, "string");
    assert.equal(typeof entry.selection.dataCode, "string");
  }
});

test("representative overseas and exchange actions preserve closed native selectors", () => {
  const gold = workflow.createFixedPlaylistEntry(
    action("overseas", "1열판기본", "국제 금").id,
    { id: "gold" });
  assert.equal(gold.builderKey, "s5001");
  assert.deepEqual(gold.selection, {
    groupCode: "COMMODITY", subject: "InternationalGold",
    graphicType: "1열판기본", subtype: "CURRENT", dataCode: ""
  });

  const fx = workflow.createFixedPlaylistEntry(
    action("exchange", "라인 그래프", "원달러_20일").id,
    { id: "fx" });
  assert.equal(fx.builderKey, "s50860");
  assert.equal(fx.selection.subject, "WonDollar");
  assert.equal(fx.selection.subtype, "TwentyDays");

  const map = workflow.createFixedPlaylistEntry(
    action("overseas", "글로벌 증시 지도", "미국 증시 지도").id,
    { id: "map" });
  assert.equal(map.builderKey, "s8067");
  assert.equal(map.code, "5068");
});

test("index variants map current, candle, trend and paged rows without free text", () => {
  const current = workflow.createFixedPlaylistEntry(
    action("index", "코스피200", "1열판기본_현재지수").id,
    { id: "current" });
  assert.deepEqual(current.selection, {
    groupCode: "KOSPI200", subject: "INDEX",
    graphicType: "1열판기본", subtype: "CURRENT", dataCode: ""
  });

  const candle = workflow.createFixedPlaylistEntry(
    action("index", "선물", "캔들그래프(예상지수)_240일").id,
    { id: "candle" });
  assert.equal(candle.builderKey, "s8010");
  assert.equal(candle.code, "8056");
  assert.equal(candle.selection.groupCode, "FUTURES_INDEX");
  assert.equal(candle.selection.subtype, "EXPECTED");

  const trend = workflow.createFixedPlaylistEntry(
    action("index", "매매동향", "외국인 매매동향_라인그래프").id,
    { id: "trend" });
  assert.equal(trend.selection.graphicType, "FOREIGN_TRADING_TREND");

  const page = workflow.createFixedPlaylistEntry(
    action("index", "12종목 현재가", "코스닥 52주 신저가").id,
    { id: "page", now: new Date(2026, 6, 11, 13, 0, 0) });
  assert.equal(page.builderKey, "s5088");
  assert.equal(page.selection.groupCode, "PAGED_DOMESTIC_KOSDAQ");
  assert.equal(page.selection.subtype, "52_WEEK_LOW");
  assert.equal(page.selection.dataCode, "2026-07-11");
});

test("fixed candle actions carry the original global 5-day and 20-day flags", () => {
  const candleAction = workflow.actions.find(value => value.builderKey === "s8010");
  assert.ok(candleAction);
  const both = workflow.createFixedPlaylistEntry(candleAction.id, {
    id: "candle-both",
    ma5: true,
    ma20: true
  });
  assert.equal(both.selection.graphicType, "MA5,MA20");
  assert.deepEqual(both.operator.movingAverages, { ma5: true, ma20: true });
  assert.deepEqual(workflow.restoreFixedPlaylistEntry(both), both);

  const ma20 = workflow.createFixedPlaylistEntry(candleAction.id, {
    id: "candle-ma20",
    ma5: false,
    ma20: true
  });
  assert.equal(ma20.selection.graphicType, "MA20");
  assert.deepEqual(workflow.restoreFixedPlaylistEntry(ma20), ma20);
  assert.equal(workflow.restoreFixedPlaylistEntry({
    ...both,
    selection: { ...both.selection, graphicType: "" }
  }), null);
});

test("pre-global-option fixed candle entries restore once through a closed flag migration", () => {
  const candleAction = workflow.actions.find(value => value.builderKey === "s8010");
  const legacy = workflow.createFixedPlaylistEntry(candleAction.id, { id: "legacy-candle" });
  const withoutMetadata = {
    ...legacy,
    operator: Object.freeze(Object.fromEntries(
      Object.entries(legacy.operator).filter(([key]) => key !== "movingAverages")))
  };
  const restored = workflow.restoreFixedPlaylistEntry(withoutMetadata);
  assert.deepEqual(restored.operator.movingAverages, { ma5: false, ma20: false });
});

test("NXT session and domestic date refresh at restore instead of remaining stale", () => {
  const nxtAction = action("index", "5단 표그래프", "코스피_NXT 거래량 상위");
  const original = workflow.createFixedPlaylistEntry(nxtAction.id, {
    id: "nxt-page", now: new Date(2026, 6, 11, 9, 0, 0)
  });
  assert.equal(original.selection.graphicType, "PRE_MARKET");
  const refreshed = workflow.restoreFixedPlaylistEntry(original, {
    now: new Date(2026, 6, 11, 15, 0, 0)
  });
  assert.equal(refreshed.selection.graphicType, "AFTER_MARKET");

  const domesticAction = action("index", "6종목 현재가", "코스피 시가총액");
  const domestic = workflow.createFixedPlaylistEntry(domesticAction.id, {
    id: "domestic-page", now: new Date(2026, 6, 10, 23, 50, 0)
  });
  const nextDay = workflow.restoreFixedPlaylistEntry(domestic, {
    now: new Date(2026, 6, 11, 0, 10, 0)
  });
  assert.equal(nextDay.selection.dataCode, "2026-07-11");
});

test("stored fixed entries fail closed after action, mapping, asset or selection tampering", () => {
  const value = workflow.createFixedPlaylistEntry(
    action("exchange", "1열판기본", "원엔").id,
    { id: "stored" });
  assert.ok(workflow.restoreFixedPlaylistEntry(value));
  assert.equal(workflow.restoreFixedPlaylistEntry({ ...value, code: "5006" }), null);
  assert.equal(workflow.restoreFixedPlaylistEntry({
    ...value,
    selection: { ...value.selection, subject: "WonDollar" }
  }), null);
  assert.equal(workflow.restoreFixedPlaylistEntry({
    ...value,
    operator: { ...value.operator, assetSha256: "0".repeat(64) }
  }), null);
  assert.equal(workflow.restoreFixedPlaylistEntry({
    ...value,
    operator: { ...value.operator, actionId: "fixed-999" }
  }), null);
});

test("all three VI placeholders preserve the original VILIST five-row outcome", () => {
  const vi = workflow.actions.filter(value => value.label === "VI 발동(수동)");
  assert.equal(vi.length, 3);
  assert.ok(vi.every(value => !value.available));
  assert.ok(vi.every(value => value.builderKey === "s5074" && value.code === "5074"));
});
