/**
 * Integration tests for the full remote steering pipeline:
 *   parse → apply → format (steer command → mode change → result display)
 *
 * Purpose: verify that a remote answer containing steering directives flows
 * correctly through all three stages and that the session mode actually reflects
 * the requested change.
 *
 * Consumer: CI gate; regression guard for remote-channel mode steering.
 */
import assert from "node:assert";
import { beforeEach, describe, test, vi } from "vitest";

// ── Mock auto/session before importing the module under test ──────────────────
const _modeState = {
	workMode: "ask",
	runControl: "manual",
	permissionProfile: "normal",
	modelMode: "auto",
};

const mockSession = {
	setMode(updates) {
		Object.assign(_modeState, updates);
	},
	getMode() {
		return { ..._modeState };
	},
};

vi.mock("../auto/session.js", () => ({
	getAutoSession: () => mockSession,
}));

vi.mock("../workflow-logger.js", () => ({
	logWarning: vi.fn(),
}));

// Import AFTER mocks are registered so module sees the mock.
const {
	parseRemoteSteeringDirectives,
	applyRemoteSteeringDirectives,
	formatRemoteSteeringResults,
} = await import("../remote-steering.js");

// ── Helpers ───────────────────────────────────────────────────────────────────

/** Reset mock mode state before each test. */
function resetMode() {
	Object.assign(_modeState, {
		workMode: "ask",
		runControl: "manual",
		permissionProfile: "normal",
		modelMode: "auto",
	});
}

/** Build a unique source key to avoid throttle carry-over between tests. */
let _srcCounter = 0;
function uniqueSrc() {
	return `test-pipeline-${_srcCounter++}`;
}

// ─────────────────────────────────────────────────────────────────────────────
// Parse stage
// ─────────────────────────────────────────────────────────────────────────────

describe("parse stage", () => {
	test("parseRemoteSteeringDirectives_when_text_has_mode_directive_returns_steering_true", () => {
		const result = parseRemoteSteeringDirectives({
			text: "/mode build",
		});
		assert.strictEqual(result.steering, true);
		assert.strictEqual(result.directives.length, 1);
		assert.deepStrictEqual(result.directives[0], {
			cmd: "mode",
			value: "build",
		});
	});

	test("parseRemoteSteeringDirectives_when_text_has_all_axes_returns_all_four_directives", () => {
		const result = parseRemoteSteeringDirectives({
			text: "/mode build /control autonomous /permission-profile trusted /model-mode deep",
		});
		assert.strictEqual(result.steering, true);
		assert.strictEqual(result.directives.length, 4);
		const cmds = result.directives.map((d) => d.cmd);
		assert.ok(cmds.includes("mode"));
		assert.ok(cmds.includes("control"));
		assert.ok(cmds.includes("permission-profile"));
		assert.ok(cmds.includes("model-mode"));
	});

	test("parseRemoteSteeringDirectives_when_answers_array_contains_directive_returns_steering_true", () => {
		const result = parseRemoteSteeringDirectives({
			answers: ["/mode review", "some other text"],
		});
		assert.strictEqual(result.steering, true);
		assert.strictEqual(result.directives[0].value, "review");
	});

	test("parseRemoteSteeringDirectives_when_value_invalid_ignores_directive", () => {
		const result = parseRemoteSteeringDirectives({
			text: "/mode notavalidmode",
		});
		assert.strictEqual(result.steering, false);
		assert.strictEqual(result.directives.length, 0);
	});

	test("parseRemoteSteeringDirectives_when_null_input_returns_no_steering", () => {
		const result = parseRemoteSteeringDirectives(null);
		assert.strictEqual(result.steering, false);
		assert.deepStrictEqual(result.directives, []);
	});
});

// ─────────────────────────────────────────────────────────────────────────────
// Apply stage
// ─────────────────────────────────────────────────────────────────────────────

describe("apply stage", () => {
	beforeEach(resetMode);

	test("applyRemoteSteeringDirectives_when_mode_directive_applies_changes_session_workMode", () => {
		const src = uniqueSrc();
		const results = applyRemoteSteeringDirectives(
			[{ cmd: "mode", value: "build" }],
			src,
		);
		assert.strictEqual(results.length, 1);
		assert.strictEqual(results[0].applied, true);
		assert.strictEqual(_modeState.workMode, "build");
	});

	test("applyRemoteSteeringDirectives_when_control_directive_applies_changes_runControl", () => {
		const src = uniqueSrc();
		applyRemoteSteeringDirectives(
			[{ cmd: "control", value: "autonomous" }],
			src,
		);
		assert.strictEqual(_modeState.runControl, "autonomous");
	});

	test("applyRemoteSteeringDirectives_when_permission_profile_directive_applies", () => {
		const src = uniqueSrc();
		applyRemoteSteeringDirectives(
			[{ cmd: "permission-profile", value: "trusted" }],
			src,
		);
		assert.strictEqual(_modeState.permissionProfile, "trusted");
	});

	test("applyRemoteSteeringDirectives_when_model_mode_directive_applies", () => {
		const src = uniqueSrc();
		applyRemoteSteeringDirectives([{ cmd: "model-mode", value: "deep" }], src);
		assert.strictEqual(_modeState.modelMode, "deep");
	});

	test("applyRemoteSteeringDirectives_when_unknown_cmd_returns_applied_false", () => {
		const src = uniqueSrc();
		const results = applyRemoteSteeringDirectives(
			[{ cmd: "notacommand", value: "whatever" }],
			src,
		);
		assert.strictEqual(results[0].applied, false);
		assert.ok(results[0].error);
	});

	test("applyRemoteSteeringDirectives_when_same_source_called_again_immediately_throttles", () => {
		// First call should succeed
		const src = uniqueSrc();
		const first = applyRemoteSteeringDirectives(
			[{ cmd: "mode", value: "build" }],
			src,
		);
		assert.strictEqual(first[0].applied, true);

		// Second immediate call with the same source must be throttled
		const second = applyRemoteSteeringDirectives(
			[{ cmd: "mode", value: "review" }],
			src,
		);
		assert.strictEqual(second[0].applied, false);
		assert.ok(second[0].error.toLowerCase().includes("throttled"));
	});

	test("applyRemoteSteeringDirectives_when_different_sources_both_applied", () => {
		const first = applyRemoteSteeringDirectives(
			[{ cmd: "mode", value: "build" }],
			uniqueSrc(),
		);
		const second = applyRemoteSteeringDirectives(
			[{ cmd: "model-mode", value: "deep" }],
			uniqueSrc(),
		);
		assert.strictEqual(first[0].applied, true);
		assert.strictEqual(second[0].applied, true);
	});

	test("applyRemoteSteeringDirectives_when_empty_directives_returns_empty", () => {
		const results = applyRemoteSteeringDirectives([], uniqueSrc());
		assert.deepStrictEqual(results, []);
	});
});

