import { z } from 'zod';
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';

extendZodWithOpenApi(z);

// Common schemas
export const uuidSchema = 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');

/** Helper to convert string to boolean for multipart/form-data */
export const booleanFromString = () =>
	z
		.union([z.boolean(), z.string()])
		.transform((val) => {
			if (typeof val === 'string') {
				return val === 'true' || val === '1';
			}
			return val;
		})
		.openapi({ type: 'boolean' });

export const ViolationSchema = z
	.object({
		code: z.string(),
		messages: z.object({ vi: z.string(), en: z.string() }),
	})
	.openapi('Violation');

export const ErrorSchema = z
	.object({
		field: z.string(),
		code: z.string(),
		messages: z.object({ vi: z.string(), en: z.string() }),
	})
	.openapi('Error');

/**
 * Pure schema composer — wraps any data type in the standard API envelope.
 * Not a runtime factory: no side effects, no registration, no imports from services/models.
 *
 * Usage:
 *   const MyResponseSchema = ApiResponseSchema(MyDataSchema).openapi('MyResponse');
 *   type MyResponse = z.infer<typeof MyResponseSchema>;
 */
export const ApiResponseSchema = <T extends z.ZodType>(data: T) =>
	z.object({
		success: z.boolean(),
		data: data.nullable().optional(),
		trace_id: z.string().nullable(),
		timestamp: z.string(),
		errors: z.array(ErrorSchema).nullable(),
	});

export type ApiResponse<T> =
	ReturnType<typeof ApiResponseSchema<z.ZodType>> extends z.ZodObject<infer _>
		? {
				success: boolean;
				data: T | null | undefined;
				trace_id: string | null;
				timestamp: string;
				errors: Array<{ field: string; code: string; messages: { vi: string; en: string } }> | null;
			}
		: never;

/**
 * Error response schema — wraps errors in the standard API envelope
 * Used for all error responses in OpenAPI documentation
 */
export const ErrorResponseSchema = ApiResponseSchema(z.array(ErrorSchema)).openapi('ErrorResponse');

// ---------------------------------------------------------------------------
// Query Parameter Schemas for Pagination and Filtering
// ---------------------------------------------------------------------------

/**
 * Schema for query parameters used by queryModifier middleware
 * Validates: page, pageSize, sortField, sortOrder, filters
 */
export const QueryParamsSchema = z
	.object({
		filters: z.string().optional().openapi({
			description: 'filter, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		sortField: z.string().optional().openapi({
			description: 'sortField, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		sortOrder: z.enum(['asc', 'desc']).optional().openapi({
			description: 'sort order, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		page: z.coerce.number().int().positive().default(1).openapi({
			description: 'page, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		pageSize: z.coerce.number().int().positive().max(100).default(10).openapi({
			description: 'pageSize, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
	})
	.openapi('QueryParams');

/**
 * Query parameters for entities with soft delete enabled.
 * By default, soft-deleted records are filtered out (deleted_at==null).
 * To include soft-deleted records, use filters=deleted_at!=null or omit the filter.
 */
export const SoftDeleteQueryParamsSchema = z
	.object({
		filters: z.string().optional().default('deleted_at==null').openapi({
			description:
				'Filter for soft delete behavior. Default: deleted_at==null (excludes soft-deleted records). To include soft-deleted records, omit this filter or use deleted_at!=null. Visit https://www.npmjs.com/package/sequelize-api-paginate for syntax.',
		}),
		sortField: z.string().optional().openapi({
			description: 'sortField, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		sortOrder: z.enum(['asc', 'desc']).optional().openapi({
			description: 'sort order, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		page: z.coerce.number().int().positive().default(1).openapi({
			description: 'page, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
		pageSize: z.coerce.number().int().positive().max(100).default(10).openapi({
			description: 'pageSize, visit https://www.npmjs.com/package/sequelize-api-paginate for syntax',
		}),
	})
	.openapi('SoftDeleteQueryParams');
