"use strict";

const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const foreign = require("../../Web/legacy-foreign-index-candle-workflow.js");
const legacyRows = require("../../Web/legacy-named-row-workflow.js");
const namedPlaylists = require("../../Web/named-playlist-workflow.js");

const root = path.resolve(__dirname, "../..");
const app = fs.readFileSync(path.join(root, "Web/app.js"), "utf8");
const index = fs.readFileSync(path.join(root, "Web/index.html"), "utf8");
const bridge = fs.readFileSync(
  path.join(root, "MainWindow.NamedForeignIndexCandleRestore.cs"), "utf8");
const namedBridge = fs.readFileSync(path.join(root, "MainWindow.NamedPlaylists.cs"), "utf8");
const core = fs.readFileSync(
  path.join(root, "src/MBN_STOCK_WEBVIEW.Core/Data/LegacyForeignIndexCandleRestore.cs"),
  "utf8");

function rawRow(period = "240일", enabled = false) {
  return {
    itemIndex: 0,
    enabled,
    groupCode: "해외지수",
    subject: "다우존스",
    graphicType: "캔들그래프",
    subtype: period,
    pageText: "1/1",
    dataCode: ""
  };
}

function resolved(pending) {
  return {
    requestId: "foreign-load-1",
    retrievedAt: "2026-07-12T12:34:56+09:00",
    totalRowCount: 1,
    rows: [{
      itemIndex: pending.itemIndex,
      status: "resolved",
      targetKey: pending.targetKey,
      inputName: pending.inputName,
      symbol: pending.symbol,
      nationCode: "US",
      foreignDomesticCode: "0",
      sceneCode: "s8010",
      valueType: "PRICE",
      periodDays: pending.periodDays,
      cutCode: pending.cutCode
    }]
  };
}

test("named load performs one correlated exact foreign-index candle batch", () => {
  assert.match(index, /<script src="legacy-foreign-index-candle-workflow\.js"><\/script>[\s\S]*<script src="legacy-named-row-workflow\.js"><\/script>/u);
  assert.match(app, /MbnLegacyForeignIndexCandleWorkflow/u);
  assert.match(app, /foreignIndexCandleRestoreWorkflow\.classify/u);
  assert.match(app, /foreignIndexCandleRestoreWorkflow\.normalizeResponse/u);
  assert.match(app, /foreignIndexCandleRestoreWorkflow\.materialize/u);
  assert.match(app, /case "named-foreign-index-candle-restore-results"/u);
  assert.match(app, /case "named-foreign-index-candle-restore-error"/u);
  assert.match(namedBridge, /await PostNamedForeignIndexCandleRestoreAsync\(/u);
  assert.match(bridge, /PostMessage\("named-foreign-index-candle-restore-results"/u);
  assert.match(bridge, /PostMessage\("named-foreign-index-candle-restore-error"/u);
});

test("native and Core retain the full raw boundary and exact bound Oracle identity", () => {
  for (const field of [
    "item.ItemIndex", "item.IsEnabled", "item.Selection.GroupCode", "item.Selection.Subject",
    "item.Selection.GraphicType", "item.Selection.Subtype", "item.Page?.ToString()",
    "item.Selection.DataCode"
  ]) assert.match(bridge, new RegExp(field.replace(/[.?]/g, value => `\\${value}`), "u"));
  assert.match(core, /F_INPUT_NAME = :input_name/u);
  assert.match(core, /F_SYMB = :symbol/u);
  assert.match(core, /F_NATC = 'US'/u);
  assert.match(core, /F_FDTC = '0'/u);
  assert.match(core, /new DataQueryParameter\("input_name"/u);
  assert.match(core, /new DataQueryParameter\("symbol"/u);
  assert.doesNotMatch(core, /\b(?:INSERT\s+INTO|UPDATE\s+[A-Z_]|DELETE\s+FROM|MERGE\s+INTO)\b/iu);
  assert.doesNotMatch(bridge, /Tornado|TakeIn|PrepareAsync|ExecuteNonQuery/iu);
});

test("restore pending locks edit, page planning and finalization until its terminal result", () => {
  assert.match(app, /state\.namedPlaylist\.foreignIndexCandleRestorePending/u);
  assert.match(app, /comparisonRestorePending \|\|[\s\S]*foreignIndexCandleRestorePending/u);
  assert.match(app, /pendingForeignIndexCandles\.length === 0/u);
  assert.match(app, /if \(state\.namedPlaylist\.manualRestoreFinalized\) requestNamedPlaylistPagePlans\(\)/u);
  assert.match(app, /foreignIndexCandleRestorePending[\s\S]*state\.manualFinancialRestore\.entries\.size > 0/u);
  assert.match(bridge, /PLAYOUT_PRIORITY/u);
  assert.match(bridge, /자동 재시도하지 않습니다/u);
  assert.match(bridge, /retryable = false/u);
});

test("verified 240-day entry round-trips through the exact legacy named signature", () => {
  const raw = rawRow();
  const pending = foreign.classify(raw, { id: "db-foreign-240", fadeDuration: 6 });
  const response = foreign.normalizeResponse(resolved(pending), "foreign-load-1", [pending]);
  const entry = foreign.materialize(pending, response.rows[0]);
  assert.ok(entry);
  assert.equal(entry.code, "8056");
  assert.deepEqual(legacyRows.legacySelectionForEntry(entry), {
    groupCode: "해외지수",
    subject: "다우존스",
    graphicType: "캔들그래프",
    subtype: "240일",
    dataCode: ""
  });
  assert.equal(legacyRows.matchesCanonicalEntry(entry, raw), true);

  const fields = namedPlaylists.toLegacySevenFields(
    { ...entry, selection: legacyRows.legacySelectionForEntry(entry) },
    "1/1");
  assert.deepEqual(fields, ["0", "해외지수", "다우존스", "캔들그래프", "240일", "1/1", ""]);

  const load = namedPlaylists.normalizeLoadResponse({
    requestId: "foreign-load-roundtrip",
    definition: { programCode: "00000001", title: "Foreign" },
    retrievedAt: "2026-07-12T12:34:56+09:00",
    totalRowCount: 1,
    canMutate: true,
    writeQuarantined: false,
    rows: [raw]
  });
  const restoration = namedPlaylists.restoreRawRows(
    load,
    () => entry,
    legacyRows.matchesCanonicalEntry);
  assert.equal(restoration.rows[0].trusted, true);
  assert.equal(restoration.rows[0].entry.code, "8056");
});

test("identity and saved-signature tampering remain blocked row by row", () => {
  const raw = rawRow("20일", true);
  const pending = foreign.classify(raw, { id: "db-foreign-tamper", fadeDuration: 6 });
  const response = foreign.normalizeResponse(resolved(pending), "foreign-load-1", [pending]);
  const entry = foreign.materialize(pending, response.rows[0]);
  assert.equal(legacyRows.matchesCanonicalEntry(entry, raw), true);
  assert.equal(legacyRows.matchesCanonicalEntry(
    { ...entry, selection: { ...entry.selection, dataCode: "NAS@IXIC|US|0" } },
    raw), false);
  assert.equal(legacyRows.matchesCanonicalEntry(entry, { ...raw, subtype: "60일" }), false);
  assert.match(app, /MATERIALIZATION_MISMATCH/u);
  assert.match(app, /outcome\.status === "failed"/u);
});
