import assert from "node:assert/strict";
import { describe, test } from "vitest";

/**
 * Tests for pure functions in the onboarding layer.
 *
 * These functions are extracted inline (most are module-private in
 * onboarding-service.ts) and tested without DOM, subprocess, or I/O.
 *
 * See: src/web/onboarding-service.ts — resolveOnboardingLockReason,
 *      redactSensitiveText, extractErrorDetail, sanitizeMessage.
 *      web/app/api/onboarding/route.ts — isActionPayload.
 */

// ---------------------------------------------------------------------------
// Extracted pure functions (mirrors the originals — kept in sync by contract)
// ---------------------------------------------------------------------------

type OnboardingBridgeAuthRefreshPhase =
	| "idle"
	| "pending"
	| "succeeded"
	| "failed";

interface OnboardingBridgeAuthRefreshState {
	phase: OnboardingBridgeAuthRefreshPhase;
	strategy: "restart" | null;
	startedAt: string | null;
	completedAt: string | null;
	error: string | null;
}

type OnboardingLockReason =
	| "required_setup"
	| "bridge_refresh_pending"
	| "bridge_refresh_failed";

/** Mirrors resolveOnboardingLockReason from onboarding-service.ts */
function resolveOnboardingLockReason(
	requiredSatisfied: boolean,
	bridgeAuthRefresh: OnboardingBridgeAuthRefreshState,
): OnboardingLockReason | null {
	if (!requiredSatisfied) {
		return "required_setup";
	}
	if (bridgeAuthRefresh.phase === "pending") {
		return "bridge_refresh_pending";
	}
	if (bridgeAuthRefresh.phase === "failed") {
		return "bridge_refresh_failed";
	}
	return null;
}

