import {
	getContactForVisitor,
	mergeContactMetadata,
} from "@api/db/queries/contact";
import type { VisitorRecord } from "@api/db/queries/visitor";
import {
	findVisitorForWebsite,
	updateVisitorForWebsite,
} from "@api/db/queries/visitor";
import { env } from "@api/env";
import { trackVisitorActivity, trackVisitorEvent } from "@api/lib/tinybird-sdk";
import {
	flattenVisitorTrackingContext,
	resolveFirstTouchAttribution,
} from "@api/lib/visitor-attribution";
import { realtime } from "@api/realtime/emitter";
import { lookupGeoIp } from "@api/services/geoip";
import { markVisitorPresence } from "@api/services/presence";
import {
	applyDevelopmentClientIpOverride,
	extractClientIpFromRequest,
} from "@api/utils/client-ip";
import {
	safelyExtractRequestData,
	validateResponse,
} from "@api/utils/validate";
import {
	type UpdateVisitorRequest,
	updateVisitorMetadataRequestSchema,
	updateVisitorRequestSchema,
	type VisitorActivityRequest,
	type VisitorActivityResponse,
	type VisitorResponse,
	visitorActivityRequestSchema,
	visitorActivityResponseSchema,
	visitorResponseSchema,
} from "@cossistant/types";
import { OpenAPIHono } from "@hono/zod-openapi";
import type { Context } from "hono";
import { z } from "zod";
import { protectedPublicApiKeyMiddleware } from "../middleware";
import { errorJsonResponse, runtimeDualAuth } from "../openapi";
import type { RestContext } from "../types";

export const visitorRouter = new OpenAPIHono<RestContext>();
const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/;
const MAX_PRECISE_GEO_ACCURACY_RADIUS_KM = 100;

type PersistedGeoUpdate = {
	ip?: string;
	city?: string | null;
	region?: string | null;
	country?: string | null;
	countryCode?: string | null;
	latitude?: number | null;
	longitude?: number | null;
	geoSource?: "maxmind" | "edge_header" | null;
	geoAccuracyRadiusKm?: number | null;
	geoResolvedAt?: string | null;
};

function formatVisitorResponse(record: VisitorRecord): VisitorResponse {
	return {
		id: record.id,
		browser: record.browser,
		browserVersion: record.browserVersion,
		os: record.os,
		osVersion: record.osVersion,
		device: record.device,
		deviceType: record.deviceType,
		ip: record.ip,
		city: record.city,
		region: record.region,
		country: record.country,
		countryCode: record.countryCode,
		latitude: record.latitude,
		longitude: record.longitude,
		language: record.language,
		timezone: record.timezone,
		screenResolution: record.screenResolution,
		viewport: record.viewport,
		createdAt: record.createdAt,
		updatedAt: record.updatedAt,
		lastSeenAt: record.lastSeenAt ?? null,
		websiteId: record.websiteId,
		organizationId: record.organizationId,
		blockedAt: record.blockedAt ?? null,
		blockedByUserId: record.blockedByUserId,
		isBlocked: Boolean(record.blockedAt),
		attribution: record.attribution ?? null,
		currentPage: record.currentPage ?? null,
		contact: null,
	};
}

function getHeaderValue(
	request: Context<RestContext>["req"],
	name: string
): string | null {
	return request.header(name) ?? null;
}

function pickFirstHeader(values: Array<string | null>): string | null {
	return values.find((value) => value && value.trim().length > 0) ?? null;
}

function parsePreferredLocale(headerValue: string | null): string | null {
	if (!headerValue) {
		return null;
	}

	return headerValue.split(",").at(0) ?? null;
}

function normalizeCountryCode(code?: string | null): string | null {
	if (!code) {
		return null;
	}

	const trimmed = code.trim();
	if (trimmed.length !== 2) {
		return null;
	}

	const normalizedCode = trimmed.toUpperCase();
	return COUNTRY_CODE_REGEX.test(normalizedCode) ? normalizedCode : null;
}

function parseCoordinate(value: string | null): number | null {
	if (!value) {
		return null;
	}

	const parsed = Number.parseFloat(value);
	return Number.isFinite(parsed) ? parsed : null;
}

