import { z } from "zod";
export const DIFF_REVIEW_PROTOCOL_VERSION = 1;
export const DIFF_REVIEW_CLIENT_METHODS = [
    "initialize",
    "session.get_context",
    "session.list_files",
    "session.get_diff",
    "session.set_ui_text",
    "thread.submit_message",
    "session.return_review",
    "session.cancel",
];
export const DIFF_REVIEW_SERVER_METHODS = ["session.close"];
export const DIFF_REVIEW_METHODS = [
    ...DIFF_REVIEW_CLIENT_METHODS,
    ...DIFF_REVIEW_SERVER_METHODS,
];
export const DIFF_REVIEW_ERROR_CODES = {
    parseError: "parse_error",
    invalidRequest: "invalid_request",
    methodNotFound: "method_not_found",
    invalidParams: "invalid_params",
    unauthorized: "unauthorized",
    notInitialized: "not_initialized",
    sessionClosed: "session_closed",
    internalError: "internal_error",
};
const EMPTY_OBJECT = Object.freeze({});
const diffReviewMethodSchema = z.enum(DIFF_REVIEW_METHODS);
const diffReviewRequestIdSchema = z.union([z.string(), z.number()]);
const diffReviewRequestEnvelopeSchema = z
    .object({
    version: z.literal(DIFF_REVIEW_PROTOCOL_VERSION),
    type: z.literal("request"),
    id: diffReviewRequestIdSchema,
    method: z.string(),
    params: z.unknown().optional(),
})
    .strict();
const diffReviewErrorSchema = z
    .object({
    code: z.string().trim().min(1),
    message: z.string(),
    data: z.unknown().optional(),
})
    .strict();
const diffReviewSuccessResponseSchema = z
    .object({
    version: z.literal(DIFF_REVIEW_PROTOCOL_VERSION),
    type: z.literal("response"),
    id: diffReviewRequestIdSchema,
    ok: z.literal(true),
    result: z.unknown(),
})
    .strict();
const diffReviewErrorResponseSchema = z
    .object({
    version: z.literal(DIFF_REVIEW_PROTOCOL_VERSION),
    type: z.literal("response"),
    id: z.union([diffReviewRequestIdSchema, z.null()]),
    ok: z.literal(false),
    error: diffReviewErrorSchema,
})
    .strict();
const diffReviewResponseSchema = z.union([
    diffReviewSuccessResponseSchema,
    diffReviewErrorResponseSchema,
]);
const diffReviewIdFieldSchema = z.object({ id: z.unknown() }).passthrough();
const diffReviewVersionFieldSchema = z.object({ version: z.unknown() }).passthrough();
const diffReviewTypeFieldSchema = z.object({ type: z.unknown() }).passthrough();
const emptyObjectSchema = z.object({}).strict();
const initializeParamsSchema = z
    .object({
    token: z.string().trim().min(1),
})
    .strict();
const sessionGetDiffParamsSchema = z
    .object({
    path: z.string().min(1).optional(),
})
    .strict();
const sessionSetUiTextParamsSchema = z
    .object({
    text: z.string(),
})
    .strict();
const threadSubmitMessageParamsSchema = z
    .object({
    threadId: z.string().trim().min(1).optional(),
    forkFromThreadId: z.string().trim().min(1).optional(),
    message: z.string().trim().min(1),
})
    .strict()
    .superRefine((value, context) => {
    if (value.threadId && value.forkFromThreadId) {
        context.addIssue({
            code: z.ZodIssueCode.custom,
            path: ["forkFromThreadId"],
            message: "forkFromThreadId can only be used when creating a new thread",
        });
    }
});
const sessionReturnReviewParamsSchema = z
    .object({
    review: z.string().trim().min(1),
})
    .strict();