/** Mirrors redactSensitiveText from onboarding-service.ts */
function redactSensitiveText(value: string): string {
	return value
		.replace(/sk-[A-Za-z0-9_-]{6,}/g, "[redacted]")
		.replace(/xox[baprs]-[A-Za-z0-9-]+/g, "[redacted]")
		.replace(/Bearer\s+[^\s]+/gi, "Bearer [redacted]")
		.replace(
			/([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET)["'=:\s]+)([^\s,;"']+)/gi,
			"$1[redacted]",
		);
}

/** Mirrors extractErrorDetail from onboarding-service.ts */
function extractErrorDetail(payload: unknown): string | null {
	if (!payload) return null;
	if (typeof payload === "string") return payload;
	if (typeof payload !== "object") return null;

	const record = payload as Record<string, unknown>;
	const candidates = [
		record.message,
		record.error,
		record.detail,
		record.error_description,
	];
	for (const candidate of candidates) {
		if (typeof candidate === "string" && candidate.trim().length > 0) {
			return candidate;
		}
		const nested = extractErrorDetail(candidate);
		if (nested) return nested;
	}
	return null;
}

/** Mirrors isActionPayload from web/app/api/onboarding/route.ts */
function isActionPayload(value: unknown): boolean {
	return (
		typeof value === "object" &&
		value !== null &&
		typeof (value as { action?: unknown }).action === "string"
	);
}

function idleBridgeRefresh(): OnboardingBridgeAuthRefreshState {
	return {
		phase: "idle",
		strategy: null,
		startedAt: null,
		completedAt: null,
		error: null,
	};
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe("resolveOnboardingLockReason", () => {
	test("lockReason_whenRequiredNotSatisfied_returnsRequiredSetup", () => {
		const result = resolveOnboardingLockReason(false, idleBridgeRefresh());
		assert.equal(result, "required_setup");
	});

	test("lockReason_whenRequiredSatisfiedAndRefreshPending_returnsBridgeRefreshPending", () => {
		const refresh: OnboardingBridgeAuthRefreshState = {
			...idleBridgeRefresh(),
			phase: "pending",
		};
		const result = resolveOnboardingLockReason(true, refresh);
		assert.equal(result, "bridge_refresh_pending");
	});

	test("lockReason_whenRequiredSatisfiedAndRefreshFailed_returnsBridgeRefreshFailed", () => {
		const refresh: OnboardingBridgeAuthRefreshState = {
			...idleBridgeRefresh(),
			phase: "failed",
			error: "connection refused",
		};
		const result = resolveOnboardingLockReason(true, refresh);
		assert.equal(result, "bridge_refresh_failed");
	});

	test("lockReason_whenRequiredSatisfiedAndRefreshIdle_returnsNull", () => {
		const result = resolveOnboardingLockReason(true, idleBridgeRefresh());
		assert.equal(result, null);
	});

	test("lockReason_whenRequiredSatisfiedAndRefreshSucceeded_returnsNull", () => {
		const refresh: OnboardingBridgeAuthRefreshState = {
			...idleBridgeRefresh(),
			phase: "succeeded",
		};
		const result = resolveOnboardingLockReason(true, refresh);
		assert.equal(result, null);
	});

	test("lockReason_requiredSetupTakesPrecedenceOverPendingRefresh", () => {
		// Even if bridge refresh is pending, missing provider setup wins.
		const refresh: OnboardingBridgeAuthRefreshState = {
			...idleBridgeRefresh(),
			phase: "pending",
		};
		const result = resolveOnboardingLockReason(false, refresh);
		assert.equal(result, "required_setup");
	});
});

describe("redactSensitiveText", () => {
	test("redact_whenApiKeyInText_replacesWithPlaceholder", () => {
		const result = redactSensitiveText("key: sk-abcdef1234567890");
		assert.ok(
			!result.includes("sk-abcdef"),
			`expected redaction, got: ${result}`,
		);
		assert.ok(result.includes("[redacted]"));
	});

	test("redact_whenBearerToken_replacesWithPlaceholder", () => {
		const result = redactSensitiveText(
			"Authorization: Bearer eyJhbGciOiJSUzI1NiJ9",
		);
		assert.ok(result.includes("Bearer [redacted]"), `got: ${result}`);
	});

	test("redact_whenSlackToken_replacesWithPlaceholder", () => {
		const result = redactSensitiveText("token=xoxb-abc123-def456");
		assert.ok(!result.includes("xoxb-abc123"), `got: ${result}`);
		assert.ok(result.includes("[redacted]"));
	});

	test("redact_whenNoSensitiveData_returnsUnchanged", () => {
		const input = "validation failed: network timeout";
		assert.equal(redactSensitiveText(input), input);
	});
});

describe("extractErrorDetail", () => {
	test("extractErrorDetail_whenString_returnsItself", () => {
		assert.equal(
			extractErrorDetail("something went wrong"),
			"something went wrong",
		);
	});

	test("extractErrorDetail_whenObjectWithMessage_returnsMessage", () => {
		assert.equal(extractErrorDetail({ message: "auth failed" }), "auth failed");
	});

	test("extractErrorDetail_whenObjectWithError_returnsError", () => {
		assert.equal(
			extractErrorDetail({ error: "invalid token" }),
			"invalid token",
		);
	});

	test("extractErrorDetail_whenNull_returnsNull", () => {
		assert.equal(extractErrorDetail(null), null);
	});

	test("extractErrorDetail_whenNestedMessage_returnsNestedValue", () => {
		const payload = { wrapper: { message: "inner error" } };
		// extractErrorDetail only recurses into known keys, not arbitrary keys.
		// The 'wrapper' key is not in the candidate list, so this returns null.
		assert.equal(extractErrorDetail(payload), null);
	});

	test("extractErrorDetail_whenObjectWithErrorDescription_returnsIt", () => {
		assert.equal(
			extractErrorDetail({ error_description: "token expired" }),
			"token expired",
		);
	});
});

describe("isActionPayload", () => {
	test("isActionPayload_whenObjectWithStringAction_returnsTrue", () => {
		assert.equal(isActionPayload({ action: "recheck" }), true);
	});

	test("isActionPayload_whenNull_returnsFalse", () => {
		assert.equal(isActionPayload(null), false);
	});

	test("isActionPayload_whenString_returnsFalse", () => {
		assert.equal(isActionPayload("recheck"), false);
	});

	test("isActionPayload_whenObjectWithNumericAction_returnsFalse", () => {
		assert.equal(isActionPayload({ action: 42 }), false);
	});

	test("isActionPayload_whenObjectMissingAction_returnsFalse", () => {
		assert.equal(isActionPayload({ provider: "anthropic" }), false);
	});
});
