import { z } from "zod";
import { asUint8Array } from "./terminal.js";
export const FileTransferOpcode = {
    FileBegin: 0x10,
    FileChunk: 0x11,
    FileEnd: 0x12,
};
export const FileBeginMetadataSchema = z
    .object({
    mime: z.string().min(1),
    size: z.number().int().nonnegative(),
    encoding: z.enum(["utf-8", "binary"]),
    modifiedAt: z.string(),
})
    .strict();
export function encodeFileTransferFrame(input) {
    const requestId = encodeRequestId(input.requestId);
    if (input.opcode === FileTransferOpcode.FileBegin) {
        const metadata = encodeJsonPayload(input.metadata);
        if (metadata.byteLength > 0xffff) {
            throw new RangeError("FileBegin metadata is too long");
        }
        const bytes = new Uint8Array(4 + requestId.byteLength + metadata.byteLength);
        const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
        bytes[0] = input.opcode;
        bytes[1] = requestId.byteLength;
        bytes.set(requestId, 2);
        view.setUint16(2 + requestId.byteLength, metadata.byteLength);
        bytes.set(metadata, 4 + requestId.byteLength);
        return bytes;
    }
    const payload = input.opcode === FileTransferOpcode.FileChunk
        ? (asUint8Array(input.payload ?? new Uint8Array()) ?? new Uint8Array())
        : new Uint8Array();
    const bytes = new Uint8Array(2 + requestId.byteLength + payload.byteLength);
    bytes[0] = input.opcode;
    bytes[1] = requestId.byteLength;
    bytes.set(requestId, 2);
    bytes.set(payload, 2 + requestId.byteLength);
    return bytes;
}
export function decodeFileTransferFrame(bytes) {
    if (bytes.byteLength < 2) {
        return null;
    }
    const opcode = bytes[0];
    if (!isFileTransferOpcode(opcode)) {
        return null;
    }
    const requestIdLength = bytes[1];
    if (requestIdLength === 0 || requestIdLength > bytes.byteLength - 2) {
        return null;
    }
    const requestId = decodeRequestId(bytes.subarray(2, 2 + requestIdLength));
    const body = bytes.subarray(2 + requestIdLength);
    if (opcode === FileTransferOpcode.FileBegin) {
        if (body.byteLength < 2) {
            return null;
        }
        const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
        const metadataLength = view.getUint16(0);
        if (metadataLength !== body.byteLength - 2) {
            return null;
        }
        const metadataBytes = body.subarray(2);
        const result = FileBeginMetadataSchema.safeParse(decodeJsonPayload(metadataBytes));
        return result.success
            ? { opcode, requestId, metadata: result.data, payload: new Uint8Array() }
            : null;
    }
    if (opcode === FileTransferOpcode.FileChunk) {
        return { opcode, requestId, payload: body };
    }
    if (body.byteLength !== 0) {
        return null;
    }
    return { opcode, requestId, payload: new Uint8Array() };
}
function isFileTransferOpcode(value) {
    return (value === FileTransferOpcode.FileBegin ||
        value === FileTransferOpcode.FileChunk ||
        value === FileTransferOpcode.FileEnd);
}
function encodeJsonPayload(value) {
    return new TextEncoder().encode(JSON.stringify(value));
}
function encodeRequestId(requestId) {
    const bytes = new TextEncoder().encode(requestId);
    if (bytes.byteLength === 0) {
        throw new RangeError("File transfer requestId is required");
    }
    if (bytes.byteLength > 0xff) {
        throw new RangeError("File transfer requestId is too long");
    }
    return bytes;
}
function decodeRequestId(bytes) {
    return new TextDecoder().decode(bytes);
}
function decodeJsonPayload(bytes) {
    try {
        return JSON.parse(new TextDecoder().decode(bytes));
    }
    catch {
        return null;
    }
}
//# sourceMappingURL=file-transfer.js.map