function setIfPresent<T extends keyof UpdateVisitorRequest>(
	target: Partial<UpdateVisitorRequest>,
	key: T,
	value: UpdateVisitorRequest[T] | null | undefined
): void {
	if (value !== null && value !== undefined) {
		target[key] = value;
	}
}

function getCountryDisplayName(
	countryCode: string | null,
	locale: string | null
): string | null {
	if (!countryCode || typeof Intl.DisplayNames === "undefined") {
		return null;
	}

	try {
		const display = new Intl.DisplayNames([locale || "en"], {
			type: "region",
		});
		return display.of(countryCode) ?? null;
	} catch {
		return null;
	}
}

function extractEdgeGeoUpdate(
	request: Context<RestContext>["req"],
	preferredLocale: string | null
): PersistedGeoUpdate {
	const header = (name: string) => getHeaderValue(request, name);
	const city = pickFirstHeader([
		header("cf-ipcity"),
		header("x-vercel-ip-city"),
	]);
	const region = pickFirstHeader([
		header("cf-ipregion"),
		header("x-vercel-ip-country-region"),
	]);
	const countryCode = normalizeCountryCode(
		pickFirstHeader([header("cf-ipcountry"), header("x-vercel-ip-country")])
	);
	const latitude = parseCoordinate(
		pickFirstHeader([
			getHeaderValue(request, "cf-iplatitude"),
			getHeaderValue(request, "x-vercel-ip-latitude"),
		])
	);
	const longitude = parseCoordinate(
		pickFirstHeader([
			getHeaderValue(request, "cf-iplongitude"),
			getHeaderValue(request, "x-vercel-ip-longitude"),
		])
	);

	const edgeUpdate: PersistedGeoUpdate = {};
	if (city) {
		edgeUpdate.city = city;
	}
	if (region) {
		edgeUpdate.region = region;
	}
	if (countryCode) {
		edgeUpdate.countryCode = countryCode;
		const countryName = getCountryDisplayName(countryCode, preferredLocale);
		if (countryName) {
			edgeUpdate.country = countryName;
		}
	}
	if (latitude !== null && longitude !== null) {
		edgeUpdate.latitude = latitude;
		edgeUpdate.longitude = longitude;
	}

	return edgeUpdate;
}

function extractRequestContext(request: Context<RestContext>["req"]): {
	preferredLocale: string | null;
	preferredLanguage: string | null;
	edgeGeoUpdate: PersistedGeoUpdate;
	edgeTimezone: string | null;
	canonicalIp: string | null;
	publicIp: string | null;
} {
	const header = (name: string) => getHeaderValue(request, name);
	const preferredLocale = parsePreferredLocale(header("accept-language"));
	const ipInfo = applyDevelopmentClientIpOverride(
		extractClientIpFromRequest(request),
		{
			nodeEnv: env.NODE_ENV,
			overrideIp: env.LOCAL_VISITOR_IP_OVERRIDE,
			warn: console.warn,
		}
	);

	return {
		preferredLocale,
		preferredLanguage: preferredLocale,
		edgeGeoUpdate: extractEdgeGeoUpdate(request, preferredLocale),
		edgeTimezone: header("x-vercel-ip-timezone"),
		canonicalIp: ipInfo.canonicalIp,
		publicIp: ipInfo.publicIp,
	};
}

function hasPersistedGeo(update: PersistedGeoUpdate): boolean {
	return Boolean(
		update.countryCode ||
			update.country ||
			update.region ||
			update.city ||
			update.latitude !== undefined ||
			update.longitude !== undefined
	);
}

function stripServerOwnedGeoFields(
	body: UpdateVisitorRequest
): Partial<UpdateVisitorRequest> {
	const {
		ip: _ip,
		city: _city,
		region: _region,
		country: _country,
		countryCode: _countryCode,
		latitude: _latitude,
		longitude: _longitude,
		...rest
	} = body;

	return rest;
}