// ─────────────────────────────────────────────────────────────────────────────
// Format stage
// ─────────────────────────────────────────────────────────────────────────────

describe("format stage", () => {
	test("formatRemoteSteeringResults_when_all_applied_renders_ok_markers", () => {
		const results = [
			{ cmd: "mode", value: "build", applied: true },
			{ cmd: "control", value: "autonomous", applied: true },
		];
		const text = formatRemoteSteeringResults(results);
		assert.ok(text.includes("[ok] /mode build"), `missing ok mode: ${text}`);
		assert.ok(
			text.includes("[ok] /control autonomous"),
			`missing ok control: ${text}`,
		);
	});

	test("formatRemoteSteeringResults_when_blocked_renders_blocked_marker_and_error", () => {
		const results = [
			{ cmd: "mode", value: "review", applied: false, error: "Throttled" },
		];
		const text = formatRemoteSteeringResults(results);
		assert.ok(
			text.includes("[blocked] /mode review"),
			`missing blocked: ${text}`,
		);
		assert.ok(text.includes("Throttled"), `missing error text: ${text}`);
	});

	test("formatRemoteSteeringResults_includes_current_mode_summary", () => {
		const results = [{ cmd: "mode", value: "build", applied: true }];
		const text = formatRemoteSteeringResults(results);
		assert.ok(text.includes("Current:"), `missing Current: line: ${text}`);
	});
});

// ─────────────────────────────────────────────────────────────────────────────
// Full pipeline integration
// ─────────────────────────────────────────────────────────────────────────────

describe("full pipeline: parse → apply → format", () => {
	beforeEach(resetMode);

	test("pipeline_when_mode_build_directive_ends_with_build_in_current_line", () => {
		const src = uniqueSrc();
		// Step 1: parse
		const parsed = parseRemoteSteeringDirectives({ text: "/mode build" });
		assert.strictEqual(parsed.steering, true);

		// Step 2: apply
		const applied = applyRemoteSteeringDirectives(parsed.directives, src);
		assert.ok(applied.every((r) => r.applied));

		// Step 3: format
		const formatted = formatRemoteSteeringResults(applied);
		assert.ok(formatted.includes("build"), `expected 'build' in: ${formatted}`);
		assert.ok(formatted.includes("[ok]"), `expected [ok] in: ${formatted}`);
	});

	test("pipeline_when_all_axes_set_mode_state_reflects_all_changes", () => {
		const src = uniqueSrc();
		const parsed = parseRemoteSteeringDirectives({
			text: "/mode build /control autonomous /permission-profile trusted /model-mode deep",
		});
		assert.strictEqual(parsed.directives.length, 4);

		const applied = applyRemoteSteeringDirectives(parsed.directives, src);
		assert.ok(
			applied.every((r) => r.applied),
			`Some failed: ${JSON.stringify(applied)}`,
		);

		assert.strictEqual(_modeState.workMode, "build");
		assert.strictEqual(_modeState.runControl, "autonomous");
		assert.strictEqual(_modeState.permissionProfile, "trusted");
		assert.strictEqual(_modeState.modelMode, "deep");
	});

	test("pipeline_when_throttled_apply_returns_blocked_and_format_shows_blocked", () => {
		const src = uniqueSrc();
		// First steering succeeds
		const parsed = parseRemoteSteeringDirectives({ text: "/mode build" });
		applyRemoteSteeringDirectives(parsed.directives, src);

		// Same source, immediate second steer
		const parsed2 = parseRemoteSteeringDirectives({ text: "/mode review" });
		const applied2 = applyRemoteSteeringDirectives(parsed2.directives, src);
		const formatted = formatRemoteSteeringResults(applied2);

		assert.strictEqual(applied2[0].applied, false);
		assert.ok(
			formatted.includes("[blocked]"),
			`expected blocked: ${formatted}`,
		);
		// Mode should not have changed
		assert.strictEqual(_modeState.workMode, "build");
	});
});