export function createDiffReviewError(code, message, data) {
    return data === undefined ? { code, message } : { code, message, data };
}
export function createDiffReviewSuccessResponse(id, result) {
    return {
        version: DIFF_REVIEW_PROTOCOL_VERSION,
        type: "response",
        id,
        ok: true,
        result,
    };
}
export function createDiffReviewErrorResponse(id, code, message, data) {
    return {
        version: DIFF_REVIEW_PROTOCOL_VERSION,
        type: "response",
        id,
        ok: false,
        error: createDiffReviewError(code, message, data),
    };
}
export function serializeDiffReviewMessage(message) {
    return `${JSON.stringify(message)}\n`;
}
export function validateDiffReviewParams(method, raw) {
    const parsed = (() => {
        switch (method) {
            case "initialize":
                return initializeParamsSchema.safeParse(raw);
            case "session.get_context":
            case "session.list_files":
            case "session.cancel":
            case "session.close":
                return emptyObjectSchema.safeParse(raw ?? EMPTY_OBJECT);
            case "session.get_diff":
                return sessionGetDiffParamsSchema.safeParse(raw ?? EMPTY_OBJECT);
            case "session.set_ui_text":
                return sessionSetUiTextParamsSchema.safeParse(raw);
            case "thread.submit_message":
                return threadSubmitMessageParamsSchema.safeParse(raw);
            case "session.return_review":
                return sessionReturnReviewParamsSchema.safeParse(raw);
        }
    })();
    if (parsed.success) {
        return {
            ok: true,
            value: parsed.data,
        };
    }
    return {
        ok: false,
        error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidParams, formatParamsError(method, parsed.error)),
    };
}
export function parseDiffReviewMessageLine(line) {
    const trimmed = line.trim();
    if (!trimmed) {
        return {
            ok: false,
            id: null,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.parseError, "request line is empty"),
        };
    }
    let decoded;
    try {
        decoded = JSON.parse(trimmed);
    }
    catch (error) {
        return {
            ok: false,
            id: null,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.parseError, "failed to parse request json", { cause: error instanceof Error ? error.message : String(error) }),
        };
    }
    const versionField = diffReviewVersionFieldSchema.safeParse(decoded);
    if (!versionField.success) {
        return {
            ok: false,
            id: null,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, "request must include numeric version field"),
        };
    }
    if (versionField.data.version !== DIFF_REVIEW_PROTOCOL_VERSION) {
        return {
            ok: false,
            id: null,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, `unsupported diff review protocol version: ${String(versionField.data.version)}`),
        };
    }
    const typeField = diffReviewTypeFieldSchema.safeParse(decoded);
    if (!typeField.success ||
        (typeField.data.type !== "request" && typeField.data.type !== "response")) {
        return {
            ok: false,
            id: null,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, "message type must be 'request' or 'response'"),
        };
    }
    if (typeField.data.type === "request") {
        return parseDecodedDiffReviewRequest(decoded);
    }
    return parseDecodedDiffReviewResponse(decoded);
}
export function parseDiffReviewRequestLine(line) {
    const parsed = parseDiffReviewMessageLine(line);
    if (!parsed.ok) {
        return parsed;
    }
    if (parsed.message.type !== "request") {
        return {
            ok: false,
            id: parsed.message.id,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, "message type must be 'request'"),
        };
    }
    return {
        ok: true,
        request: parsed.message,
    };
}
function parseDecodedDiffReviewRequest(decoded) {
    const idField = diffReviewIdFieldSchema.safeParse(decoded);
    const parsedId = idField.success ? diffReviewRequestIdSchema.safeParse(idField.data.id) : null;
    const requestId = parsedId?.success ? parsedId.data : null;
    const envelope = diffReviewRequestEnvelopeSchema.safeParse(decoded);
    if (!envelope.success) {
        const hasMethod = envelope.error.issues.some((issue) => issue.path[0] === "method");
        const message = hasMethod
            ? "request must include string method field"
            : "request contains unsupported or invalid top-level fields";
        return {
            ok: false,
            id: requestId,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, message),
        };
    }
    const parsedMethod = diffReviewMethodSchema.safeParse(envelope.data.method);
    if (!parsedMethod.success) {
        return {
            ok: false,
            id: envelope.data.id,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.methodNotFound, `unknown diff review method '${envelope.data.method}'`),
        };
    }
    const paramsResult = validateDiffReviewParams(parsedMethod.data, envelope.data.params ?? EMPTY_OBJECT);
    if (!paramsResult.ok) {
        return {
            ok: false,
            id: envelope.data.id,
            error: paramsResult.error,
        };
    }
    return {
        ok: true,
        message: {
            version: DIFF_REVIEW_PROTOCOL_VERSION,
            type: "request",
            id: envelope.data.id,
            method: parsedMethod.data,
            params: paramsResult.value,
        },
    };
}
function parseDecodedDiffReviewResponse(decoded) {
    const envelope = diffReviewResponseSchema.safeParse(decoded);
    if (!envelope.success) {
        const idField = diffReviewIdFieldSchema.safeParse(decoded);
        const parsedId = idField.success
            ? z.union([diffReviewRequestIdSchema, z.null()]).safeParse(idField.data.id)
            : null;
        const responseId = parsedId?.success ? parsedId.data : null;
        return {
            ok: false,
            id: responseId,
            error: createDiffReviewError(DIFF_REVIEW_ERROR_CODES.invalidRequest, "response contains unsupported or invalid top-level fields"),
        };
    }
    return {
        ok: true,
        message: envelope.data,
    };
}
function formatParamsError(method, _error) {
    switch (method) {
        case "initialize":
            return "initialize.token must be a non-empty string";
        case "session.get_context":
        case "session.list_files":
        case "session.cancel":
        case "session.close":
            return `${method} does not accept parameters`;
        case "session.get_diff":
            return "session.get_diff.path must be a non-empty string when provided";
        case "session.set_ui_text":
            return "session.set_ui_text.text must be a string";
        case "thread.submit_message":
            return "thread.submit_message requires a non-empty message, optional threadId, and optional forkFromThreadId when creating a new thread";
        case "session.return_review":
            return "session.return_review.review must be a non-empty string";
    }
}
//# sourceMappingURL=protocol.js.map