function extractManualGeoFallback(
	body: UpdateVisitorRequest,
	preferredLocale: string | null
): Partial<UpdateVisitorRequest> {
	const manualGeo: Partial<UpdateVisitorRequest> = {};
	setIfPresent(manualGeo, "city", body.city);
	setIfPresent(manualGeo, "region", body.region);
	setIfPresent(manualGeo, "country", body.country);
	setIfPresent(manualGeo, "latitude", body.latitude);
	setIfPresent(manualGeo, "longitude", body.longitude);

	const normalizedBodyCountryCode = normalizeCountryCode(body.countryCode);
	if (normalizedBodyCountryCode) {
		manualGeo.countryCode = normalizedBodyCountryCode;
		if (!manualGeo.country) {
			const countryName = getCountryDisplayName(
				normalizedBodyCountryCode,
				preferredLocale
			);
			if (countryName) {
				manualGeo.country = countryName;
			}
		}
	}

	return manualGeo;
}

async function resolveServerGeoUpdate(params: {
	existingVisitor: VisitorRecord;
	canonicalIp: string | null;
	publicIp: string | null;
	edgeGeoUpdate: PersistedGeoUpdate;
	edgeTimezone: string | null;
	resolvedAt: string;
}): Promise<{
	geoUpdate: PersistedGeoUpdate;
	timezoneFallback: string | null;
}> {
	const geoUpdate: PersistedGeoUpdate = {};
	if (params.canonicalIp) {
		geoUpdate.ip = params.canonicalIp;
	}

	const ipChanged =
		params.canonicalIp !== null &&
		params.canonicalIp !== params.existingVisitor.ip;
	const shouldLookupGeo =
		Boolean(params.publicIp) &&
		(ipChanged ||
			!params.existingVisitor.geoSource ||
			params.existingVisitor.geoSource !== "maxmind");

	if (shouldLookupGeo && params.publicIp) {
		const lookup = await lookupGeoIp(params.publicIp);
		if (lookup?.found) {
			const normalizedLookupCountryCode = normalizeCountryCode(
				lookup.country_code
			);
			const hasLookupGeoPayload = Boolean(
				normalizedLookupCountryCode ||
					lookup.country ||
					lookup.region ||
					lookup.city ||
					lookup.timezone ||
					lookup.latitude !== null ||
					lookup.longitude !== null
			);

			if (hasLookupGeoPayload) {
				if (normalizedLookupCountryCode) {
					geoUpdate.countryCode = normalizedLookupCountryCode;
				}
				if (lookup.country) {
					geoUpdate.country = lookup.country;
				}
				if (lookup.region) {
					geoUpdate.region = lookup.region;
				}
				if (lookup.city) {
					geoUpdate.city = lookup.city;
				}

				const shouldPersistPreciseCoordinates =
					lookup.accuracy_radius_km !== null &&
					lookup.accuracy_radius_km <= MAX_PRECISE_GEO_ACCURACY_RADIUS_KM &&
					lookup.latitude !== null &&
					lookup.longitude !== null;
				geoUpdate.latitude = shouldPersistPreciseCoordinates
					? lookup.latitude
					: null;
				geoUpdate.longitude = shouldPersistPreciseCoordinates
					? lookup.longitude
					: null;
				geoUpdate.geoSource = "maxmind";
				geoUpdate.geoAccuracyRadiusKm = lookup.accuracy_radius_km;
				geoUpdate.geoResolvedAt = lookup.resolved_at;

				return {
					geoUpdate,
					timezoneFallback: lookup.timezone ?? params.edgeTimezone,
				};
			}
		}
	}

	if (hasPersistedGeo(params.edgeGeoUpdate)) {
		return {
			geoUpdate: {
				...geoUpdate,
				...params.edgeGeoUpdate,
				geoSource: "edge_header",
				geoAccuracyRadiusKm: null,
				geoResolvedAt: params.resolvedAt,
			},
			timezoneFallback: params.edgeTimezone,
		};
	}

	if (ipChanged) {
		return {
			geoUpdate: {
				...geoUpdate,
				city: null,
				region: null,
				country: null,
				countryCode: null,
				latitude: null,
				longitude: null,
				geoSource: null,
				geoAccuracyRadiusKm: null,
				geoResolvedAt: null,
			},
			timezoneFallback: params.edgeTimezone,
		};
	}

	return {
		geoUpdate,
		timezoneFallback: params.edgeTimezone,
	};
}

function toOptionalNumber(
	value: number | null | undefined
): number | undefined {
	if (value === null || value === undefined) {
		return;
	}

	return value;
}

function toOptionalString(
	value: string | null | undefined
): string | undefined {
	if (value === null || value === undefined) {
		return;
	}

	return value;
}

