import { getLogger } from "./logger.js";
import { z } from "@hono/zod-openapi";
import { HTTPException } from "hono/http-exception";

//#region src/utils/error.ts
const PG_ERROR_KEYS = [
	"code",
	"detail",
	"hint",
	"severity",
	"schema",
	"table",
	"column",
	"constraint",
	"routine",
	"position",
	"internalPosition",
	"internalQuery",
	"where",
	"dataType",
	"line",
	"file"
];
const DRIZZLE_EXTRA_KEYS = ["query", "params"];
/**
* Builds structured fields from an error chain (Drizzle → pg DatabaseError / Doltgres).
* Outer wrappers often omit SQLSTATE and server detail; those live on nested `.cause`.
*/
function getDatabaseErrorLogContext(error) {
	const chain = [];
	const seen = /* @__PURE__ */ new Set();
	let current = error;
	let depth = 0;
	while (current != null && typeof current === "object" && !seen.has(current)) {
		seen.add(current);
		const e = current;
		const slice = {};
		if (typeof e.message === "string" && e.message.length > 0) slice.message = e.message;
		if (depth === 0 && typeof e.stack === "string" && e.stack.length > 0) slice.stack = e.stack;
		for (const k of PG_ERROR_KEYS) {
			const v = e[k];
			if (v !== void 0 && v !== null && v !== "") slice[k] = v;
		}
		for (const k of DRIZZLE_EXTRA_KEYS) if (e[k] !== void 0) slice[k] = k === "params" ? `[${Array.isArray(e[k]) ? e[k].length : "?"} params redacted]` : e[k];
		if (Object.keys(slice).length > 0) chain.push(slice);
		depth += 1;
		current = "cause" in e ? e.cause : void 0;
	}
	if (chain.length === 0) return {};
	const root = chain[chain.length - 1];
	const out = { dbErrorChain: chain };
	if (root.message !== void 0) out.dbRootMessage = root.message;
	if (root.code !== void 0) out.dbRootCode = root.code;
	if (root.detail !== void 0) out.dbRootDetail = root.detail;
	if (root.hint !== void 0) out.dbRootHint = root.hint;
	return out;
}
const ErrorCode = z.enum([
	"bad_request",
	"unauthorized",
	"payment_required",
	"forbidden",
	"not_found",
	"conflict",
	"too_many_requests",
	"internal_server_error",
	"unprocessable_entity",
	"bad_gateway",
	"service_unavailable"
]);
const errorCodeToHttpStatus = {
	bad_request: 400,
	unauthorized: 401,
	payment_required: 402,
	forbidden: 403,
	not_found: 404,
	conflict: 409,
	too_many_requests: 429,
	unprocessable_entity: 422,
	internal_server_error: 500,
	bad_gateway: 502,
	service_unavailable: 503
};
const ERROR_DOCS_BASE_URL = "https://docs.inkeep.com/agents-api/errors";
const problemDetailsSchema = z.object({
	title: z.string().openapi({
		description: "A short, human-readable summary of the problem type.",
		example: "Resource Not Found"
	}),
	status: z.number().int().openapi({
		description: "The HTTP status code.",
		example: 404
	}),
	detail: z.string().openapi({
		description: "A human-readable explanation specific to this occurrence of the problem.",
		example: "The requested resource was not found."
	}),
	instance: z.string().optional().openapi({
		description: "A URI reference that identifies the specific occurrence of the problem.",
		example: "/conversations/123"
	}),
	requestId: z.string().optional().openapi({
		description: "A unique identifier for the request, useful for troubleshooting.",
		example: "req_1234567890"
	}),
	code: ErrorCode.openapi({
		description: "A short code indicating the error code returned.",
		example: "not_found"
	})
}).openapi("ProblemDetails");
const errorResponseSchema = z.object({ error: z.object({
	code: ErrorCode.openapi({
		description: "A short code indicating the error code returned.",
		example: "not_found"
	}),
	message: z.string().openapi({
		description: "A human readable error message.",
		example: "The requested resource was not found."
	})
}) }).openapi("ErrorResponse");
const STATIC_500_MESSAGE = "An internal server error occurred. Please try again later.";
function createApiError({ code, message, instance, requestId, extensions }) {
	const status = errorCodeToHttpStatus[code];
	const title = getTitleFromCode(code);
	`${ERROR_DOCS_BASE_URL}${code}`;
	const externalMessage = status >= 500 ? STATIC_500_MESSAGE : message;
	const problemDetails = {
		title,
		status,
		detail: externalMessage,
		code,
		...instance && { instance },
		...requestId && { requestId }
	};
	const errorMessage = externalMessage.length > 100 ? `${externalMessage.substring(0, 97)}...` : externalMessage;
	const responseBody = {
		...problemDetails,
		error: {
			code,
			message: errorMessage
		},
		...extensions && extensions
	};
	return new HTTPException(status, {
		message,
		res: new Response(JSON.stringify(responseBody), {
			status,
			headers: {
				"Content-Type": "application/problem+json",
				"X-Content-Type-Options": "nosniff",
				...requestId && { "X-Request-ID": requestId }
			}
		})
	});
}
async function handleApiError(error, requestId) {
	if (error instanceof HTTPException) {
		const responseText = await error.getResponse().text();
		let responseJson;
		try {
			responseJson = JSON.parse(responseText);
			if (requestId && !responseJson.requestId) responseJson.requestId = requestId;
		} catch (_e) {
			responseJson = {
				title: "Internal Server Error",
				status: error.status,
				detail: `Error processing request: ${responseText || "Unknown error"}`,
				code: "internal_server_error",
				...requestId && { requestId },
				error: {
					code: "internal_server_error",
					message: "An internal server error occurred"
				}
			};
		}
		if (error.status >= 500) {
			responseJson.detail = STATIC_500_MESSAGE;
			responseJson.error.message = STATIC_500_MESSAGE;
		} else getLogger("core").info({
			error,
			status: error.status,
			code: responseJson.code,
			message: responseJson.detail || responseJson.error.message,
			requestId: responseJson.requestId || requestId || "unknown"
		}, "API client error occurred");
		return responseJson;
	}
	return {
		title: "Internal Server Error",
		status: 500,
		detail: STATIC_500_MESSAGE,
		code: "internal_server_error",
		...requestId && { requestId },
		error: {
			code: "internal_server_error",
			message: STATIC_500_MESSAGE
		}
	};
}
function getTitleFromCode(code) {
	switch (code) {
		case "bad_request": return "Bad Request";
		case "unauthorized": return "Unauthorized";
		case "payment_required": return "Payment Required";
		case "forbidden": return "Forbidden";
		case "not_found": return "Not Found";
		case "conflict": return "Conflict";
		case "too_many_requests": return "Too Many Requests";
		case "unprocessable_entity": return "Unprocessable Entity";
		case "internal_server_error": return "Internal Server Error";
		case "bad_gateway": return "Bad Gateway";
		case "service_unavailable": return "Service Unavailable";
		default: return "Error";
	}
}
const toTitleCase = (str) => str?.replace(/\w+/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).replace(/\s+/g, "") ?? "";
const errorSchemaFactory = (code, description) => ({
	description,
	content: { "application/problem+json": { schema: problemDetailsSchema.extend({
		code: z.literal(code).openapi({
			description: "A short code indicating the error code returned.",
			example: code
		}),
		detail: z.string().openapi({
			description: "A detailed explanation specific to this occurrence of the problem, providing context and specifics about what went wrong.",
			example: description
		}),
		title: z.string().openapi({
			description: "A short, human-readable summary of the problem type.",
			example: getTitleFromCode(code)
		}),
		status: z.number().int().openapi({
			description: "The HTTP status code.",
			example: errorCodeToHttpStatus[code]
		}),
		error: z.object({
			code: z.literal(code).openapi({
				description: "A short code indicating the error code returned.",
				example: code
			}),
			message: z.string().openapi({
				description: "A concise error message suitable for display to end users. May be truncated if the full detail is long.",
				example: description.length > 100 ? `${description.substring(0, 97)}...` : description
			})
		}).openapi({ description: "Legacy error format for backward compatibility." })
	}).openapi(toTitleCase(description)) } }
});
function isUniqueConstraintError(error) {
	const err = error;
	return err?.cause?.code === "23505" || !!err?.cause?.message?.includes("1062") || !!err?.message?.includes("already exists");
}
function throwIfUniqueConstraintError(error, message) {
	if (isUniqueConstraintError(error)) throw createApiError({
		code: "conflict",
		message
	});
}
const commonCreateErrorResponses = {
	400: errorSchemaFactory("bad_request", "Bad Request"),
	401: errorSchemaFactory("unauthorized", "Unauthorized"),
	403: errorSchemaFactory("forbidden", "Forbidden"),
	422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
	500: errorSchemaFactory("internal_server_error", "Internal Server Error")
};
const commonUpdateErrorResponses = {
	400: errorSchemaFactory("bad_request", "Bad Request"),
	401: errorSchemaFactory("unauthorized", "Unauthorized"),
	403: errorSchemaFactory("forbidden", "Forbidden"),
	404: errorSchemaFactory("not_found", "Not Found"),
	422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
	500: errorSchemaFactory("internal_server_error", "Internal Server Error")
};
const commonGetErrorResponses = {
	400: errorSchemaFactory("bad_request", "Bad Request"),
	401: errorSchemaFactory("unauthorized", "Unauthorized"),
	403: errorSchemaFactory("forbidden", "Forbidden"),
	404: errorSchemaFactory("not_found", "Not Found"),
	422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
	500: errorSchemaFactory("internal_server_error", "Internal Server Error")
};
const commonDeleteErrorResponses = {
	400: errorSchemaFactory("bad_request", "Bad Request"),
	401: errorSchemaFactory("unauthorized", "Unauthorized"),
	403: errorSchemaFactory("forbidden", "Forbidden"),
	404: errorSchemaFactory("not_found", "Not Found"),
	422: errorSchemaFactory("unprocessable_entity", "Unprocessable Entity"),
	500: errorSchemaFactory("internal_server_error", "Internal Server Error")
};

//#endregion
export { ERROR_DOCS_BASE_URL, ErrorCode, commonCreateErrorResponses, commonDeleteErrorResponses, commonGetErrorResponses, commonUpdateErrorResponses, createApiError, errorResponseSchema, errorSchemaFactory, getDatabaseErrorLogContext, handleApiError, isUniqueConstraintError, problemDetailsSchema, throwIfUniqueConstraintError };