#!/usr/bin/env node
import chalk from "chalk";
import { Option, program } from "commander";
import { setTimeout as delay } from "node:timers/promises";
import { getApiKey, resetApiKey, setApiKey } from "./auth.js";
import { PROGRAM_NAME, PROGRAM_VERSION } from "./constants.js";
import { db } from "./db.js";
import { updateTorrentCache } from "./decide.js";
import { createDevLogin } from "./devLogin.js";
import { diffCmd } from "./diff.js";
import { clearIndexerFailures } from "./indexers.js";
import { injectSavedTorrents, restoreFromTorrentCache } from "./inject.js";
import { jobsLoop } from "./jobs.js";
import { bulkSearch, scanRssFeeds } from "./pipeline.js";
import { sendTestNotification } from "./pushNotifier.js";
import { serve } from "./server.js";
import { withFullRuntime, withMinimalRuntime } from "./startup.js";
import { indexTorrentsAndDataDirs, parseTorrentFromPath } from "./torrent.js";
import { resetUsers } from "./userAuth.js";
const verboseOption = new Option("-v, --verbose", "Log verbose output").default(false);
async function runDaemonStartupWork() {
    await indexTorrentsAndDataDirs({ startup: true });
    await jobsLoop();
}
program.name(PROGRAM_NAME);
program.description(chalk.yellow.bold(`${PROGRAM_NAME} v${PROGRAM_VERSION}`));
program.version(PROGRAM_VERSION, "-V, --version", "output the current version");
program
    .command("update-torrent-cache-trackers")
    .description("Update announce urls in the torrent cache")
    .usage("<old-announce-url> <new-announce-url>")
    .argument("old-announce-url", 'A substring of the announce url to replace, e.g. update-torrent-cache-trackers "old.example.com" "new.example.com"')
    .argument("new-announce-url", 'A substring of the new announce url to replace the old one with, e.g. update-torrent-cache-trackers "myoldpasskey" "mynewpasskey"')
    .action(withMinimalRuntime(updateTorrentCache));
program
    .command("diff")
    .description("Analyze two torrent files for cross-seed compatibility")
    .argument("searchee")
    .argument("candidate")
    .action(withMinimalRuntime(diffCmd, { migrate: false }));
program
    .command("tree")
    .description("Print a torrent's file tree")
    .argument("torrent")
    .action(withMinimalRuntime(async (torrentPath) => {
    console.log("Use `cross-seed diff` to compare two .torrent files");
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { category, isSingleFileTorrent, raw, tags, ...meta } = await parseTorrentFromPath(torrentPath);
    console.log(meta);
}, { migrate: false }));
program
    .command("clear-indexer-failures")
    .description("Clear the cached details of indexers (failures and caps)")
    .action(withMinimalRuntime(clearIndexerFailures));
program
    .command("clear-cache")
    .description("Clear the cache without causing torrents to be re-snatched and reset the timestamps for excludeOlder and excludeRecentSearch")
    .action(withMinimalRuntime(async () => {
    console.log("Clearing cache...");
    await db("decision").whereNull("info_hash").del();
    await db("timestamp").del();
}));
program
    .command("clear-client-cache")
    .description("Clear cross-seed's cache of your client's torrents. Only necessary if you have recently changed clients or modified the torrents in client and don't want to wait on the cleanup job.")
    .action(withMinimalRuntime(async () => {
    console.log("Clearing client cache...");
    await db("torrent").del();
    await db("client_searchee").del();
    await db("data").del();
    await db("ensemble").del();
}));
program
    .command("api-key")
    .description("Show the api key")
    .addOption(new Option("--api-key <key>", "Provide your own API key to override the autogenerated one."))
    .action(withMinimalRuntime(async (options) => {
    if (options.apiKey)
        return setApiKey(options.apiKey);
    return getApiKey();
}));
program
    .command("reset-api-key")
    .description("Reset the api key")
    .action(withMinimalRuntime(resetApiKey));
program
    .command("reset-user")
    .description("Delete all users and sessions")
    .action(withMinimalRuntime(resetUsers));
program
    .command("dev-login")
    .description("Create a development login URL")
    .option("--user <username>", "Use a specific username")
    .option("--origin <origin>", "Origin where cross-seed is running", "http://localhost:2468")
    .option("--redirect-to <path>", "Local path to open after login", "/settings/general")
    .action(withMinimalRuntime(createDevLogin));
program
    .command("daemon")
    .description("Start the cross-seed daemon")
    .option("-p, --port <port>", "Listen on a custom port", (n) => parseInt(n))
    .option("--host <host>", "Bind to a specific IP address")
    .option("--base-path <path>", "Serve cross-seed behind this base path", "")
    .option("--no-port", "Do not listen on any port")
    .addOption(verboseOption)
    .action(withFullRuntime(async (options) => {
    let startStartupWork;
    const startupWorkPromise = new Promise((resolve, reject) => {
        startStartupWork = () => {
            delay(1000)
                .then(runDaemonStartupWork)
                .then(resolve, reject);
        };
    });
    const serverPromise = serve(options.port, options.host, options.basePath, startStartupWork);
    if (!options.port) {
        startStartupWork();
    }
    // technically this will never resolve, but it's necessary to keep the process running
    await Promise.all([serverPromise, startupWorkPromise]);
}));
program
    .command("rss")
    .description("Run an rss scan")
    .addOption(verboseOption)
    .action(withFullRuntime(async () => {
    await indexTorrentsAndDataDirs({ startup: true });
    await scanRssFeeds();
}));
program
    .command("search")
    .description("Search for cross-seeds")
    .addOption(new Option("--torrents <torrents...>", "torrent files separated by spaces").hideHelp())
    .addOption(verboseOption)
    .action(withFullRuntime(() => bulkSearch()));
program
    .command("inject")
    .description("Inject saved cross-seeds into your client (without filtering, see docs)")
    .option("--inject-dir <dir>", "Directory of torrent files to try to inject")
    .option("--ignore-titles", "Searchee and candidate titles do not need to pass the fuzzy matching check (useful if `cross-seed inject` erroneously rejects by title)")
    .option("--no-ignore-titles", "Searchee and candidate titles need to pass the fuzzy matching check (default)")
    .addOption(verboseOption)
    .action(withFullRuntime(injectSavedTorrents));
program
    .command("restore")
    .description("Use snatched torrents from torrent_cache to attempt to restore cross seeds. Will need to run `cross-seed inject` afterwards with dataDirs configured.")
    .addOption(verboseOption)
    .action(withFullRuntime(restoreFromTorrentCache));
program
    .command("test-notification")
    .description("Send a test notification")
    .addOption(verboseOption)
    .action(withFullRuntime(async () => {
    const results = await sendTestNotification();
    if (results.some((result) => !result.ok)) {
        process.exitCode = 1;
    }
}));
program.showHelpAfterError("(add --help for additional information)");
await program.parseAsync();
//# sourceMappingURL=cmd.js.map