visitorRouter.use("/*", ...protectedPublicApiKeyMiddleware);

// POST /visitors/:id/activity - Track live visitor activity
visitorRouter.openapi(
	{
		method: "post",
		path: "/:id/activity",
		summary: "Track live visitor activity",
		description:
			"Records live visitor activity for realtime dashboards. This endpoint is the canonical ingestion path for live visitor presence and page activity.",
		request: {
			body: {
				content: {
					"application/json": {
						schema: visitorActivityRequestSchema,
					},
				},
			},
		},
		responses: {
			200: {
				description: "Live activity accepted",
				content: {
					"application/json": {
						schema: visitorActivityResponseSchema,
					},
				},
			},
			400: errorJsonResponse("Invalid request data"),
			401: errorJsonResponse("Unauthorized - Invalid or missing API key"),
			403: errorJsonResponse("Forbidden - Public key origin validation failed"),
			404: errorJsonResponse("Visitor not found"),
			500: errorJsonResponse("Internal server error"),
		},
		...runtimeDualAuth({
			parameters: [
				{
					name: "id",
					in: "path",
					required: true,
					description: "The visitor ID",
					schema: {
						type: "string",
					},
				},
			],
		}),
	},
	async (c) => {
		try {
			const { body, db, website } = await safelyExtractRequestData(
				c,
				visitorActivityRequestSchema
			);
			const visitorId = c.req.param("id");

			if (!visitorId) {
				return c.json(
					{
						error: "BAD_REQUEST",
						message: "Visitor ID is required",
					},
					400
				);
			}

			const visitor = await findVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
			});

			if (!visitor) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			const acceptedAt = new Date().toISOString();
			const updatedVisitor = await updateVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
				data: {
					lastSeenAt: acceptedAt,
					updatedAt: acceptedAt,
				},
			});
			const trackingContext = flattenVisitorTrackingContext({
				attribution: body.attribution,
				currentPage: body.currentPage,
			});
			void updatedVisitor;

			trackVisitorActivity({
				website_id: website.id,
				visitor_id: visitorId,
				session_id: body.sessionId,
				event_type: body.activityType,
				city: toOptionalString(visitor.city),
				country_code: toOptionalString(visitor.countryCode),
				latitude: toOptionalNumber(visitor.latitude),
				longitude: toOptionalNumber(visitor.longitude),
				...trackingContext,
			});

			await markVisitorPresence({
				websiteId: website.id,
				visitorId,
				lastSeenAt: acceptedAt,
				geo: {
					city: toOptionalString(visitor.city),
					countryCode: toOptionalString(visitor.countryCode),
					latitude: toOptionalNumber(visitor.latitude),
					longitude: toOptionalNumber(visitor.longitude),
				},
			});

			void realtime
				.emit("visitorPresenceUpdate", {
					activityType: body.activityType,
					attribution: body.attribution,
					currentPage: body.currentPage,
					organizationId: visitor.organizationId,
					sessionId: body.sessionId,
					userId: null,
					visitorId,
					websiteId: website.id,
				})
				.catch((error) => {
					console.error("[VisitorActivity] Failed to publish realtime event", {
						websiteId: website.id,
						visitorId,
						error,
					});
				});

			const response: VisitorActivityResponse = {
				ok: true,
				acceptedAt,
			};

			return c.json(
				validateResponse(response, visitorActivityResponseSchema),
				200
			);
		} catch (error) {
			console.error("Error tracking visitor activity:", error);
			return c.json(
				{
					error: "INTERNAL_SERVER_ERROR",
					message: "Failed to track visitor activity",
				},
				500
			);
		}
	}
);

