import bencode from "bencode";
import { createHash } from "crypto";
import { join } from "path";
import { parseTitle } from "./searchee.js";
import { fallback, isTruthy } from "./utils.js";
export function updateMetafileMetadata(metafile, metadata) {
    if (metadata["qBt-category"]) {
        metafile.category = metadata["qBt-category"].toString();
    }
    if (metadata["qBt-tags"]) {
        metafile.tags = metadata["qBt-tags"].toString().split(",");
    }
    if (metadata.trackers) {
        metafile.trackers = metadata.trackers
            .map((tier) => sanitizeTrackerUrls(tier))
            .flat();
    }
}
export function sanitizeTrackerUrl(url) {
    try {
        return new URL(url).host;
    }
    catch {
        return null;
    }
}
function sanitizeTrackerUrls(urls) {
    return urls
        .map((url) => sanitizeTrackerUrl(url.toString()))
        .filter(isTruthy);
}
function sumLength(sum, file) {
    return sum + file.length;
}
function ensure(bool, fieldName) {
    if (!bool)
        throw new Error(`Torrent is missing required field: ${fieldName}`);
}
function sha1(buf) {
    const hash = createHash("sha1");
    hash.update(new Uint8Array(buf));
    return hash.digest("hex");
}
export class Metafile {
    infoHash;
    length;
    name;
    /**
     * Always name, exists for compatibility with Searchee
     */
    title;
    pieceLength;
    files;
    isSingleFileTorrent;
    category;
    tags;
    trackers;
    raw;
    constructor(raw) {
        ensure(raw.info, "info");
        ensure(raw.info["name.utf-8"] || raw.info.name, "info.name");
        ensure(raw.info["piece length"], "info['piece length']");
        ensure(raw.info.pieces, "info.pieces");
        if (raw.info.files) {
            raw.info.files.forEach((file) => {
                ensure(typeof file.length === "number", "info.files[0].length");
                ensure(file["path.utf-8"] || file.path, "info.files[0].path");
            });
        }
        else {
            ensure(typeof raw.info.length === "number", "info.length");
        }
        this.raw = raw;
        this.infoHash = sha1(bencode.encode(raw.info));
        this.name = fallback(raw.info["name.utf-8"], raw.info.name).toString();
        this.pieceLength = raw.info["piece length"];
        if (!raw.info.files) {
            // length exists if files doesn't exist
            const length = raw.info.length;
            this.files = [
                {
                    name: this.name,
                    path: this.name,
                    length: length,
                },
            ];
            this.length = length;
            this.isSingleFileTorrent = true;
        }
        else {
            this.files = raw.info.files
                .map((file) => {
                const rawPathSegments = fallback(file["path.utf-8"], file.path);
                const pathSegments = rawPathSegments.map((buf) => {
                    const seg = buf.toString();
                    // convention for zero-length path segments is to treat them as underscores
                    return seg === "" ? "_" : seg;
                });
                return {
                    name: pathSegments[pathSegments.length - 1],
                    length: file.length,
                    path: join(this.name, ...pathSegments),
                };
            })
                .sort((a, b) => a.path.localeCompare(b.path));
            this.length = this.files.reduce(sumLength, 0);
            this.isSingleFileTorrent = false;
        }
        this.title = parseTitle(this.name, this.files) ?? this.name;
        const announceList = raw["announce-list"];
        this.trackers =
            Array.isArray(announceList) && announceList.length > 0
                ? announceList.map((tier) => sanitizeTrackerUrls(tier)).flat()
                : raw.announce
                    ? sanitizeTrackerUrls([raw.announce])
                    : [];
    }
    static decode(buf) {
        return new Metafile(bencode.decode(buf));
    }
    getFileSystemSafeName() {
        return this.name.replace("/", "");
    }
    encode() {
        return bencode.encode(this.raw);
    }
}
//# sourceMappingURL=parseTorrent.js.map