// FileSource：发送端的「字节来源」抽象，把传输层（p2p / relay）与具体文件载体解耦。
// web / 桌面用真 File（slice 即内存读）；iOS 无头引擎用 Range 拉取——向原生
// WKURLSchemeHandler 按需取片，整文件永不进 WebView 内存，避 jetsam（R-iOS-4）。
// 两实现字节语义一致，故 p2p 的块读 / 子片发送、relay 的 1 MiB 分块全程不感知来源差异。

export interface FileSource
{
    readonly name: string;
    readonly size: number;
    readonly type: string;
    // 读取 [start, end) 字节区间（end 独占），返回该区间的副本。底层 buffer 固定为
    // ArrayBuffer（非 SharedArrayBuffer），以满足 RTCDataChannel.send / fetch body 的类型约束。
    slice(start: number, end: number): Promise<Uint8Array<ArrayBuffer>>;
}

// fileSource：包一个已在内存的 File（web / 桌面，以及 iOS「收到的文件再转发」等场景）。
// slice 行为与既有 `new Uint8Array(await file.slice(...).arrayBuffer())` 逐字节一致，
// 故对非 iOS 平台零行为变更。
export function fileSource(file: File): FileSource
{
    return {
        name: file.name,
        size: file.size,
        type: file.type || "application/octet-stream",
        async slice(start, end)
        {
            return new Uint8Array(await file.slice(start, end).arrayBuffer());
        },
    };
}

// rangeSource：经 HTTP Range 向原生 WKURLSchemeHandler 惰性拉取文件片（iOS 大文件流式发送）。
// 每次 slice 只取 [start, end) 一段，WebView 常驻内存上限即单块大小（p2p 的
// READ_BLOCK_SIZE = 4 MiB）。url 形如 `cdrop-file://<id>`；原生据 Range 头 seek 文件、
// 回 206 Partial Content 仅含该区间字节。
export function rangeSource(
    url: string,
    name: string,
    size: number,
    type: string,
): FileSource
{
    return {
        name,
        size,
        type: type || "application/octet-stream",
        async slice(start, end)
        {
            const resp = await fetch(url, {
                headers: { Range: `bytes=${start}-${end - 1}` },
            });
            if (!resp.ok && resp.status !== 206)
            {
                throw new Error(`range fetch ${resp.status} for [${start}, ${end})`);
            }
            const buf = new Uint8Array(await resp.arrayBuffer());
            // 正确性兜底：若原生未识别 Range（返回 200 整文件），客户端再切一刀，使返回
            // 严格等于请求区间。原生正常回 206 时此分支不触发，内存仍按块有界。
            if (resp.status === 200 && buf.byteLength > end - start)
            {
                return buf.subarray(start, end);
            }
            return buf;
        },
    };
}