// PATCH /visitors/:id - Update existing visitor information
visitorRouter.openapi(
	{
		method: "patch",
		path: "/:id",
		summary: "Update existing visitor information",
		description:
			"Updates an existing visitor's browser, device, and location data. The visitor must already exist in the system.",
		request: {
			body: {
				content: {
					"application/json": {
						schema: updateVisitorRequestSchema,
					},
				},
			},
		},
		responses: {
			200: {
				content: {
					"application/json": {
						schema: visitorResponseSchema,
					},
				},
				description: "Visitor information successfully created or updated",
			},
			400: errorJsonResponse("Invalid request data"),
			401: errorJsonResponse("Unauthorized - Invalid API key"),
			403: errorJsonResponse("Forbidden - Public key origin validation failed"),
			404: errorJsonResponse("Visitor not found"),
			500: errorJsonResponse("Internal server error"),
		},
		...runtimeDualAuth({
			parameters: [
				{
					name: "id",
					in: "path",
					required: true,
					description: "The visitor ID to update",
					schema: {
						type: "string",
					},
				},
			],
		}),
	},
	async (c) => {
		try {
			const { db, website, body } = await safelyExtractRequestData(
				c,
				updateVisitorRequestSchema
			);
			const visitorId = c.req.param("id");

			if (!visitorId) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			if (!website?.id) {
				return c.json(
					{ error: "UNAUTHORIZED", message: "Invalid API key" },
					401
				);
			}

			const existingVisitor = await findVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
			});

			if (!existingVisitor) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			const normalizedBodyCountryCode = normalizeCountryCode(body.countryCode);
			if (
				typeof body.countryCode === "string" &&
				normalizedBodyCountryCode === null
			) {
				return c.json(
					{
						error: "BAD_REQUEST",
						message: "countryCode must be a valid ISO 3166-1 alpha-2 code",
					},
					400
				);
			}

			const normalizedBody: UpdateVisitorRequest = {
				...body,
				...(normalizedBodyCountryCode
					? { countryCode: normalizedBodyCountryCode }
					: {}),
				attribution: body.attribution,
				currentPage: body.currentPage,
			};

			const now = new Date();
			const nowIso = now.toISOString();
			const requestContext = extractRequestContext(c.req);
			const { geoUpdate, timezoneFallback } = await resolveServerGeoUpdate({
				existingVisitor,
				canonicalIp: requestContext.canonicalIp,
				publicIp: requestContext.publicIp,
				edgeGeoUpdate: requestContext.edgeGeoUpdate,
				edgeTimezone: requestContext.edgeTimezone,
				resolvedAt: nowIso,
			});

			const baseVisitorUpdate = stripServerOwnedGeoFields(normalizedBody);
			if (
				baseVisitorUpdate.language === undefined &&
				requestContext.preferredLanguage
			) {
				baseVisitorUpdate.language = requestContext.preferredLanguage;
			}
			if (baseVisitorUpdate.timezone === undefined && timezoneFallback) {
				baseVisitorUpdate.timezone = timezoneFallback;
			}

			const manualGeoFallback =
				requestContext.canonicalIp === null
					? extractManualGeoFallback(
							normalizedBody,
							requestContext.preferredLocale
						)
					: {};

			const updatedVisitor = await updateVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
				data: {
					...baseVisitorUpdate,
					...manualGeoFallback,
					...geoUpdate,
					attribution: resolveFirstTouchAttribution({
						existingAttribution: existingVisitor.attribution,
						incomingAttribution: normalizedBody.attribution,
					}),
					currentPage:
						normalizedBody.currentPage ?? existingVisitor.currentPage,
					lastSeenAt: nowIso,
					updatedAt: nowIso,
				},
			});

			if (!updatedVisitor) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			if (normalizedBody.currentPage) {
				const trackingContext = flattenVisitorTrackingContext({
					attribution: updatedVisitor.attribution,
					currentPage: updatedVisitor.currentPage,
				});

				trackVisitorEvent({
					website_id: website.id,
					visitor_id: visitorId,
					event_type: "page_view",
					...trackingContext,
				});

				trackVisitorActivity({
					website_id: website.id,
					visitor_id: visitorId,
					event_type: "page_sync",
					session_id: visitorId,
					city: updatedVisitor.city ?? undefined,
					country_code: updatedVisitor.countryCode ?? undefined,
					latitude: updatedVisitor.latitude ?? undefined,
					longitude: updatedVisitor.longitude ?? undefined,
					...trackingContext,
				});
			}

			const response = formatVisitorResponse(updatedVisitor);

			return c.json(validateResponse(response, visitorResponseSchema), 200);
		} catch (error) {
			console.error("Error updating visitor:", error);
			return c.json(
				{
					error: "INTERNAL_SERVER_ERROR",
					message: "Failed to update visitor information",
				},
				500
			);
		}
	}
);

