import { Req, Res } from '#interfaces/IApi';
import { NextFunction } from 'express';
import { ValidationError } from '#interfaces/error';
import { type ZodType, ZodError, z } from 'zod';
import { getZodErrorCode, getZodErrorMessage } from './error-mapper';

// ---------------------------------------------------------------------------
// Zod-based middleware — for use with src/contracts/*.schema.ts
// ---------------------------------------------------------------------------

// Format Zod error into structured response
const formatZodError = (error: ZodError) => {
	const errors = error.issues.map((e) => ({
		field: e.path.map(String).join('.'),
		code: getZodErrorCode(e),
		message: getZodErrorMessage(e),
	}));
	return { errors };
};

/**
 * Create an Express middleware that validates req.body against a Zod schema.
 * On success, req.body is replaced with the parsed (stripped) value.
 * On failure, responds with 422 + structured error matching ValidationError format.
 *
 * Supports both ZodObject and ZodArray schemas.
 */
export const validateZod =
	<T extends ZodType>(bodySchema: T) =>
	async (req: Req, res: Res, next: NextFunction) => {
		const result = bodySchema.safeParse(req.body);
		if (!result.success) {
			const { errors } = formatZodError(result.error);
			return res.error(new ValidationError(errors));
		}
		req.body = result.data;
		next();
	};

/**
 * Create an Express middleware that validates req.query against a Zod schema.
 * On success, req.query is replaced with the parsed (stripped) value.
 * On failure, responds with 422 + structured error matching ValidationError format.
 *
 * Use this for validating pagination, filtering, and sorting parameters.
 */
export const validateQueryZod =
	<T extends ZodType>(querySchema: T) =>
	async (req: Req, res: Res, next: NextFunction) => {
		const result = querySchema.safeParse(req.query);
		if (!result.success) {
			const { errors } = formatZodError(result.error);
			return res.error(new ValidationError(errors));
		}
		req.query = result.data as typeof req.query;
		next();
	};

/**
 * Create an Express middleware that validates req.params against a Zod schema.
 * On success, req.params is replaced with the parsed (stripped) value.
 * On failure, responds with 422 + structured error matching ValidationError format.
 */
export const validateParamsZod =
	<T extends ZodType>(paramsSchema: T) =>
	async (req: Req, res: Res, next: NextFunction) => {
		const result = paramsSchema.safeParse(req.params);
		if (!result.success) {
			const { errors } = formatZodError(result.error);
			return res.error(new ValidationError(errors));
		}
		req.params = result.data as typeof req.params;
		next();
	};

// ---------------------------------------------------------------------------
// Common validators (Zod-based)
// ---------------------------------------------------------------------------

// Common Zod schemas
const idSchema = z
	.string()
	.regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, 'Invalid UUID format');
const emailSchema = z.email();
const slugSchema = z.string().min(1).max(255);

/**
 * Validates req.params.id is a valid UUID
 */
export const validateId = validateParamsZod(z.object({ id: idSchema }));

/**
 * Validates req.body.email is a valid email format
 */
export const validateEmail = validateZod(z.object({ email: emailSchema }));

/**
 * Validates req.params.slug is a valid slug format
 */
export const validateSlug = validateParamsZod(z.object({ slug: slugSchema }));

export default validateZod;
