/**
 * 输入校验工具（Zod SSOT）
 *
 * 用法：
 *   import { z } from "../_shared/deps.ts";
 *   import { parseJsonBody, parseQuery } from "../_shared/validate.ts";
 *
 *   const Body = z.object({ phone: z.string().regex(/^1\d{10}$/) });
 *   const parsed = await parseJsonBody(req, Body);
 *   if (!parsed.ok) return ctx.fail(parsed.message, 400, {
 *     code: "VALIDATION_ERROR", details: parsed.issues
 *   });
 *   const { phone } = parsed.data;
 *
 * 设计原则：
 *  - 始终返回 { ok: true, data } 或 { ok: false, ... }，不抛异常
 *  - issues 字段使用 Zod 的 flatten().fieldErrors 形态，前端可直接渲染
 *  - 体积上限 1MB，防止恶意 payload
 */

import type { ZodSchema, ZodIssue } from "./deps.ts";

const MAX_BODY_BYTES = 1024 * 1024;

export type ParseResult<T> =
  | { ok: true; data: T }
  | {
      ok: false;
      message: string;
      issues?: Record<string, string[]>;
      raw?: ZodIssue[];
    };

export async function parseJsonBody<T>(
  req: Request,
  schema: ZodSchema<T>,
): Promise<ParseResult<T>> {
  // 1. Content-Length 防护
  const lenHeader = req.headers.get("content-length");
  if (lenHeader && Number(lenHeader) > MAX_BODY_BYTES) {
    return { ok: false, message: "Request body too large (>1MB)" };
  }

  // 2. 解析 JSON
  let json: unknown;
  try {
    const text = await req.text();
    if (!text || text.trim() === "") {
      json = {};
    } else if (text.length > MAX_BODY_BYTES) {
      return { ok: false, message: "Request body too large (>1MB)" };
    } else {
      json = JSON.parse(text);
    }
  } catch (e) {
    return {
      ok: false,
      message: `Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`,
    };
  }

  // 3. Zod 校验
  const result = schema.safeParse(json);
  if (!result.success) {
    return {
      ok: false,
      message: "Validation failed",
      issues: result.error.flatten().fieldErrors as Record<string, string[]>,
      raw: result.error.issues,
    };
  }
  return { ok: true, data: result.data };
}

export function parseQuery<T>(
  req: Request,
  schema: ZodSchema<T>,
): ParseResult<T> {
  const url = new URL(req.url);
  const obj: Record<string, string> = {};
  url.searchParams.forEach((v, k) => { obj[k] = v; });

  const result = schema.safeParse(obj);
  if (!result.success) {
    return {
      ok: false,
      message: "Query validation failed",
      issues: result.error.flatten().fieldErrors as Record<string, string[]>,
      raw: result.error.issues,
    };
  }
  return { ok: true, data: result.data };
}