// PATCH /visitors/:id/metadata - Update contact metadata for a visitor
visitorRouter.openapi(
	{
		method: "patch",
		path: "/:id/metadata",
		summary: "Update contact metadata for a visitor",
		description:
			"Merges the provided metadata into the contact profile associated with the visitor. The visitor must be identified first (linked to a contact) via the /contacts/identify endpoint.",
		request: {
			body: {
				content: {
					"application/json": {
						schema: updateVisitorMetadataRequestSchema,
					},
				},
			},
		},
		responses: {
			200: {
				description: "Contact metadata updated successfully",
				content: {
					"application/json": {
						schema: visitorResponseSchema,
					},
				},
			},
			400: errorJsonResponse("Invalid request data or visitor not identified"),
			401: errorJsonResponse("Unauthorized - Invalid API key"),
			403: errorJsonResponse("Forbidden - Public key origin validation failed"),
			404: errorJsonResponse("Visitor not found"),
			500: errorJsonResponse("Internal server error"),
		},
		...runtimeDualAuth({
			parameters: [
				{
					name: "id",
					in: "path",
					required: true,
					description: "The visitor ID",
					schema: {
						type: "string",
					},
				},
			],
		}),
	},
	async (c) => {
		try {
			const { db, website, body } = await safelyExtractRequestData(
				c,
				updateVisitorMetadataRequestSchema
			);
			const visitorId = c.req.param("id");

			if (!visitorId) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			if (!website?.id) {
				return c.json(
					{ error: "UNAUTHORIZED", message: "Invalid API key" },
					401
				);
			}

			const contact = await getContactForVisitor(db, {
				visitorId,
				websiteId: website.id,
			});

			if (!contact) {
				return c.json(
					{
						error: "BAD_REQUEST",
						message:
							"Visitor is not identified. Please use the /contacts/identify endpoint first to create a contact for this visitor.",
					},
					400
				);
			}

			await mergeContactMetadata(db, {
				contactId: contact.id,
				websiteId: website.id,
				metadata: body.metadata,
			});

			const visitor = await findVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
			});

			if (!visitor) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			const response = formatVisitorResponse(visitor);

			return c.json(validateResponse(response, visitorResponseSchema), 200);
		} catch (error) {
			console.error("Error updating contact metadata:", error);
			return c.json(
				{
					error: "INTERNAL_SERVER_ERROR",
					message: "Failed to update contact metadata",
				},
				500
			);
		}
	}
);

// GET /visitors/:id - Get visitor information by ID
visitorRouter.openapi(
	{
		method: "get",
		path: "/:id",
		summary: "Get visitor information",
		description: "Retrieves visitor information by visitor ID",
		responses: {
			200: {
				content: {
					"application/json": {
						schema: visitorResponseSchema,
					},
				},
				description: "Visitor information retrieved successfully",
			},
			401: errorJsonResponse("Unauthorized - Invalid API key"),
			403: errorJsonResponse("Forbidden - Public key origin validation failed"),
			404: errorJsonResponse("Visitor not found"),
			500: errorJsonResponse("Internal server error"),
		},
		...runtimeDualAuth({
			parameters: [
				{
					name: "id",
					in: "path",
					required: true,
					description: "The visitor ID",
					schema: {
						type: "string",
					},
				},
			],
		}),
	},
	async (c) => {
		try {
			const { db, website } = await safelyExtractRequestData(c);
			const visitorId = c.req.param("id");

			if (!visitorId) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			if (!website?.id) {
				return c.json(
					{ error: "UNAUTHORIZED", message: "Invalid API key" },
					401
				);
			}

			const visitorRecord = await findVisitorForWebsite(db, {
				visitorId,
				websiteId: website.id,
			});

			if (!visitorRecord) {
				return c.json(
					{ error: "NOT_FOUND", message: "Visitor not found" },
					404
				);
			}

			const response = formatVisitorResponse(visitorRecord);

			return c.json(validateResponse(response, visitorResponseSchema), 200);
		} catch (error) {
			console.error("Error fetching visitor:", error);
			return c.json(
				{
					error: "INTERNAL_SERVER_ERROR",
					message: "Failed to fetch visitor information",
				},
				500
			);
		}
	}
);
