"use strict";

const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");

const root = path.resolve(__dirname, "../..");
const read = relativePath => fs.readFileSync(path.join(root, relativePath), "utf8");
const host = read("MainWindow.xaml.cs");
const playout = read("MainWindow.Playout.cs");
const integration = read("MainWindow.NativeOperatorLog.cs");
const writer = read("src/MBN_STOCK_WEBVIEW.Infrastructure/Diagnostics/NativeOperatorLogWriter.cs");
const transitions = read("src/MBN_STOCK_WEBVIEW.Infrastructure/Diagnostics/NativeOperatorLogTransitions.cs");

function sliceBetween(source, startMarker, endMarker) {
  const start = source.indexOf(startMarker);
  const end = source.indexOf(endMarker, start + startMarker.length);
  assert.ok(start >= 0, `${startMarker} must exist`);
  assert.ok(end > start, `${endMarker} must follow ${startMarker}`);
  return source.slice(start, end);
}

test("lifecycle and provider transitions are wired with closed scalars", () => {
  const constructor = sliceBetween(host, "public MainWindow()", "private void InitializeDatabaseRuntime(");
  assert.match(constructor,
    /InitializeComponent\(\);\s*LogNativeOperatorStartup\(\);\s*EnsureCloseConfirmationAttached\(\);/u);
  const closed = sliceBetween(host, "private void OnClosed(", "private sealed record WebRequest(");
  assert.match(closed, /LogNativeOperatorShutdown\(\);/u);
  assert.match(integration,
    /Interlocked\.Exchange\(ref _nativeStartupRecorded, 1\) == 0/u);
  assert.match(integration,
    /Interlocked\.Exchange\(ref _nativeShutdownRecorded, 1\) == 0/u);

  const database = sliceBetween(
    host,
    "private async Task QueryAndPostDatabaseStatusAsync(",
    "private void PostDatabaseStatus(");
  assert.match(database,
    /ObserveNativeDatabaseState\(DataSourceKind\.Oracle, DatabaseHealthState\.NotConfigured\)/u);
  assert.match(database,
    /ObserveNativeDatabaseState\(DataSourceKind\.MariaDb, DatabaseHealthState\.NotConfigured\)/u);
  assert.match(database,
    /foreach \(var status in statuses\)[\s\S]*ObserveNativeDatabaseState\(status\.Source, status\.State\)/u);
  assert.match(database,
    /ObserveNativeDatabaseState\(DataSourceKind\.Oracle, DatabaseHealthState\.Unhealthy\)/u);
  assert.doesNotMatch(database, /ObserveNativeDatabaseState\([^\n]*(?:Message|Sql|Path|RequestId)/u);

  assert.match(playout,
    /_playoutEngine\.StatusChanged \+= OnPlayoutStatusChanged;\s*ObserveNativeTornadoState\(_playoutEngine\.Status\.State\);/u);
  assert.match(playout,
    /catch \(Exception exception\)\s*\{\s*ObserveNativeTornadoState\(PlayoutConnectionState\.Faulted\);/u);
  assert.match(playout,
    /private void OnPlayoutStatusChanged[\s\S]*?ObserveNativeTornadoState\(args\.Current\.State\);/u);
  assert.match(transitions, /_oracleState != mapped/u);
  assert.match(transitions, /_mariaDbState != mapped/u);
  assert.match(transitions, /if \(_tornadoState == mapped\)/u);
});

test("only gate-entered commands receive one request and a closed terminal outcome", () => {
  const commandHandler = sliceBetween(
    playout,
    "private async Task HandlePlayoutCommandAsync(",
    "private async Task HandlePlayoutTimeoutQuarantineAsync(");
  const gate = commandHandler.indexOf("_playoutCommandGate.WaitAsync(0)");
  const requested = commandHandler.indexOf("LogNativePlayoutCommandRequested(request.Command)");
  assert.ok(gate >= 0 && requested > gate, "request logging must happen only after gate entry");
  assert.equal(commandHandler.indexOf("LogNativePlayoutCommandRequested(request.Command)", requested + 1), -1);
  assert.match(commandHandler,
    /var result = await ExecutePlayoutCommandAsync[\s\S]*LogNativePlayoutCommandCompleted\(request\.Command, result\.Code\);/u);
  assert.match(commandHandler,
    /catch \(OperationCanceledException\)[\s\S]*NativeOperatorPlayoutCompletion\.Cancelled/u);
  assert.match(commandHandler,
    /catch \(DatabaseOperationException exception\)[\s\S]*NativeOperatorPlayoutCompletion\.Unavailable/u);
  assert.match(commandHandler,
    /LegacySceneDataException or ArgumentException\)[\s\S]*NativeOperatorPlayoutCompletion\.Rejected/u);
  assert.match(commandHandler, /catch\s*\{\s*LogNativePlayoutOutcomeUnknown\(request\.Command\);/u);
  assert.match(playout,
    /QuarantineForBrowserCorrelationLossAsync[\s\S]*LogCurrentNativePlayoutOutcomeUnknown\(\);/u);

  assert.match(integration, /"prepare" => NativeOperatorPlayoutCommand\.Prepare/u);
  assert.match(integration, /"take-in" => NativeOperatorPlayoutCommand\.TakeIn/u);
  assert.match(integration, /"next" => NativeOperatorPlayoutCommand\.Next/u);
  assert.match(integration, /"take-out" => NativeOperatorPlayoutCommand\.TakeOut/u);
  for (const result of ["Success", "Rejected", "Cancelled", "Unavailable", "Failed"]) {
    assert.match(integration, new RegExp(`PlayoutResultCode\\.${result} => NativeOperatorPlayoutCompletion\\.`));
  }
  assert.match(integration,
    /result is PlayoutResultCode\.OutcomeUnknown or PlayoutResultCode\.TimedOut[\s\S]*TryMarkOutcomeUnknown/u);
  assert.match(transitions, /if \(_currentCommand != command \|\| _terminalRecorded\)/u);
});

test("native boundary cannot receive or persist operational free-form data", () => {
  assert.match(writer, /public readonly record struct NativeOperatorLogRecord/u);
  assert.doesNotMatch(writer,
    /public\s+(?:string|Exception|JsonElement|object|long|int)\??\s+\w+\s*\{\s*get;/u);
  assert.doesNotMatch(integration, /(?:RequestId|Scene|Title|Path|Sql|Message)/u);
  assert.match(integration, /_nativeOperatorLogWriter\.TryWrite\(record\)/u);
  assert.match(integration,
    /try[\s\S]*_nativeOperatorLogWriter\.TryWrite\(record\);[\s\S]*catch/u);
  assert.doesNotMatch(integration, /TryWrite\((?:command|result|source|state|exception|payload)/u);
});
