"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TorrentClient = void 0;
const util_1 = require("util");
const parse_torrent_1 = __importDefault(require("parse-torrent"));
const mime_types_1 = require("mime-types");
const _1 = require(".");
class TorrentClient {
    constructor(config, adapter) {
        this.config = config;
        this.adapter = adapter;
        this.torrents = {};
        this.cleanLocked = false;
    }
    static async create(config, adapter) {
        return new TorrentClient({
            ...config,
            announce: [
                ...(config.announce || []),
                ...(config.useDefaultTrackers
                    ? await _1.downloadTrackers()
                        .then((v) => {
                        config.logger.info(`Loaded ${v.length} trackers`);
                        return v;
                    })
                        .catch(() => {
                        config.logger.warn('Failed to load tracker list');
                        return [];
                    })
                    : []),
            ],
        }, adapter || new _1.WebtorrentAdapter());
    }
    getTorrents() {
        return Object.values(this.torrents);
    }
    getTorrent(infoHash) {
        return this.torrents[infoHash];
    }
    async removeTorrent(infoHash) {
        const torrent = this.torrents[infoHash];
        if (torrent) {
            await torrent.remove();
            delete this.torrents[infoHash];
        }
    }
    async addTorrent(link) {
        let parsedLink;
        try {
            parsedLink = await util_1.promisify(parse_torrent_1.default.remote)(link);
        }
        catch (err) {
            throw new _1.TorrentClientError(`Cannot parse torrent: ${err instanceof Error ? err.message : err}, link: ${link}`);
        }
        if (!parsedLink) {
            throw new _1.TorrentClientError(`Cannot parse torrent: ${link}`);
        }
        const magnet = parse_torrent_1.default.toMagnetURI({
            ...parsedLink,
            announce: [
                ...new Set([...(parsedLink.announce || []), ...(this.config.announce || [])]),
            ],
            urlList: [...new Set([...(parsedLink.urlList || []), ...(this.config.urlList || [])])],
            // @ts-ignore
            peerAddresses: [
                ...new Set([
                    // @ts-ignore
                    ...(parsedLink.peerAddresses || []),
                    ...(this.config.peerAddresses || []),
                ]),
            ],
        });
        const infoHash = parsedLink.infoHash;
        const foundTorrent = this.torrents[infoHash];
        if (foundTorrent) {
            this.torrents[infoHash] = {
                ...foundTorrent,
                updated: new Date(),
            };
            return foundTorrent;
        }
        const torrent = await this.adapter.add(magnet, this.config.path).then((v) => ({
            ...v,
            link,
            infoHash,
            created: new Date(),
            updated: new Date(),
            files: v.files.map((f) => ({
                ...f,
                type: mime_types_1.lookup(f.name) || '',
            })),
        }));
        this.torrents[torrent.infoHash] = torrent;
        setTimeout(() => {
            this.checkForExpiredTorrents().catch((err) => {
                this.config.logger.error(err);
            });
        }, 1000);
        return torrent;
    }
    async destroy() {
        this.config.logger.info('Closing torrent client');
        await this.adapter.destroy();
    }
    async checkForExpiredTorrents() {
        if (this.cleanLocked) {
            return;
        }
        this.cleanLocked = true;
        try {
            const torrentToRemove = Object.values(this.torrents).filter((torrent) => Date.now() - torrent.updated.getTime() > this.config.ttl * 1000);
            for (const torrent of torrentToRemove) {
                this.config.logger.info(`Removing expired ${torrent.name} torrent`);
                await this.removeTorrent(torrent.infoHash);
            }
        }
        finally {
            this.cleanLocked = false;
        }
    }
}
exports.TorrentClient = TorrentClient;
