//#region src/browser/humanize.ts
const DEFAULT_CONFIG = {
	typingDelayMs: 70,
	typingDelaySpreadMs: 40,
	typingPauseChance: .1,
	typingPauseRangeMs: [400, 1e3],
	keyHoldMs: [15, 35],
	mouseStepsDivisor: 8,
	mouseMinSteps: 25,
	mouseMaxSteps: 80,
	mouseWobbleMax: 1.5,
	mouseOvershootChance: .15,
	mouseOvershootPx: [3, 6],
	mouseBurstSize: [3, 5],
	mouseBurstPauseMs: [8, 18],
	clickAimDelayMs: [60, 140],
	clickHoldMs: [40, 100],
	clickInputXRange: [.05, .3],
	scrollDeltaBase: [80, 130],
	scrollDeltaVariance: .2,
	scrollPauseFastMs: [30, 80],
	scrollPauseSlowMs: [80, 200],
	scrollAccelSteps: [2, 3],
	scrollDecelSteps: [2, 3],
	scrollOvershootChance: .1,
	scrollOvershootPx: [50, 150],
	scrollSettleDelayMs: [300, 600],
	initialCursorX: [400, 700],
	initialCursorY: [45, 60]
};
const CAREFUL_CONFIG = {
	...DEFAULT_CONFIG,
	typingDelayMs: 100,
	typingDelaySpreadMs: 50,
	typingPauseChance: .15,
	typingPauseRangeMs: [500, 1200],
	keyHoldMs: [20, 45],
	mouseBurstPauseMs: [12, 25],
	clickAimDelayMs: [80, 200],
	clickHoldMs: [60, 150],
	scrollPauseFastMs: [100, 200],
	scrollPauseSlowMs: [250, 600],
	scrollSettleDelayMs: [400, 800]
};
function resolveHumanConfig(preset = "default") {
	return preset === "careful" ? { ...CAREFUL_CONFIG } : { ...DEFAULT_CONFIG };
}
function randRange(min, max) {
	return min + Math.random() * (max - min);
}
function randInt(min, max) {
	return Math.floor(randRange(min, max + 1));
}
function easeInOut(t) {
	return (1 - Math.cos(Math.PI * t)) / 2;
}
function bezier(p0, p1, p2, p3, t) {
	const u = 1 - t;
	const tt = t * t;
	const uu = u * u;
	const uuu = uu * u;
	const ttt = tt * t;
	return {
		x: uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x,
		y: uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y
	};
}
/** Generate a human-like mouse path from start to end using cubic Bezier with wobble. */
function generateMousePath(start, end, cfg) {
	const dx = end.x - start.x;
	const dy = end.y - start.y;
	const distance = Math.sqrt(dx * dx + dy * dy);
	const steps = Math.min(cfg.mouseMaxSteps, Math.max(cfg.mouseMinSteps, Math.round(distance / cfg.mouseStepsDivisor)));
	const cp1 = {
		x: start.x + dx * randRange(.2, .4) + randRange(-30, 30),
		y: start.y + dy * randRange(0, .3) + randRange(-30, 30)
	};
	const cp2 = {
		x: start.x + dx * randRange(.6, .8) + randRange(-30, 30),
		y: start.y + dy * randRange(.7, 1) + randRange(-30, 30)
	};
	const path = [];
	for (let i = 0; i <= steps; i++) {
		const raw = i / steps;
		const pt = bezier(start, cp1, cp2, end, easeInOut(raw));
		const wobble = cfg.mouseWobbleMax * (1 - raw);
		pt.x += randRange(-wobble, wobble);
		pt.y += randRange(-wobble, wobble);
		path.push(pt);
	}
	if (Math.random() < cfg.mouseOvershootChance) {
		const overshootPx = randRange(cfg.mouseOvershootPx[0], cfg.mouseOvershootPx[1]);
		const angle = Math.atan2(dy, dx);
		path.push({
			x: end.x + Math.cos(angle) * overshootPx,
			y: end.y + Math.sin(angle) * overshootPx
		});
		path.push({
			x: end.x + randRange(-1, 1),
			y: end.y + randRange(-1, 1)
		});
	}
	return path;
}
/** Generate a plan of typing actions for a given text. */
function generateTypingPlan(text, cfg) {
	const actions = [];
	const chars = [...text];
	for (let i = 0; i < chars.length; i++) {
		const hold = randRange(cfg.keyHoldMs[0], cfg.keyHoldMs[1]);
		actions.push({
			kind: "char",
			ch: chars[i],
			holdMs: hold
		});
		if (i < chars.length - 1) if (Math.random() < cfg.typingPauseChance) {
			const pause = randRange(cfg.typingPauseRangeMs[0], cfg.typingPauseRangeMs[1]);
			actions.push({
				kind: "pause",
				ms: pause
			});
		} else {
			const delay = cfg.typingDelayMs + (Math.random() - .5) * 2 * cfg.typingDelaySpreadMs;
			actions.push({
				kind: "delay",
				ms: Math.max(10, delay)
			});
		}
	}
	return actions;
}
/** Generate a scroll plan to move totalDelta pixels vertically. */
function generateScrollPlan(totalDelta, cfg) {
	const direction = totalDelta > 0 ? 1 : -1;
	const absDistance = Math.abs(totalDelta);
	const avgDelta = (cfg.scrollDeltaBase[0] + cfg.scrollDeltaBase[1]) / 2;
	const totalClicks = Math.max(3, Math.ceil(absDistance / avgDelta));
	const accelSteps = randInt(cfg.scrollAccelSteps[0], cfg.scrollAccelSteps[1]);
	const decelSteps = randInt(cfg.scrollDecelSteps[0], cfg.scrollDecelSteps[1]);
	const actions = [];
	let scrolled = 0;
	for (let i = 0; i < totalClicks; i++) {
		if (scrolled >= absDistance) break;
		let delta;
		let pause;
		if (i < accelSteps) {
			delta = randRange(60, 100);
			pause = randRange(cfg.scrollPauseSlowMs[0], cfg.scrollPauseSlowMs[1]);
		} else if (i >= totalClicks - decelSteps) {
			delta = randRange(50, 90);
			pause = randRange(cfg.scrollPauseSlowMs[0], cfg.scrollPauseSlowMs[1]);
		} else {
			delta = randRange(cfg.scrollDeltaBase[0], cfg.scrollDeltaBase[1]);
			pause = randRange(cfg.scrollPauseFastMs[0], cfg.scrollPauseFastMs[1]);
		}
		const variance = 1 + (Math.random() - .5) * 2 * cfg.scrollDeltaVariance;
		const finalDelta = Math.round(delta * variance * direction);
		actions.push({
			kind: "wheel",
			deltaY: finalDelta
		});
		actions.push({
			kind: "pause",
			ms: pause
		});
		scrolled += delta * variance;
	}
	if (Math.random() < cfg.scrollOvershootChance) {
		const overshoot = Math.round(randRange(cfg.scrollOvershootPx[0], cfg.scrollOvershootPx[1]) * direction);
		actions.push({
			kind: "wheel",
			deltaY: overshoot
		});
		const settle = randRange(cfg.scrollSettleDelayMs[0], cfg.scrollSettleDelayMs[1]);
		actions.push({
			kind: "pause",
			ms: settle
		});
		const correction = Math.round(randRange(40, 80) * -direction);
		actions.push({
			kind: "wheel",
			deltaY: correction
		});
	}
	return actions;
}
function clickTarget(bbox, isInput, cfg) {
	return {
		x: isInput ? bbox.x + bbox.width * randRange(cfg.clickInputXRange[0], cfg.clickInputXRange[1]) : bbox.x + randRange(bbox.width * .15, bbox.width * .85),
		y: bbox.y + randRange(bbox.height * .2, bbox.height * .8)
	};
}
function sleepMs(ms) {
	if (ms <= 0) return Promise.resolve();
	return new Promise((resolve) => setTimeout(resolve, Math.round(ms)));
}
async function ensureCdpSession(page) {
	return page.context().newCDPSession(page);
}
async function dispatchMouseMove(cdp, x, y) {
	await cdp.send("Input.dispatchMouseEvent", {
		type: "mouseMoved",
		x,
		y
	});
}
async function dispatchMouseEvent(cdp, eventType, x, y, clickCount) {
	await cdp.send("Input.dispatchMouseEvent", {
		type: eventType,
		x,
		y,
		button: "left",
		clickCount
	});
}
async function dispatchWheel(cdp, x, y, deltaY) {
	await cdp.send("Input.dispatchMouseEvent", {
		type: "mouseWheel",
		x,
		y,
		deltaX: 0,
		deltaY
	});
}
async function dispatchChar(cdp, ch, holdMs) {
	await cdp.send("Input.dispatchKeyEvent", {
		type: "keyDown",
		key: ch,
		text: ch
	});
	await sleepMs(holdMs);
	await cdp.send("Input.dispatchKeyEvent", {
		type: "keyUp",
		key: ch
	});
}
async function dispatchKey(cdp, key, modifiers) {
	await cdp.send("Input.dispatchKeyEvent", {
		type: "keyDown",
		key,
		...modifiers !== void 0 ? { modifiers } : {}
	});
	await cdp.send("Input.dispatchKeyEvent", {
		type: "keyUp",
		key,
		...modifiers !== void 0 ? { modifiers } : {}
	});
}
async function executeMousePath(cdp, path, cfg) {
	const burstSize = randInt(cfg.mouseBurstSize[0], cfg.mouseBurstSize[1]);
	let burstCounter = 0;
	for (const pt of path) {
		await dispatchMouseMove(cdp, pt.x, pt.y);
		burstCounter++;
		if (burstCounter >= burstSize) {
			await sleepMs(randRange(cfg.mouseBurstPauseMs[0], cfg.mouseBurstPauseMs[1]));
			burstCounter = 0;
		}
	}
}
async function getElementBBox(page, selector) {
	const bbox = await page.locator(selector).boundingBox();
	if (!bbox) throw new Error(`selector not found: ${selector}`);
	return bbox;
}
/** Humanized click on a CSS selector. */
async function humanizedClick(page, selector, preset = "default") {
	const cfg = resolveHumanConfig(preset);
	const cdp = await ensureCdpSession(page);
	try {
		let cx = randRange(cfg.initialCursorX[0], cfg.initialCursorX[1]);
		let cy = randRange(cfg.initialCursorY[0], cfg.initialCursorY[1]);
		await dispatchMouseMove(cdp, cx, cy);
		const target = clickTarget(await getElementBBox(page, selector), false, cfg);
		const path = generateMousePath({
			x: cx,
			y: cy
		}, target, cfg);
		await executeMousePath(cdp, path, cfg);
		const last = path[path.length - 1];
		if (last) {
			cx = last.x;
			cy = last.y;
		}
		await sleepMs(randRange(cfg.clickAimDelayMs[0], cfg.clickAimDelayMs[1]));
		await dispatchMouseEvent(cdp, "mousePressed", cx, cy, 1);
		await sleepMs(randRange(cfg.clickHoldMs[0], cfg.clickHoldMs[1]));
		await dispatchMouseEvent(cdp, "mouseReleased", cx, cy, 1);
	} finally {
		await cdp.detach().catch(() => {});
	}
}
/** Humanized fill: click the field, clear, type character by character. */
async function humanizedFill(page, selector, value, preset = "default") {
	const cfg = resolveHumanConfig(preset);
	const cdp = await ensureCdpSession(page);
	try {
		let cx = randRange(cfg.initialCursorX[0], cfg.initialCursorX[1]);
		let cy = randRange(cfg.initialCursorY[0], cfg.initialCursorY[1]);
		await dispatchMouseMove(cdp, cx, cy);
		const target = clickTarget(await getElementBBox(page, selector), true, cfg);
		const path = generateMousePath({
			x: cx,
			y: cy
		}, target, cfg);
		await executeMousePath(cdp, path, cfg);
		const last = path[path.length - 1];
		if (last) {
			cx = last.x;
			cy = last.y;
		}
		await sleepMs(randRange(cfg.clickAimDelayMs[0], cfg.clickAimDelayMs[1]));
		await dispatchMouseEvent(cdp, "mousePressed", cx, cy, 1);
		await sleepMs(randRange(cfg.clickHoldMs[0], cfg.clickHoldMs[1]));
		await dispatchMouseEvent(cdp, "mouseReleased", cx, cy, 1);
		await sleepMs(100);
		await dispatchKey(cdp, "a", 8);
		await sleepMs(50);
		await dispatchKey(cdp, "Backspace");
		await sleepMs(80);
		const plan = generateTypingPlan(value, cfg);
		for (const action of plan) if (action.kind === "char") await dispatchChar(cdp, action.ch, action.holdMs);
		else await sleepMs(action.ms);
	} finally {
		await cdp.detach().catch(() => {});
	}
}
/** Humanized key press with hold delay. */
async function humanizedPress(page, key, preset = "default") {
	const cfg = resolveHumanConfig(preset);
	const cdp = await ensureCdpSession(page);
	try {
		const hold = randRange(cfg.keyHoldMs[0], cfg.keyHoldMs[1]);
		await cdp.send("Input.dispatchKeyEvent", {
			type: "keyDown",
			key
		});
		await sleepMs(hold);
		await cdp.send("Input.dispatchKeyEvent", {
			type: "keyUp",
			key
		});
	} finally {
		await cdp.detach().catch(() => {});
	}
}
/** Humanized scroll by deltaY pixels using wheel events. */
async function humanizedScroll(page, deltaY, preset = "default") {
	const cfg = resolveHumanConfig(preset);
	const cdp = await ensureCdpSession(page);
	try {
		const cx = randRange(cfg.initialCursorX[0], cfg.initialCursorX[1]);
		const cy = randRange(cfg.initialCursorY[0], cfg.initialCursorY[1]);
		await dispatchMouseMove(cdp, cx, cy);
		const plan = generateScrollPlan(deltaY, cfg);
		for (const action of plan) if (action.kind === "wheel") await dispatchWheel(cdp, cx, cy, action.deltaY);
		else await sleepMs(action.ms);
	} finally {
		await cdp.detach().catch(() => {});
	}
}
//#endregion
export { generateMousePath, generateScrollPlan, generateTypingPlan, humanizedClick, humanizedFill, humanizedPress, humanizedScroll, resolveHumanConfig };

//# sourceMappingURL=humanize.js.map