import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import http from "node:http";
import { EventEmitter, once } from "node:events";
import AdmZip from "adm-zip";
import { afterEach, describe, expect, it } from "vitest";
import { DownloadManager } from "../src/main/download-manager";
import { defaultSettings } from "../src/main/constants";
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
import { getProviderUsageDayKey } from "../src/shared/provider-daily-limits";
import { createStoragePaths, emptySession } from "../src/main/storage";

const tempDirs: string[] = [];
const originalFetch = globalThis.fetch;

async function waitFor(predicate: () => boolean, timeoutMs = 15000): Promise<void> {
  const started = Date.now();
  while (!predicate()) {
    if (Date.now() - started > timeoutMs) {
      throw new Error("waitFor timeout");
    }
    await new Promise((resolve) => setTimeout(resolve, 60));
  }
}

async function removeDirWithRetries(dir: string): Promise<void> {
  let lastError: unknown = null;
  for (let attempt = 1; attempt <= 5; attempt += 1) {
    try {
      fs.rmSync(dir, { recursive: true, force: true });
      return;
    } catch (error) {
      lastError = error;
      await new Promise((resolve) => setTimeout(resolve, attempt * 80));
    }
  }
  if (lastError) {
    throw lastError;
  }
}

afterEach(async () => {
  globalThis.fetch = originalFetch;
  for (const dir of tempDirs.splice(0)) {
    await removeDirWithRetries(dir);
  }
});

describe("download manager", () => {
  function createCompletedArchiveSession(root: string, packageName: string, extractedFileName: string): {
    session: ReturnType<typeof emptySession>;
    packageId: string;
    itemId: string;
    outputDir: string;
    extractDir: string;
    originalExtractedPath: string;
  } {
    const outputDir = path.join(root, "downloads", packageName);
    const extractDir = path.join(root, "extract", packageName);
    fs.mkdirSync(outputDir, { recursive: true });

    const zip = new AdmZip();
    zip.addFile(extractedFileName, Buffer.from("video"));
    const archivePath = path.join(outputDir, "episode.zip");
    zip.writeZip(archivePath);
    const archiveSize = fs.statSync(archivePath).size;

    const session = emptySession();
    const packageId = `${packageName}-pkg`;
    const itemId = `${packageName}-item`;
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: packageName,
      outputDir,
      extractDir,
      status: "downloading",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: `https://dummy/${packageName}`,
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: archiveSize,
      totalBytes: archiveSize,
      progressPercent: 100,
      fileName: "episode.zip",
      targetPath: archivePath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (100 MB)",
      createdAt,
      updatedAt: createdAt
    };

    return {
      session,
      packageId,
      itemId,
      outputDir,
      extractDir,
      originalExtractedPath: path.join(extractDir, extractedFileName)
    };
  }

  it("retries interrupted streams and resumes download", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(512 * 1024, 11);
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/direct") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;

      if (directCalls === 1 && start === 0) {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.write(binary.subarray(0, Math.floor(binary.length / 2)));
        res.socket?.destroy();
        return;
      }

      const chunk = binary.subarray(start);
      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      res.end(chunk);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/direct`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "episode.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "retry", links: ["https://dummy/retry"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(item?.retries).toBeGreaterThan(0);
      expect(directCalls).toBeGreaterThan(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("requests a fresh direct link after repeated same-link download failures", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(256 * 1024, 17);
    let badCalls = 0;
    let goodCalls = 0;
    let unrestrictCalls = 0;

    const server = http.createServer((req, res) => {
      const route = req.url || "";
      if (route === "/bad") {
        badCalls += 1;
        const range = String(req.headers.range || "");
        const match = range.match(/bytes=(\d+)-/i);
        const start = match ? Number(match[1]) : 0;
        const end = Math.min(binary.length, start + 64 * 1024);
        const chunk = binary.subarray(start, end);
        if (start > 0) {
          res.statusCode = 206;
          res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
        } else {
          res.statusCode = 200;
        }
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(chunk.length));
        res.write(chunk);
        res.socket?.destroy();
        return;
      }

      if (route === "/good") {
        goodCalls += 1;
        const range = String(req.headers.range || "");
        const match = range.match(/bytes=(\d+)-/i);
        const start = match ? Number(match[1]) : 0;
        const chunk = binary.subarray(start);
        if (start > 0) {
          res.statusCode = 206;
          res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
        } else {
          res.statusCode = 200;
        }
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(chunk.length));
        res.end(chunk);
        return;
      }

      res.statusCode = 404;
      res.end("not-found");
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const badUrl = `http://127.0.0.1:${address.port}/bad`;
    const goodUrl = `http://127.0.0.1:${address.port}/good`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        unrestrictCalls += 1;
        return new Response(
          JSON.stringify({
            download: unrestrictCalls === 1 ? badUrl : goodUrl,
            filename: "refresh-link.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false,
          retryLimit: 0
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "fresh-link", links: ["https://dummy/fresh-link"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 12000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(item?.downloadedBytes).toBe(binary.length);
      expect(unrestrictCalls).toBeGreaterThanOrEqual(2);
      expect(badCalls).toBe(3);
      expect(goodCalls).toBeGreaterThanOrEqual(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("preserves partial files and requests a fresh direct link when resume gets HTTP 200", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(256 * 1024, 21);
    const pkgDir = path.join(root, "downloads", "resume-ignored");
    fs.mkdirSync(pkgDir, { recursive: true });
    const existingTargetPath = path.join(pkgDir, "resume-ignored.mkv");
    const partialSize = 96 * 1024;
    fs.writeFileSync(existingTargetPath, binary.subarray(0, partialSize));

    let unrestrictCalls = 0;
    let ignoredRangeCalls = 0;
    let resumeCalls = 0;
    const resumeStarts: number[] = [];

    const server = http.createServer((req, res) => {
      const route = req.url || "";
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;

      if (route === "/ignored-range") {
        ignoredRangeCalls += 1;
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.end(binary);
        return;
      }

      if (route === "/resume-ok") {
        resumeCalls += 1;
        resumeStarts.push(start);
        const chunk = binary.subarray(start);
        if (start > 0) {
          res.statusCode = 206;
          res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
        } else {
          res.statusCode = 200;
        }
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(chunk.length));
        res.end(chunk);
        return;
      }

      res.statusCode = 404;
      res.end("not-found");
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const ignoredRangeUrl = `http://127.0.0.1:${address.port}/ignored-range`;
    const resumeUrl = `http://127.0.0.1:${address.port}/resume-ok`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        unrestrictCalls += 1;
        return new Response(
          JSON.stringify({
            download: unrestrictCalls === 1 ? ignoredRangeUrl : resumeUrl,
            filename: "resume-ignored.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const packageId = "resume-ignored-pkg";
      const itemId = "resume-ignored-item";
      const createdAt = Date.now() - 10_000;

      session.packageOrder = [packageId];
      session.packages[packageId] = {
        id: packageId,
        name: "resume-ignored",
        outputDir: pkgDir,
        extractDir: path.join(root, "extract", "resume-ignored"),
        status: "queued",
        itemIds: [itemId],
        cancelled: false,
        enabled: true,
        createdAt,
        updatedAt: createdAt
      };
      session.items[itemId] = {
        id: itemId,
        packageId,
        url: "https://dummy/resume-ignored",
        provider: null,
        status: "queued",
        retries: 0,
        speedBps: 0,
        downloadedBytes: partialSize,
        totalBytes: binary.length,
        progressPercent: Math.floor((partialSize / binary.length) * 100),
        fileName: "resume-ignored.mkv",
        targetPath: existingTargetPath,
        resumable: true,
        attempts: 0,
        lastError: "",
        fullStatus: "Wartet",
        createdAt,
        updatedAt: createdAt
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          retryLimit: 1,
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(item?.status).toBe("completed");
      expect(item?.downloadedBytes).toBe(binary.length);
      expect(unrestrictCalls).toBeGreaterThanOrEqual(2);
      expect(ignoredRangeCalls).toBeGreaterThanOrEqual(1);
      expect(resumeCalls).toBeGreaterThanOrEqual(1);
      expect(resumeStarts).toContain(partialSize);
      expect(fs.statSync(existingTargetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("assigns unique target paths for same filenames in parallel", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(64 * 1024, 9);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/same") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      setTimeout(() => {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.end(binary);
      }, 260);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/same`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "same-release.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 2
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "same-name", links: ["https://dummy/first", "https://dummy/second"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const items = Object.values(manager.getSnapshot().session.items);
      expect(items).toHaveLength(2);
      expect(items.every((item) => item.status === "completed")).toBe(true);
      const targetPaths = items.map((item) => item.targetPath);
      expect(new Set(targetPaths).size).toBe(2);
      for (const targetPath of targetPaths) {
        expect(fs.existsSync(targetPath)).toBe(true);
      }
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("does not mark truncated archive downloads as completed", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const advertised = Buffer.alloc(96 * 1024, 5);
    const actual = advertised.subarray(0, advertised.length - 2048);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/short-archive") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(actual.length));
      res.end(actual);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/short-archive`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "broken.part01.rar",
            filesize: advertised.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false,
          retryLimit: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "short-archive", links: ["https://dummy/short-archive"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("failed");
      expect(item?.fullStatus || item?.lastError || "").toContain("download_underflow");
      expect(item?.downloadedBytes).toBe(actual.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("continues downloading while package post-processing is pending", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(80 * 1024, 7);

    const server = http.createServer((req, res) => {
      const route = req.url || "";
      if (route !== "/first" && route !== "/second") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const firstUrl = `http://127.0.0.1:${address.port}/first`;
    const secondUrl = `http://127.0.0.1:${address.port}/second`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        const body = init?.body;
        const bodyText = body instanceof URLSearchParams ? body.toString() : String(body || "");
        const originalLink = new URLSearchParams(bodyText).get("link") || "";
        const directUrl = originalLink.includes("second") ? secondUrl : firstUrl;
        const filename = originalLink.includes("second") ? "second.bin" : "first.bin";
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename,
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    let releaseBlockedPostProcess: ((value?: void | PromiseLike<void>) => void) | undefined;
    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      const blocker = new Promise<void>((resolve) => {
        releaseBlockedPostProcess = resolve;
      });
      (manager as unknown as { packagePostProcessQueue: Promise<void> }).packagePostProcessQueue = blocker;

      manager.addPackages([
        { name: "first", links: ["https://dummy/first"] },
        { name: "second", links: ["https://dummy/second"] }
      ]);

      const initial = manager.getSnapshot();
      const firstPackage = initial.session.packageOrder[0];
      const secondPackage = initial.session.packageOrder[1];
      const firstItem = initial.session.packages[firstPackage]?.itemIds[0] || "";
      const secondItem = initial.session.packages[secondPackage]?.itemIds[0] || "";

      manager.start();

      await waitFor(() => manager.getSnapshot().session.items[firstItem]?.status === "completed", 12000);
      await waitFor(() => {
        const state = manager.getSnapshot().session.items[secondItem]?.status;
        return state === "validating" || state === "downloading" || state === "integrity_check" || state === "completed";
      }, 6000);

      if (releaseBlockedPostProcess) {
        releaseBlockedPostProcess();
      }
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const done = manager.getSnapshot();
      expect(done.session.items[firstItem]?.status).toBe("completed");
      expect(done.session.items[secondItem]?.status).toBe("completed");
    } finally {
      if (releaseBlockedPostProcess) {
        releaseBlockedPostProcess();
      }
      server.close();
      await once(server, "close");
    }
  });

  it("recovers from stalled download streams without manual pause/resume", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(300 * 1024, 17);
    const previousStallTimeout = process.env.RD_STALL_TIMEOUT_MS;
    process.env.RD_STALL_TIMEOUT_MS = "2500";
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/stall") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;

      if (directCalls === 1 && start === 0) {
        const firstChunk = Math.floor(binary.length / 3);
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.write(binary.subarray(0, firstChunk));
        setTimeout(() => {
          if (!res.writableEnded && !res.destroyed) {
            res.end(binary.subarray(firstChunk));
          }
        }, 5000);
        return;
      }

      const chunk = binary.subarray(start);
      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      res.end(chunk);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/stall`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "stall.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "stall", links: ["https://dummy/stall"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThan(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      if (previousStallTimeout === undefined) {
        delete process.env.RD_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_STALL_TIMEOUT_MS = previousStallTimeout;
      }
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("recovers when direct download connection stalls before first byte", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(220 * 1024, 23);
    const previousStallTimeout = process.env.RD_STALL_TIMEOUT_MS;
    const previousConnectTimeout = process.env.RD_CONNECT_TIMEOUT_MS;
    process.env.RD_STALL_TIMEOUT_MS = "2500";
    process.env.RD_CONNECT_TIMEOUT_MS = "1800";
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/connect-stall") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      if (directCalls === 1) {
        setTimeout(() => {
          if (res.destroyed || res.writableEnded) {
            return;
          }
          res.statusCode = 200;
          res.setHeader("Accept-Ranges", "bytes");
          res.setHeader("Content-Length", String(binary.length));
          res.end(binary);
        }, 5200);
        return;
      }

      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/connect-stall`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "connect-stall.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "connect-stall", links: ["https://dummy/connect-stall"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThan(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      if (previousStallTimeout === undefined) {
        delete process.env.RD_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_STALL_TIMEOUT_MS = previousStallTimeout;
      }
      if (previousConnectTimeout === undefined) {
        delete process.env.RD_CONNECT_TIMEOUT_MS;
      } else {
        process.env.RD_CONNECT_TIMEOUT_MS = previousConnectTimeout;
      }
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("recovers when direct download stalls before first response bytes", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(180 * 1024, 12);
    const previousStallTimeout = process.env.RD_STALL_TIMEOUT_MS;
    const previousConnectTimeout = process.env.RD_CONNECT_TIMEOUT_MS;
    process.env.RD_STALL_TIMEOUT_MS = "2500";
    process.env.RD_CONNECT_TIMEOUT_MS = "2000";
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/stall-connect") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      if (directCalls === 1) {
        setTimeout(() => {
          if (res.writableEnded || res.destroyed || res.headersSent) {
            return;
          }
          res.statusCode = 200;
          res.setHeader("Accept-Ranges", "bytes");
          res.setHeader("Content-Length", String(binary.length));
          res.end(binary);
        }, 5000);
        return;
      }

      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/stall-connect`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "stall-connect.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "stall-connect", links: ["https://dummy/stall-connect"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThan(1);
    } finally {
      if (previousStallTimeout === undefined) {
        delete process.env.RD_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_STALL_TIMEOUT_MS = previousStallTimeout;
      }
      if (previousConnectTimeout === undefined) {
        delete process.env.RD_CONNECT_TIMEOUT_MS;
      } else {
        process.env.RD_CONNECT_TIMEOUT_MS = previousConnectTimeout;
      }
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("recovers via global watchdog when stream hangs without reader timeout", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(240 * 1024, 31);
    const previousStallTimeout = process.env.RD_STALL_TIMEOUT_MS;
    const previousConnectTimeout = process.env.RD_CONNECT_TIMEOUT_MS;
    const previousGlobalWatchdog = process.env.RD_GLOBAL_STALL_TIMEOUT_MS;
    process.env.RD_STALL_TIMEOUT_MS = "120000";
    process.env.RD_CONNECT_TIMEOUT_MS = "120000";
    process.env.RD_GLOBAL_STALL_TIMEOUT_MS = "2500";
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/watchdog-stall") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      if (directCalls === 1) {
        const firstChunk = Math.floor(binary.length / 3);
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.write(binary.subarray(0, firstChunk));
        return;
      }

      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/watchdog-stall`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "watchdog-stall.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "watchdog-stall", links: ["https://dummy/watchdog-stall"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThan(1);
    } finally {
      if (previousStallTimeout === undefined) {
        delete process.env.RD_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_STALL_TIMEOUT_MS = previousStallTimeout;
      }
      if (previousConnectTimeout === undefined) {
        delete process.env.RD_CONNECT_TIMEOUT_MS;
      } else {
        process.env.RD_CONNECT_TIMEOUT_MS = previousConnectTimeout;
      }
      if (previousGlobalWatchdog === undefined) {
        delete process.env.RD_GLOBAL_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_GLOBAL_STALL_TIMEOUT_MS = previousGlobalWatchdog;
      }
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("recovers when write stream backpressure never drains", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(180 * 1024, 19);
    const previousStallTimeout = process.env.RD_STALL_TIMEOUT_MS;
    process.env.RD_STALL_TIMEOUT_MS = "2200";
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/drain-stall") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      directCalls += 1;
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/drain-stall`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "drain-stall.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    const originalCreateWriteStream = fs.createWriteStream;
    let writeStreamCalls = 0;
    const fsMutable = fs as unknown as { createWriteStream: typeof fs.createWriteStream };
    fsMutable.createWriteStream = ((...args: Parameters<typeof fs.createWriteStream>) => {
      writeStreamCalls += 1;
      if (writeStreamCalls === 1) {
        class HangingWriteStream extends EventEmitter {
          public closed = false;

          public destroyed = false;

          public write(): boolean {
            return false;
          }

          public end(): void {
            this.closed = true;
            this.emit("close");
          }
        }
        return new HangingWriteStream() as unknown as ReturnType<typeof fs.createWriteStream>;
      }
      return originalCreateWriteStream(...args);
    }) as typeof fs.createWriteStream;

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "drain-stall", links: ["https://dummy/drain-stall"] }]);
      const queuedSnapshot = manager.getSnapshot();
      const packageId = queuedSnapshot.session.packageOrder[0] || "";
      const itemId = queuedSnapshot.session.packages[packageId]?.itemIds[0] || "";
      manager.start();
      await waitFor(() => {
        const status = manager.getSnapshot().session.items[itemId]?.fullStatus || "";
        return status.includes("Warte auf Festplatte");
      }, 12000);
      await waitFor(() => !manager.getSnapshot().session.running, 40000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(item?.retries).toBeGreaterThan(0);
      expect(directCalls).toBeGreaterThan(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      fsMutable.createWriteStream = originalCreateWriteStream;
      if (previousStallTimeout === undefined) {
        delete process.env.RD_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_STALL_TIMEOUT_MS = previousStallTimeout;
      }
      server.close();
      await once(server, "close");
    }
  }, 45000);

  it("uses content-disposition filename when provider filename is opaque", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(96 * 1024, 13);
    const expectedName = "Banshee.S04E01.German.DL.720p.part01.rar";

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/content-name") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.setHeader("Content-Disposition", `attachment; filename="${expectedName}"`);
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/content-name`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "6f09df2984fe01378537c7cd8d7fa7ce",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          retryLimit: 1,
          autoExtract: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "content-name", links: ["https://rapidgator.net/file/6f09df2984fe01378537c7cd8d7fa7ce"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(item?.fileName).toBe(expectedName);
      expect(path.basename(item?.targetPath || "")).toBe(expectedName);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("reuses stored partial target path when queued item resumes", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(256 * 1024, 7);
    const partialSize = 64 * 1024;
    const pkgDir = path.join(root, "downloads", "resume");
    fs.mkdirSync(pkgDir, { recursive: true });
    const existingTargetPath = path.join(pkgDir, "resume.mkv");
    fs.writeFileSync(existingTargetPath, binary.subarray(0, partialSize));
    let sawResumeRange = false;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/resume") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;
      if (start === partialSize) {
        sawResumeRange = true;
      }
      const chunk = binary.subarray(start);

      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      res.end(chunk);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/resume`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "resume.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const packageId = "resume-pkg";
      const itemId = "resume-item";
      const createdAt = Date.now() - 10_000;

      session.packageOrder = [packageId];
      session.packages[packageId] = {
        id: packageId,
        name: "resume",
        outputDir: pkgDir,
        extractDir: path.join(root, "extract", "resume"),
        status: "queued",
        itemIds: [itemId],
        cancelled: false,
        enabled: true,
        createdAt,
        updatedAt: createdAt
      };
      session.items[itemId] = {
        id: itemId,
        packageId,
        url: "https://dummy/resume",
        provider: null,
        status: "queued",
        retries: 0,
        speedBps: 0,
        downloadedBytes: partialSize,
        totalBytes: binary.length,
        progressPercent: Math.floor((partialSize / binary.length) * 100),
        fileName: "resume.mkv",
        targetPath: existingTargetPath,
        resumable: true,
        attempts: 0,
        lastError: "",
        fullStatus: "Wartet",
        createdAt,
        updatedAt: createdAt
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          retryLimit: 1,
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(item?.status).toBe("completed");
      expect(item?.targetPath).toBe(existingTargetPath);
      expect(sawResumeRange).toBe(true);
      expect(fs.statSync(existingTargetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("treats HTTP 416 on full range as completed resume", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(128 * 1024, 2);
    const pkgDir = path.join(root, "downloads", "range-complete");
    fs.mkdirSync(pkgDir, { recursive: true });
    const existingTargetPath = path.join(pkgDir, "complete.mkv");
    fs.writeFileSync(existingTargetPath, binary);
    let saw416 = false;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/complete") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;
      if (start >= binary.length) {
        saw416 = true;
        res.statusCode = 416;
        res.setHeader("Content-Range", `bytes */${binary.length}`);
        res.end("");
        return;
      }

      const chunk = binary.subarray(start);
      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      res.end(chunk);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/complete`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "complete.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const packageId = "complete-pkg";
      const itemId = "complete-item";
      const createdAt = Date.now() - 10_000;

      session.packageOrder = [packageId];
      session.packages[packageId] = {
        id: packageId,
        name: "range-complete",
        outputDir: pkgDir,
        extractDir: path.join(root, "extract", "range-complete"),
        status: "queued",
        itemIds: [itemId],
        cancelled: false,
        enabled: true,
        createdAt,
        updatedAt: createdAt
      };
      session.items[itemId] = {
        id: itemId,
        packageId,
        url: "https://dummy/complete",
        provider: null,
        status: "queued",
        retries: 0,
        speedBps: 0,
        downloadedBytes: binary.length,
        totalBytes: binary.length,
        progressPercent: 100,
        fileName: "complete.mkv",
        targetPath: existingTargetPath,
        resumable: true,
        attempts: 0,
        lastError: "",
        fullStatus: "Wartet",
        createdAt,
        updatedAt: createdAt
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          retryLimit: 1,
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(saw416).toBe(true);
      expect(item?.status).toBe("completed");
      expect(item?.targetPath).toBe(existingTargetPath);
      expect(item?.downloadedBytes).toBe(binary.length);
      expect(fs.statSync(existingTargetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("recovers from HTTP 416 by restarting download from zero", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(96 * 1024, 3);
    const pkgDir = path.join(root, "downloads", "range-reset");
    fs.mkdirSync(pkgDir, { recursive: true });
    const existingTargetPath = path.join(pkgDir, "reset.mkv");
    const partialSize = 64 * 1024;
    fs.writeFileSync(existingTargetPath, binary.subarray(0, partialSize));

    let saw416 = false;
    let fullRestarted = false;
    let requestCount = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/range-reset") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      requestCount += 1;
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;

      if (requestCount === 1 && start === partialSize) {
        saw416 = true;
        res.statusCode = 416;
        res.setHeader("Content-Range", "bytes */32768");
        res.end("");
        return;
      }

      if (start === 0) {
        fullRestarted = true;
      }
      const chunk = binary.subarray(start);
      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      res.end(chunk);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/range-reset`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "reset.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const packageId = "range-reset-pkg";
      const itemId = "range-reset-item";
      const createdAt = Date.now() - 10_000;

      session.packageOrder = [packageId];
      session.packages[packageId] = {
        id: packageId,
        name: "range-reset",
        outputDir: pkgDir,
        extractDir: path.join(root, "extract", "range-reset"),
        status: "queued",
        itemIds: [itemId],
        cancelled: false,
        enabled: true,
        createdAt,
        updatedAt: createdAt
      };
      session.items[itemId] = {
        id: itemId,
        packageId,
        url: "https://dummy/range-reset",
        provider: null,
        status: "queued",
        retries: 0,
        speedBps: 0,
        downloadedBytes: partialSize,
        totalBytes: binary.length,
        progressPercent: Math.floor((partialSize / binary.length) * 100),
        fileName: "reset.mkv",
        targetPath: existingTargetPath,
        resumable: true,
        attempts: 0,
        lastError: "",
        fullStatus: "Wartet",
        createdAt,
        updatedAt: createdAt
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          retryLimit: 1,
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(item?.status).toBe("completed");
      expect(saw416).toBe(true);
      expect(fullRestarted).toBe(true);
      expect(fs.statSync(existingTargetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("counts retries and resets stale 100% progress on persistent HTTP 416", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const staleBinary = Buffer.alloc(64 * 1024, 9);
    const pkgDir = path.join(root, "downloads", "range-416-fail");
    fs.mkdirSync(pkgDir, { recursive: true });
    const existingTargetPath = path.join(pkgDir, "broken.part3.rar");
    fs.writeFileSync(existingTargetPath, staleBinary);
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/range-416-fail") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      directCalls += 1;
      res.statusCode = 416;
      res.setHeader("Content-Range", "bytes */32768");
      res.end("");
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/range-416-fail`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "broken.part3.rar",
            filesize: 32768
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const packageId = "range-416-fail-pkg";
      const itemId = "range-416-fail-item";
      const createdAt = Date.now() - 10_000;

      session.packageOrder = [packageId];
      session.packages[packageId] = {
        id: packageId,
        name: "range-416-fail",
        outputDir: pkgDir,
        extractDir: path.join(root, "extract", "range-416-fail"),
        status: "queued",
        itemIds: [itemId],
        cancelled: false,
        enabled: true,
        createdAt,
        updatedAt: createdAt
      };
      session.items[itemId] = {
        id: itemId,
        packageId,
        url: "https://dummy/range-416-fail",
        provider: null,
        status: "queued",
        retries: 0,
        speedBps: 0,
        downloadedBytes: staleBinary.length,
        totalBytes: staleBinary.length,
        progressPercent: 100,
        fileName: "broken.part3.rar",
        targetPath: existingTargetPath,
        resumable: true,
        attempts: 0,
        lastError: "",
        fullStatus: "Wartet",
        createdAt,
        updatedAt: createdAt
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 45000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(item?.status).toBe("failed");
      expect(item?.retries).toBeGreaterThan(0);
      expect(item?.progressPercent).toBe(0);
      expect(item?.downloadedBytes).toBe(0);
      expect(item?.lastError).toContain("416");
      expect(directCalls).toBeGreaterThanOrEqual(3);
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 70000);

  it("retries non-retriable HTTP statuses and eventually succeeds", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(96 * 1024, 5);
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/status-retry") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      directCalls += 1;
      if (directCalls <= 2) {
        res.statusCode = 403;
        res.end("forbidden");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/status-retry`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "status-retry.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "status-retry", links: ["https://dummy/status-retry"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThanOrEqual(3);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("normalizes stale running state on startup", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    session.running = true;
    session.paused = true;
    session.reconnectUntil = Date.now() + 30_000;
    session.reconnectReason = "HTTP 429";
    const packageId = "stale-pkg";
    const itemId = "stale-item";
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "stale",
      outputDir: path.join(root, "downloads", "stale"),
      extractDir: path.join(root, "extract", "stale"),
      status: "reconnect_wait",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/stale",
      provider: "realdebrid",
      status: "paused",
      retries: 0,
      speedBps: 100,
      downloadedBytes: 123,
      totalBytes: 456,
      progressPercent: 26,
      fileName: "stale.mkv",
      targetPath: path.join(root, "downloads", "stale", "stale.mkv"),
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Pausiert",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    expect(snapshot.session.running).toBe(false);
    expect(snapshot.session.paused).toBe(false);
    expect(snapshot.session.reconnectUntil).toBe(0);
    expect(snapshot.session.reconnectReason).toBe("");
    expect(snapshot.session.items[itemId]?.status).toBe("queued");
    expect(snapshot.session.items[itemId]?.speedBps).toBe(0);
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
    expect(snapshot.canStart).toBe(true);
  });

  it("requeues failed HTTP 416 items automatically on startup", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "retry-416-pkg";
    const itemId = "retry-416-item";
    const createdAt = Date.now() - 20_000;
    const outputDir = path.join(root, "downloads", "retry-416");
    const targetPath = path.join(outputDir, "broken.part03.rar");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.writeFileSync(targetPath, Buffer.alloc(12 * 1024, 1));

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "retry-416",
      outputDir,
      extractDir: path.join(root, "extract", "retry-416"),
      status: "failed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/retry-416",
      provider: "megadebrid",
      status: "failed",
      retries: 4,
      speedBps: 0,
      downloadedBytes: 12 * 1024,
      totalBytes: 8 * 1024,
      progressPercent: 100,
      fileName: "broken.part03.rar",
      targetPath,
      resumable: true,
      attempts: 3,
      lastError: "Error: HTTP 416",
      fullStatus: "Fehler: Error: HTTP 416",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    const item = snapshot.session.items[itemId];
    expect(item?.status).toBe("queued");
    expect(item?.attempts).toBe(0);
    expect(item?.downloadedBytes).toBe(0);
    expect(item?.progressPercent).toBe(0);
    expect(item?.fullStatus).toContain("Auto-Retry");
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
    expect(fs.existsSync(targetPath)).toBe(false);
  });

  it("requeues completed zero-byte archive items automatically on startup", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "zero-byte-pkg";
    const itemId = "zero-byte-item";
    const createdAt = Date.now() - 20_000;
    const outputDir = path.join(root, "downloads", "zero-byte");
    const targetPath = path.join(outputDir, "archive.part01.rar");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.writeFileSync(targetPath, Buffer.alloc(0));

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "zero-byte",
      outputDir,
      extractDir: path.join(root, "extract", "zero-byte"),
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/zero-byte",
      provider: "megadebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 0,
      totalBytes: null,
      progressPercent: 100,
      fileName: "archive.part01.rar",
      targetPath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (0 B)",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    const item = snapshot.session.items[itemId];
    expect(item?.status).toBe("queued");
    expect(item?.downloadedBytes).toBe(0);
    expect(item?.progressPercent).toBe(0);
    expect(item?.fullStatus).toContain("0B-Datei");
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
    expect(fs.existsSync(targetPath)).toBe(false);
  });

  it("requeues preallocated completed archive items automatically on startup", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "prealloc-pkg";
    const itemId = "prealloc-item";
    const createdAt = Date.now() - 20_000;
    const outputDir = path.join(root, "downloads", "prealloc");
    const targetPath = path.join(outputDir, "archive.part01.rar");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.writeFileSync(targetPath, Buffer.alloc(8192));

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "prealloc",
      outputDir,
      extractDir: path.join(root, "extract", "prealloc"),
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/prealloc",
      provider: "megadebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 1024,
      totalBytes: 8192,
      progressPercent: 100,
      fileName: "archive.part01.rar",
      targetPath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (8 KB)",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    const item = snapshot.session.items[itemId];
    expect(item?.status).toBe("queued");
    expect(item?.fullStatus).toContain("pre-alloc");
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
  });

  it("requeues completed archive parts after auto-recovery extraction failures", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "crc-pkg";
    const createdAt = Date.now() - 10_000;
    const outputDir = path.join(root, "downloads", "crc");
    const extractDir = path.join(root, "extract", "crc");
    fs.mkdirSync(outputDir, { recursive: true });

    const archiveNames = ["show.s01e01.part1.rar", "show.s01e01.part2.rar"];
    const itemIds = archiveNames.map((_, index) => `crc-item-${index}`);

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "crc",
      outputDir,
      extractDir,
      status: "extracting",
      itemIds,
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };

    for (const [index, archiveName] of archiveNames.entries()) {
      const targetPath = path.join(outputDir, archiveName);
      fs.writeFileSync(targetPath, Buffer.from(`part-${index}`));
      session.items[itemIds[index]!] = {
        id: itemIds[index]!,
        packageId,
        url: `https://dummy/${archiveName}`,
        provider: "realdebrid",
        status: "completed",
        retries: 0,
        speedBps: 0,
        downloadedBytes: 4096,
        totalBytes: 4096,
        progressPercent: 100,
        fileName: archiveName,
        targetPath,
        resumable: true,
        attempts: 1,
        lastError: "",
        fullStatus: "Entpacken - Ausstehend",
        createdAt,
        updatedAt: createdAt
      };
    }

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const changed = (manager as any).autoRecoverArchiveCrcFailure(
      session.packages[packageId],
      itemIds.map((itemId) => session.items[itemId]!),
      {
        archiveName: "show.s01e01.part1.rar",
        errorText: "Checksum error in the encrypted file",
        category: "crc_error",
        suggestRedownload: true,
        jvmFailureReason: "Can not open the file as archive"
      },
      "hybrid"
    );

    expect(changed).toBe(2);
    for (const itemId of itemIds) {
      const item = session.items[itemId]!;
      expect(item.status).toBe("queued");
      expect(item.targetPath).toBe("");
      expect(item.downloadedBytes).toBe(0);
      expect(item.attempts).toBe(0);
      expect(item.fullStatus).toContain("Auto-Recovery");
    }
    expect(fs.existsSync(path.join(outputDir, archiveNames[0]!))).toBe(false);
    expect(fs.existsSync(path.join(outputDir, archiveNames[1]!))).toBe(false);
    expect(session.packages[packageId]?.status).toBe("queued");
  });

  it("requeues archive parts on CRC error when file has invalid archive signature (corrupt content)", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "crc-corrupt-sig-pkg";
    const createdAt = Date.now() - 10_000;
    const outputDir = path.join(root, "downloads", "crc-corrupt-sig");
    const extractDir = path.join(root, "extract", "crc-corrupt-sig");
    fs.mkdirSync(outputDir, { recursive: true });

    const archiveNames = ["show.s01e01.part1.rar", "show.s01e01.part2.rar"];
    const itemIds = archiveNames.map((_, index) => `crc-corrupt-sig-item-${index}`);
    const archiveSize = 64 * 1024;

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "crc-corrupt-sig",
      outputDir,
      extractDir,
      status: "extracting",
      itemIds,
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };

    for (const [index, archiveName] of archiveNames.entries()) {
      const targetPath = path.join(outputDir, archiveName);
      // Write garbage content (no valid archive signature) — simulates corrupt download
      fs.writeFileSync(targetPath, Buffer.alloc(archiveSize, 0xAA));
      session.items[itemIds[index]!] = {
        id: itemIds[index]!,
        packageId,
        url: `https://dummy/${archiveName}`,
        provider: "realdebrid",
        status: "completed",
        retries: 0,
        speedBps: 0,
        downloadedBytes: archiveSize,
        totalBytes: archiveSize,
        progressPercent: 100,
        fileName: archiveName,
        targetPath,
        resumable: true,
        attempts: 1,
        lastError: "",
        fullStatus: "Entpacken - Ausstehend",
        createdAt,
        updatedAt: createdAt
      };
    }

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const changed = (manager as any).autoRecoverArchiveCrcFailure(
      session.packages[packageId],
      itemIds.map((itemId) => session.items[itemId]!),
      {
        archiveName: "show.s01e01.part1.rar",
        errorText: "Checksum error in the encrypted file",
        category: "crc_error",
        suggestRedownload: true,
        jvmFailureReason: "Can not open the file as archive"
      },
      "hybrid"
    );

    // Invalid archive signature = genuine corruption → force re-download
    expect(changed).toBe(2);
    for (const itemId of itemIds) {
      const item = session.items[itemId]!;
      expect(item.status).toBe("queued");
      expect(item.targetPath).toBe("");
      expect(item.downloadedBytes).toBe(0);
      expect(item.fullStatus).toContain("Auto-Recovery");
    }
    expect(fs.existsSync(path.join(outputDir, archiveNames[0]!))).toBe(false);
    expect(fs.existsSync(path.join(outputDir, archiveNames[1]!))).toBe(false);
  });

  it("does not requeue archive parts on CRC error when file has valid RAR signature (wrong password)", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "crc-valid-sig-pkg";
    const createdAt = Date.now() - 10_000;
    const outputDir = path.join(root, "downloads", "crc-valid-sig");
    const extractDir = path.join(root, "extract", "crc-valid-sig");
    fs.mkdirSync(outputDir, { recursive: true });

    const archiveNames = ["show.s01e01.part1.rar", "show.s01e01.part2.rar"];
    const itemIds = archiveNames.map((_, index) => `crc-valid-sig-item-${index}`);
    const archiveSize = 64 * 1024;

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "crc-valid-sig",
      outputDir,
      extractDir,
      status: "extracting",
      itemIds,
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };

    for (const [index, archiveName] of archiveNames.entries()) {
      const targetPath = path.join(outputDir, archiveName);
      // Write file with valid RAR5 signature — simulates wrong password, not corruption
      const content = Buffer.alloc(archiveSize, 0);
      Buffer.from([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]).copy(content);
      fs.writeFileSync(targetPath, content);
      session.items[itemIds[index]!] = {
        id: itemIds[index]!,
        packageId,
        url: `https://dummy/${archiveName}`,
        provider: "realdebrid",
        status: "completed",
        retries: 0,
        speedBps: 0,
        downloadedBytes: archiveSize,
        totalBytes: archiveSize,
        progressPercent: 100,
        fileName: archiveName,
        targetPath,
        resumable: true,
        attempts: 1,
        lastError: "",
        fullStatus: "Entpacken - Ausstehend",
        createdAt,
        updatedAt: createdAt
      };
    }

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const changed = (manager as any).autoRecoverArchiveCrcFailure(
      session.packages[packageId],
      itemIds.map((itemId) => session.items[itemId]!),
      {
        archiveName: "show.s01e01.part1.rar",
        errorText: "Checksum error in the encrypted file",
        category: "crc_error",
        suggestRedownload: true,
        jvmFailureReason: "Can not open the file as archive"
      },
      "hybrid"
    );

    // Valid RAR signature = file is structurally intact → wrong password, don't re-download
    expect(changed).toBe(0);
    for (const itemId of itemIds) {
      const item = session.items[itemId]!;
      expect(item.status).toBe("completed");
      expect(item.targetPath).toContain(".rar");
      expect(item.downloadedBytes).toBe(archiveSize);
    }
  });

  it("does not treat rev files as ready archive parts during disk fallback", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "disk-fallback-rev-pkg";
    const itemIds = ["disk-fallback-rev-1", "disk-fallback-rev-2"];
    const createdAt = Date.now() - 10_000;
    const outputDir = path.join(root, "downloads", "disk-fallback-rev");
    const extractDir = path.join(root, "extract", "disk-fallback-rev");
    const part1Path = path.join(outputDir, "show.s01e01.part1.rar");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.writeFileSync(part1Path, Buffer.alloc(64 * 1024, 1));
    fs.writeFileSync(path.join(outputDir, "show.s01e01.rev"), Buffer.alloc(32 * 1024, 2));

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "disk-fallback-rev",
      outputDir,
      extractDir,
      status: "downloading",
      itemIds,
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemIds[0]] = {
      id: itemIds[0],
      packageId,
      url: "https://dummy/show.s01e01.part1.rar",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 64 * 1024,
      totalBytes: 64 * 1024,
      progressPercent: 100,
      fileName: "show.s01e01.part1.rar",
      targetPath: part1Path,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpacken - Ausstehend",
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemIds[1]] = {
      id: itemIds[1],
      packageId,
      url: "https://dummy/show.s01e01.part2.rar",
      provider: "realdebrid",
      status: "queued",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 0,
      totalBytes: 64 * 1024,
      progressPercent: 0,
      fileName: "show.s01e01.part2.rar",
      targetPath: "",
      resumable: true,
      attempts: 0,
      lastError: "",
      fullStatus: "Wartet",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const ready = await (manager as any).findReadyArchiveSets(session.packages[packageId]);
    expect(Array.from(ready)).toHaveLength(0);
  });

  it("allows disk fallback when queued archive parts are fully present on disk", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "disk-fallback-ready-pkg";
    const itemIds = ["disk-fallback-ready-1", "disk-fallback-ready-2"];
    const createdAt = Date.now() - 10_000;
    const outputDir = path.join(root, "downloads", "disk-fallback-ready");
    const extractDir = path.join(root, "extract", "disk-fallback-ready");
    const part1Path = path.join(outputDir, "show.s01e01.part1.rar");
    const part2Path = path.join(outputDir, "show.s01e01.part2.rar");
    const archiveSize = 64 * 1024;
    fs.mkdirSync(outputDir, { recursive: true });
    fs.writeFileSync(part1Path, Buffer.alloc(archiveSize, 1));
    fs.writeFileSync(part2Path, Buffer.alloc(archiveSize, 2));

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "disk-fallback-ready",
      outputDir,
      extractDir,
      status: "downloading",
      itemIds,
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemIds[0]] = {
      id: itemIds[0],
      packageId,
      url: "https://dummy/show.s01e01.part1.rar",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: archiveSize,
      totalBytes: archiveSize,
      progressPercent: 100,
      fileName: "show.s01e01.part1.rar",
      targetPath: part1Path,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpacken - Ausstehend",
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemIds[1]] = {
      id: itemIds[1],
      packageId,
      url: "https://dummy/show.s01e01.part2.rar",
      provider: "realdebrid",
      status: "queued",
      retries: 0,
      speedBps: 0,
      downloadedBytes: archiveSize,
      totalBytes: archiveSize,
      progressPercent: 100,
      fileName: "show.s01e01.part2.rar",
      targetPath: "",
      resumable: true,
      attempts: 0,
      lastError: "",
      fullStatus: "Wartet",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const ready = await (manager as any).findReadyArchiveSets(session.packages[packageId]);
    expect(Array.from(ready)).toEqual([part1Path.toLowerCase()]);
  });

  it("skips unchanged hybrid archives after a previous extraction failure", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const {
      session,
      packageId,
      itemId,
      outputDir,
      extractDir
    } = createCompletedArchiveSession(root, "hybrid-failure-skip", "episode.mkv");
    const item = session.items[itemId]!;
    const archiveKey = item.targetPath.toLowerCase();
    item.fullStatus = "Entpacken - Error";
    session.packages[packageId]!.status = "queued";

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        hybridExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const pkg = (manager as any).session.packages[packageId];
    const items = [((manager as any).session.items[itemId])];
    const marker = (manager as any).buildHybridArchiveRetryMarker(pkg, items, archiveKey);
    (manager as any).hybridFailedArchives.set(packageId, new Map([
      [archiveKey, { marker, lastError: "Checksum error in the encrypted file", updatedAt: Date.now() }]
    ]));

    const extracted = await (manager as any).runHybridExtraction(packageId, pkg, items);

    expect(extracted).toBe(0);
    expect(fs.existsSync(path.join(extractDir, "episode.mkv"))).toBe(false);
    expect(((manager as any).session.items[itemId]).fullStatus).toBe("Entpacken - Error");
    expect(((manager as any).session.packages[packageId]).status).not.toBe("extracting");
    expect(fs.existsSync(path.join(outputDir, "episode.zip"))).toBe(true);
  });

  it("does not auto-reschedule extraction for completed items already marked as extract error", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const {
      session,
      packageId,
      itemId
    } = createCompletedArchiveSession(root, "hybrid-error-hold", "episode.mkv");
    session.items[itemId]!.fullStatus = "Entpacken - Error";
    session.packages[packageId]!.status = "queued";

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        hybridExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    (manager as any).triggerPendingExtractions();

    expect((manager as any).packagePostProcessTasks.has(packageId)).toBe(false);
    expect((manager as any).session.items[itemId].fullStatus).toBe("Entpacken - Error");
  });

  it("does not auto-reschedule extraction for completed items already marked as entpack-fehler", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const {
      session,
      packageId,
      itemId
    } = createCompletedArchiveSession(root, "hybrid-entpack-fehler-hold", "episode.mkv");
    session.items[itemId]!.fullStatus = "Entpack-Fehler: Checksum error in encrypted file";
    session.packages[packageId]!.status = "queued";

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        hybridExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    (manager as any).triggerPendingExtractions();

    expect((manager as any).packagePostProcessTasks.has(packageId)).toBe(false);
    expect((manager as any).session.items[itemId].fullStatus).toBe("Entpack-Fehler: Checksum error in encrypted file");
  });

  it("detects start conflicts when extract output already exists", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageId = "conflict-pkg";
    const itemId = "conflict-item";
    const now = Date.now() - 5000;
    const outputDir = path.join(root, "downloads", "conflict");
    const extractDir = path.join(root, "extract", "conflict");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.mkdirSync(extractDir, { recursive: true });
    fs.writeFileSync(path.join(extractDir, "existing.mkv"), "x", "utf8");

    const session = emptySession();
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "conflict",
      outputDir,
      extractDir,
      status: "queued",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/conflict",
      provider: null,
      status: "queued",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 0,
      totalBytes: null,
      progressPercent: 0,
      fileName: "conflict.part01.rar",
      targetPath: path.join(outputDir, "conflict.part01.rar"),
      resumable: true,
      attempts: 0,
      lastError: "",
      fullStatus: "Wartet",
      createdAt: now,
      updatedAt: now
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const conflicts = await manager.getStartConflicts();
    expect(conflicts.length).toBe(1);
    expect(conflicts[0]?.packageId).toBe(packageId);
  });

  it("resolves start conflict by skipping package", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageId = "skip-pkg";
    const itemId = "skip-item";
    const now = Date.now() - 5000;
    const outputDir = path.join(root, "downloads", "skip");
    const extractDir = path.join(root, "extract", "skip");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.mkdirSync(extractDir, { recursive: true });
    fs.writeFileSync(path.join(extractDir, "existing.mkv"), "x", "utf8");

    const session = emptySession();
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "skip",
      outputDir,
      extractDir,
      status: "queued",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/skip",
      provider: null,
      status: "queued",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 0,
      totalBytes: null,
      progressPercent: 0,
      fileName: "skip.part01.rar",
      targetPath: path.join(outputDir, "skip.part01.rar"),
      resumable: true,
      attempts: 0,
      lastError: "",
      fullStatus: "Wartet",
      createdAt: now,
      updatedAt: now
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const result = await manager.resolveStartConflict(packageId, "skip");
    expect(result.skipped).toBe(true);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]).toBeDefined();
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
    expect(snapshot.session.items[itemId]).toBeDefined();
    expect(snapshot.session.items[itemId]?.status).toBe("queued");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Wartet");
  });

  it("keeps already completed items when skipping start conflict", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageId = "skip-partial-pkg";
    const completedItemId = "skip-partial-completed";
    const pendingItemId = "skip-partial-pending";
    const now = Date.now() - 5000;
    const outputDir = path.join(root, "downloads", "skip-partial");
    const extractDir = path.join(root, "extract", "skip-partial");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.mkdirSync(extractDir, { recursive: true });
    fs.writeFileSync(path.join(extractDir, "existing.mkv"), "x", "utf8");
    const completedTarget = path.join(outputDir, "skip-partial.part01.rar");
    fs.writeFileSync(completedTarget, "part", "utf8");

    const session = emptySession();
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "skip-partial",
      outputDir,
      extractDir,
      status: "queued",
      itemIds: [completedItemId, pendingItemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[completedItemId] = {
      id: completedItemId,
      packageId,
      url: "https://dummy/skip-partial/completed",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: "skip-partial.part01.rar",
      targetPath: completedTarget,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpackt",
      createdAt: now,
      updatedAt: now
    };
    session.items[pendingItemId] = {
      id: pendingItemId,
      packageId,
      url: "https://dummy/skip-partial/pending",
      provider: null,
      status: "queued",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 0,
      totalBytes: null,
      progressPercent: 0,
      fileName: "skip-partial.part02.rar",
      targetPath: path.join(outputDir, "skip-partial.part02.rar"),
      resumable: true,
      attempts: 0,
      lastError: "",
      fullStatus: "Wartet",
      createdAt: now,
      updatedAt: now
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const result = await manager.resolveStartConflict(packageId, "skip");
    expect(result.skipped).toBe(true);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]).toBeDefined();
    expect(snapshot.session.items[completedItemId]?.status).toBe("completed");
    expect(snapshot.session.items[completedItemId]?.fullStatus).toBe("Entpackt");
    expect(snapshot.session.items[pendingItemId]?.status).toBe("queued");
    expect(snapshot.session.items[pendingItemId]?.fullStatus).toBe("Wartet");
  });

  it("resolves start conflict by overwriting and resetting queued package", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageId = "overwrite-pkg";
    const itemId = "overwrite-item";
    const now = Date.now() - 5000;
    const outputDir = path.join(root, "downloads", "overwrite");
    const extractDir = path.join(root, "extract", "overwrite");
    fs.mkdirSync(outputDir, { recursive: true });
    fs.mkdirSync(extractDir, { recursive: true });
    fs.writeFileSync(path.join(outputDir, "overwrite.part01.rar"), "part", "utf8");
    fs.writeFileSync(path.join(extractDir, "existing.mkv"), "x", "utf8");

    const session = emptySession();
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "overwrite",
      outputDir,
      extractDir,
      status: "queued",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/overwrite",
      provider: null,
      status: "queued",
      retries: 1,
      speedBps: 0,
      downloadedBytes: 42,
      totalBytes: 100,
      progressPercent: 42,
      fileName: "overwrite.part01.rar",
      targetPath: path.join(outputDir, "overwrite.part01.rar"),
      resumable: true,
      attempts: 3,
      lastError: "x",
      fullStatus: "Wartet",
      createdAt: now,
      updatedAt: now
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const result = await manager.resolveStartConflict(packageId, "overwrite");
    expect(result.overwritten).toBe(true);
    const snapshot = manager.getSnapshot();
    const item = snapshot.session.items[itemId];
    expect(item?.status).toBe("queued");
    expect(item?.downloadedBytes).toBe(0);
    expect(item?.progressPercent).toBe(0);
    expect(item?.attempts).toBe(0);
    expect(item?.lastError).toBe("");
    expect(item?.fullStatus).toBe("Wartet");
    expect(fs.existsSync(outputDir)).toBe(false);
    expect(fs.existsSync(extractDir)).toBe(false);
  });

  it("requeues legacy 'Gestoppt' items on startup", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "stopped-pkg";
    const itemId = "stopped-item";
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "stopped",
      outputDir: path.join(root, "downloads", "stopped"),
      extractDir: path.join(root, "extract", "stopped"),
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/stopped",
      provider: "megadebrid",
      status: "cancelled",
      retries: 1,
      speedBps: 0,
      downloadedBytes: 512,
      totalBytes: 2048,
      progressPercent: 25,
      fileName: "resume.part01.rar",
      targetPath: path.join(root, "downloads", "stopped", "resume.part01.rar"),
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Gestoppt",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    expect(snapshot.session.items[itemId]?.status).toBe("queued");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Wartet");
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
  });

  it("cleans leftover split archives on startup for already extracted packages", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageDir = path.join(root, "downloads", "legacy");
    fs.mkdirSync(packageDir, { recursive: true });
    const part1 = path.join(packageDir, "legacy.release.part01.rar");
    const part2 = path.join(packageDir, "legacy.release.part02.rar");
    const part3 = path.join(packageDir, "legacy.release.part03.rar");
    const keep = path.join(packageDir, "keep.txt");
    fs.writeFileSync(part2, "part2", "utf8");
    fs.writeFileSync(part3, "part3", "utf8");
    fs.writeFileSync(keep, "keep", "utf8");

    const session = emptySession();
    const packageId = "legacy-pkg";
    const itemId = "legacy-item";
    const createdAt = Date.now() - 20_000;

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "legacy",
      outputDir: packageDir,
      extractDir: path.join(root, "extract", "legacy"),
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/legacy",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: path.basename(part1),
      targetPath: part1,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpackt",
      createdAt,
      updatedAt: createdAt
    };

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        cleanupMode: "delete"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => !fs.existsSync(part2) && !fs.existsSync(part3), 5000);
    expect(fs.existsSync(keep)).toBe(true);
  });

  it("cleans legacy leftovers when package is extracted but marker is old", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageDir = path.join(root, "downloads", "legacy-old");
    const extractDir = path.join(root, "extract", "legacy-old");
    fs.mkdirSync(packageDir, { recursive: true });
    fs.mkdirSync(extractDir, { recursive: true });

    const part1 = path.join(packageDir, "legacy.old.part01.rar");
    const part2 = path.join(packageDir, "legacy.old.part02.rar");
    const part3 = path.join(packageDir, "legacy.old.part03.rar");
    const keep = path.join(packageDir, "keep.nfo");
    fs.writeFileSync(part1, "part1", "utf8");
    fs.writeFileSync(part2, "part2", "utf8");
    fs.writeFileSync(part3, "part3", "utf8");
    fs.writeFileSync(keep, "keep", "utf8");
    fs.writeFileSync(path.join(extractDir, "episode.mkv"), "video", "utf8");

    const session = emptySession();
    const packageId = "legacy-old-pkg";
    const itemId = "legacy-old-item";
    const createdAt = Date.now() - 20_000;

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "legacy-old",
      outputDir: packageDir,
      extractDir,
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/legacy-old",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: path.basename(part1),
      targetPath: part1,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (123 MB)",
      createdAt,
      updatedAt: createdAt
    };

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false,
        cleanupMode: "delete"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => !fs.existsSync(part1) && !fs.existsSync(part2) && !fs.existsSync(part3), 5000);
    expect(fs.existsSync(keep)).toBe(true);
    expect(fs.existsSync(path.join(extractDir, "episode.mkv"))).toBe(true);
  });

  it("removes empty download package directory after startup cleanup backfill", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageDir = path.join(root, "downloads", "legacy-empty");
    fs.mkdirSync(packageDir, { recursive: true });
    const part1 = path.join(packageDir, "legacy.empty.part01.rar");
    const part2 = path.join(packageDir, "legacy.empty.part02.rar");
    fs.writeFileSync(part1, "part1", "utf8");
    fs.writeFileSync(part2, "part2", "utf8");

    const session = emptySession();
    const packageId = "legacy-empty-pkg";
    const itemId = "legacy-empty-item";
    const createdAt = Date.now() - 20_000;

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "legacy-empty",
      outputDir: packageDir,
      extractDir: path.join(root, "extract", "legacy-empty"),
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/legacy-empty",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: path.basename(part1),
      targetPath: part1,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpackt",
      createdAt,
      updatedAt: createdAt
    };

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false,
        cleanupMode: "delete"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => !fs.existsSync(packageDir), 5000);
  });

  it("does not over-clean packages that share one extract directory", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const sharedExtractDir = path.join(root, "extract", "shared");
    fs.mkdirSync(sharedExtractDir, { recursive: true });
    fs.writeFileSync(path.join(sharedExtractDir, "already-extracted.mkv"), "ok", "utf8");

    const pkg1Dir = path.join(root, "downloads", "pkg1");
    const pkg2Dir = path.join(root, "downloads", "pkg2");
    fs.mkdirSync(pkg1Dir, { recursive: true });
    fs.mkdirSync(pkg2Dir, { recursive: true });

    const pkg1Part1 = path.join(pkg1Dir, "show.one.part01.rar");
    const pkg1Part2 = path.join(pkg1Dir, "show.one.part02.rar");
    const pkg2Part1 = path.join(pkg2Dir, "show.two.part01.rar");
    const pkg2Part2 = path.join(pkg2Dir, "show.two.part02.rar");
    fs.writeFileSync(pkg1Part1, "a1", "utf8");
    fs.writeFileSync(pkg1Part2, "a2", "utf8");
    fs.writeFileSync(pkg2Part1, "b1", "utf8");
    fs.writeFileSync(pkg2Part2, "b2", "utf8");

    const session = emptySession();
    const createdAt = Date.now() - 30_000;

    session.packageOrder = ["pkg1", "pkg2"];
    session.packages.pkg1 = {
      id: "pkg1",
      name: "pkg1",
      outputDir: pkg1Dir,
      extractDir: sharedExtractDir,
      status: "completed",
      itemIds: ["pkg1-item"],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.packages.pkg2 = {
      id: "pkg2",
      name: "pkg2",
      outputDir: pkg2Dir,
      extractDir: sharedExtractDir,
      status: "completed",
      itemIds: ["pkg2-item"],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };

    session.items["pkg1-item"] = {
      id: "pkg1-item",
      packageId: "pkg1",
      url: "https://dummy/pkg1",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 1,
      totalBytes: 1,
      progressPercent: 100,
      fileName: path.basename(pkg1Part1),
      targetPath: pkg1Part1,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpackt",
      createdAt,
      updatedAt: createdAt
    };
    session.items["pkg2-item"] = {
      id: "pkg2-item",
      packageId: "pkg2",
      url: "https://dummy/pkg2",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 1,
      totalBytes: 1,
      progressPercent: 100,
      fileName: path.basename(pkg2Part1),
      targetPath: pkg2Part1,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (100 MB)",
      createdAt,
      updatedAt: createdAt
    };

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false,
        cleanupMode: "delete"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => !fs.existsSync(pkg1Part1) && !fs.existsSync(pkg1Part2), 5000);
    expect(fs.existsSync(pkg2Part1)).toBe(true);
    expect(fs.existsSync(pkg2Part2)).toBe(true);
  });

  it("resets run counters and reconnect state on start", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    session.runStartedAt = Date.now() - 3600 * 1000;
    session.totalDownloadedBytes = 9_999_999;
    session.reconnectUntil = Date.now() + 120000;
    session.reconnectReason = "HTTP 503";

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await manager.start();
    await waitFor(() => !manager.getSnapshot().session.running, 5000);

    const snapshot = manager.getSnapshot();
    const summary = manager.getSummary();
    expect(snapshot.session.totalDownloadedBytes).toBe(0);
    expect(snapshot.session.reconnectUntil).toBe(0);
    expect(snapshot.session.reconnectReason).toBe("");
    expect(summary).toBeNull();
  });

  it("shows zero total when queue is empty despite stale persisted bytes", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    session.totalDownloadedBytes = 19.99 * 1024 * 1024 * 1024;
    session.runStartedAt = Date.now() - 5 * 60 * 1000;

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const snapshot = manager.getSnapshot();
    expect(snapshot.stats.totalPackages).toBe(0);
    expect(snapshot.stats.totalFiles).toBe(0);
    expect(snapshot.stats.totalDownloaded).toBe(0);
    expect(snapshot.session.totalDownloadedBytes).toBe(0);
    expect(snapshot.session.runStartedAt).toBe(0);
  });

  it("clearAll resets total bytes and stats", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "pkg-clear";
    const itemId = "item-clear";
    const now = Date.now() - 1000;
    const outputDir = path.join(root, "downloads", "pkg-clear");
    const extractDir = path.join(root, "extract", "pkg-clear");
    const targetPath = path.join(outputDir, "episode.mkv");

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "pkg-clear",
      outputDir,
      extractDir,
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/item-clear",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 1024,
      totalBytes: 1024,
      progressPercent: 100,
      fileName: "episode.mkv",
      targetPath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (1 KB)",
      createdAt: now,
      updatedAt: now
    };
    session.totalDownloadedBytes = 1024;
    session.runStartedAt = now;

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    manager.clearAll();
    const snapshot = manager.getSnapshot();
    expect(snapshot.stats.totalPackages).toBe(0);
    expect(snapshot.stats.totalFiles).toBe(0);
    expect(snapshot.stats.totalDownloaded).toBe(0);
    expect(snapshot.session.totalDownloadedBytes).toBe(0);
    expect(snapshot.session.runStartedAt).toBe(0);
  });

  it("keeps cumulative session totals when completed items are removed from the queue", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "pkg-complete-remove";
    const itemId = "item-complete-remove";
    const now = Date.now() - 1000;
    const outputDir = path.join(root, "downloads", "pkg-complete-remove");
    const extractDir = path.join(root, "extract", "pkg-complete-remove");
    const targetPath = path.join(outputDir, "episode.mkv");

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "pkg-complete-remove",
      outputDir,
      extractDir,
      status: "completed",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt: now,
      updatedAt: now
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/item-complete-remove",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 3 * 1024,
      totalBytes: 3 * 1024,
      progressPercent: 100,
      fileName: "episode.mkv",
      targetPath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (3 KB)",
      createdAt: now,
      updatedAt: now
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      session: { totalDownloadedBytes: number };
      sessionDownloadedBytes: number;
      sessionCompletedFiles: number;
      itemContributedBytes: Map<string, number>;
      removePackageFromSession: (packageId: string, itemIds: string[], reason?: "completed" | "deleted") => void;
    };

    internal.session.totalDownloadedBytes = 16 * 1024 * 1024 * 1024;
    internal.sessionDownloadedBytes = 16 * 1024 * 1024 * 1024;
    internal.sessionCompletedFiles = 1;
    internal.itemContributedBytes.set(itemId, 3 * 1024 * 1024 * 1024);

    internal.removePackageFromSession(packageId, [itemId], "completed");

    const snapshot = manager.getSnapshot();
    expect(snapshot.stats.totalPackages).toBe(0);
    expect(snapshot.stats.totalDownloaded).toBe(16 * 1024 * 1024 * 1024);
    expect(snapshot.stats.totalFilesSession).toBe(1);
    expect(snapshot.session.totalDownloadedBytes).toBe(16 * 1024 * 1024 * 1024);
  });

  it("does not start a run when queue is empty", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    manager.start();
    await new Promise((resolve) => setTimeout(resolve, 80));

    const snapshot = manager.getSnapshot();
    expect(snapshot.session.running).toBe(false);
    expect(manager.getSummary()).toBeNull();
  });

  it("calculates ETA from current run items only", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(128 * 1024, 4);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/slow-eta") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      const half = Math.floor(binary.length / 2);
      res.write(binary.subarray(0, half));
      setTimeout(() => {
        res.end(binary.subarray(half));
      }, 700);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/slow-eta`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "new-episode.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const session = emptySession();
      const oldPkgId = "old-pkg";
      const oldItemId = "old-item";
      const oldNow = Date.now() - 5000;
      session.packageOrder = [oldPkgId];
      session.packages[oldPkgId] = {
        id: oldPkgId,
        name: "old",
        outputDir: path.join(root, "downloads", "old"),
        extractDir: path.join(root, "extract", "old"),
        status: "completed",
        itemIds: [oldItemId],
        cancelled: false,
        enabled: true,
        createdAt: oldNow,
        updatedAt: oldNow
      };
      session.items[oldItemId] = {
        id: oldItemId,
        packageId: oldPkgId,
        url: "https://dummy/old",
        provider: "realdebrid",
        status: "completed",
        retries: 0,
        speedBps: 0,
        downloadedBytes: 100,
        totalBytes: 100,
        progressPercent: 100,
        fileName: "old.bin",
        targetPath: path.join(root, "downloads", "old", "old.bin"),
        resumable: true,
        attempts: 1,
        lastError: "",
        fullStatus: "done",
        createdAt: oldNow,
        updatedAt: oldNow
      };

      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false
        },
        session,
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "new", links: ["https://dummy/new"] }]);
      await manager.start();
      await new Promise((resolve) => setTimeout(resolve, 120));

      const runningSnapshot = manager.getSnapshot();
      expect(runningSnapshot.session.running).toBe(true);
      expect(runningSnapshot.etaText).toBe("ETA: --");

      await waitFor(() => !manager.getSnapshot().session.running, 25000);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("performs one fresh retry after fetch failed during unrestrict", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(96 * 1024, 12);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/fresh-retry") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/fresh-retry`;
    let unrestrictCalls = 0;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        unrestrictCalls += 1;
        if (unrestrictCalls <= 3) {
          throw new TypeError("fetch failed");
        }
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "fresh-retry.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "fresh-retry", links: ["https://dummy/fresh"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(unrestrictCalls).toBeGreaterThan(3);
      expect(item?.status).toBe("completed");
      expect(item?.lastError || "").toBe("");
      expect(fs.existsSync(item.targetPath)).toBe(true);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("retries suspicious mini files under 100 KB until the full file arrives", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(2 * 1024 * 1024, 21);
    let directCalls = 0;

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/mini-retry") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }

      directCalls += 1;
      if (directCalls === 1) {
        const tiny = Buffer.from("<html><body>temporary error</body></html>", "utf8");
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(tiny.length));
        res.end(tiny);
        return;
      }

      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/mini-retry`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "mini-retry.part01.rar",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "mini-retry", links: ["https://dummy/mini-retry"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(directCalls).toBeGreaterThan(1);
      expect(fs.existsSync(item.targetPath)).toBe(true);
      expect(fs.statSync(item.targetPath).size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("limits AllDebrid rapidgator starts to one active task by default", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(2 * 1024 * 1024, 6);
    let unlockInFlight = 0;
    let maxUnlockInFlight = 0;

    const server = http.createServer((req, res) => {
      const route = req.url || "";
      if (route !== "/rg-1" && route !== "/rg-2") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      setTimeout(() => {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.end(binary);
      }, 1500);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }

    const link1 = "https://rapidgator.net/file/12345678901234567890123456789012/file1.mkv.html";
    const link2 = "https://rapidgator.net/file/abcdefabcdefabcdefabcdefabcdef12/file2.mkv.html";
    const directUrl1 = `http://127.0.0.1:${address.port}/rg-1`;
    const directUrl2 = `http://127.0.0.1:${address.port}/rg-2`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      const method = String(
        init?.method
          || (typeof input === "string" || input instanceof URL ? "" : input.method || "")
      ).toUpperCase();

      if (url.includes("/user/hosts")) {
        return new Response(
          JSON.stringify({
            status: "success",
            data: {
              hosts: {
                rapidgator: {
                  name: "Rapidgator",
                  status: true,
                  quota: 50,
                  quotaMax: 100,
                  quotaType: "traffic",
                  limitSimuDl: 1
                }
              }
            }
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }

      if (url.includes("/link/unlock")) {
        unlockInFlight += 1;
        maxUnlockInFlight = Math.max(maxUnlockInFlight, unlockInFlight);
        try {
          await new Promise((resolve) => setTimeout(resolve, 120));
          const body = init?.body;
          const bodyText = body instanceof URLSearchParams ? body.toString() : String(body || "");
          const originalLink = new URLSearchParams(bodyText).get("link") || "";
          const directUrl = originalLink === link2 ? directUrl2 : directUrl1;
          const fileName = originalLink === link2 ? "rg-2.mkv" : "rg-1.mkv";
          return new Response(
            JSON.stringify({
              status: "success",
              data: {
                link: directUrl,
                filename: fileName,
                filesize: binary.length
              }
            }),
            {
              status: 200,
              headers: { "Content-Type": "application/json" }
            }
          );
        } finally {
          unlockInFlight = Math.max(0, unlockInFlight - 1);
        }
      }

      if (url.startsWith("https://rapidgator.net/")) {
        if (method === "HEAD") {
          return new Response(null, { status: 200 });
        }
        return new Response("<html><title>Rapidgator</title></html>", {
          status: 200,
          headers: { "Content-Type": "text/html" }
        });
      }

      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          allDebridToken: "ad-token",
          providerPrimary: "alldebrid",
          providerSecondary: "none",
          providerTertiary: "none",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false,
          enableIntegrityCheck: false,
          maxParallel: 2
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "rg-all-debrid", links: [link1, link2] }]);
      await manager.start();
      await waitFor(() => {
        const items = Object.values(manager.getSnapshot().session.items);
        return items.some((item) => item.status === "downloading") && maxUnlockInFlight >= 1;
      }, 15000);
      await new Promise((resolve) => setTimeout(resolve, 400));

      const items = Object.values(manager.getSnapshot().session.items);
      expect(items).toHaveLength(2);
      expect(items.filter((item) => item.status === "downloading" || item.status === "completed")).toHaveLength(1);
      expect(items.filter((item) => item.status === "queued" || item.status === "validating" || item.status === "reconnect_wait")).toHaveLength(1);
      expect(maxUnlockInFlight).toBe(1);
      manager.stop();
      await waitFor(() => !manager.getSnapshot().session.running, 15000);
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("staggeres AllDebrid starts by 2.5 seconds per active download", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(512 * 1024, 5);

    const server = http.createServer((req, res) => {
      const route = req.url || "";
      if (route !== "/ad-1" && route !== "/ad-2" && route !== "/ad-3") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      setTimeout(() => {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(binary.length));
        res.end(binary);
      }, 1800);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }

    const link1 = "https://host-a.example/file1.bin";
    const link2 = "https://host-b.example/file2.bin";
    const link3 = "https://host-c.example/file3.bin";
    const directUrl1 = `http://127.0.0.1:${address.port}/ad-1`;
    const directUrl2 = `http://127.0.0.1:${address.port}/ad-2`;
    const directUrl3 = `http://127.0.0.1:${address.port}/ad-3`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/link/unlock")) {
        const body = init?.body;
        const bodyText = body instanceof URLSearchParams ? body.toString() : String(body || "");
        const originalLink = new URLSearchParams(bodyText).get("link") || "";
        const directUrl = originalLink === link2 ? directUrl2 : originalLink === link3 ? directUrl3 : directUrl1;
        const fileName = originalLink === link2 ? "ad-2.bin" : originalLink === link3 ? "ad-3.bin" : "ad-1.bin";
        return new Response(
          JSON.stringify({
            status: "success",
            data: {
              link: directUrl,
              filename: fileName,
              filesize: binary.length
            }
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          allDebridToken: "ad-token",
          providerPrimary: "alldebrid",
          providerSecondary: "none",
          providerTertiary: "none",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          autoReconnect: false,
          enableIntegrityCheck: false,
          maxParallel: 3
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "ad-paced", links: [link1, link2, link3] }]);
      await manager.start();

      const managerInternals = manager as unknown as { retryAfterByItem: Map<string, number> };
      await waitFor(() => managerInternals.retryAfterByItem.size >= 2, 5000);

      const now = Date.now();
      const readyTimes = [...managerInternals.retryAfterByItem.values()].sort((a, b) => a - b);
      expect(readyTimes).toHaveLength(2);
      const firstDelay = readyTimes[0] - now;
      const secondDelay = readyTimes[1] - now;
      expect(firstDelay).toBeGreaterThan(1500);
      expect(firstDelay).toBeLessThan(4500);
      expect(secondDelay).toBeGreaterThan(1500);
      expect(secondDelay).toBeLessThan(4500);
      expect(Math.abs(secondDelay - firstDelay)).toBeLessThan(1000);

      manager.stop();
      await waitFor(() => !manager.getSnapshot().session.running, 15000);
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 20000);

  it("creates extract directory only at extraction and marks items as Entpackt", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const zip = new AdmZip();
    zip.addFile("inside.txt", Buffer.from("ok"));
    const archive = zip.toBuffer();

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/archive") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      setTimeout(() => {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(archive.length));
        res.end(archive);
      }, 450);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/archive`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "sample.zip",
            filesize: archive.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          createExtractSubfolder: true,
          autoExtract: true,
          enableIntegrityCheck: false,
          cleanupMode: "none"
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "zip-pack", links: ["https://dummy/archive"] }]);
      const pkgId = manager.getSnapshot().session.packageOrder[0];
      const extractDir = manager.getSnapshot().session.packages[pkgId]?.extractDir || "";
      expect(extractDir).toBeTruthy();
      expect(fs.existsSync(extractDir)).toBe(false);

      await manager.start();
      await new Promise((resolve) => setTimeout(resolve, 140));
      expect(fs.existsSync(extractDir)).toBe(false);

      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const snapshot = manager.getSnapshot();
      const item = Object.values(snapshot.session.items)[0];
      expect(item?.status).toBe("completed");
      expect(item?.fullStatus).toBe("Entpackt - Done");
      expect(fs.existsSync(extractDir)).toBe(true);
      expect(fs.existsSync(path.join(extractDir, "inside.txt"))).toBe(true);
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("keeps accurate summary when completed items are cleaned immediately", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(128 * 1024, 3);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/file") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/file`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "episode.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          completedCleanupPolicy: "immediate"
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "cleanup", links: ["https://dummy/cleanup"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const snapshot = manager.getSnapshot();
      const summary = manager.getSummary();
      expect(Object.keys(snapshot.session.items)).toHaveLength(0);
      expect(summary).not.toBeNull();
      expect(summary?.total).toBe(1);
      expect(summary?.success).toBe(1);
      expect(summary?.failed).toBe(0);
      expect(summary?.cancelled).toBe(0);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("removes finished package when package_done cleanup policy is enabled", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const zip = new AdmZip();
    zip.addFile("episode.txt", Buffer.from("ok"));
    const archiveBinary = zip.toBuffer();

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/cleanup-package") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(archiveBinary.length));
      res.end(archiveBinary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/cleanup-package`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "cleanup-package.zip",
            filesize: archiveBinary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: true,
          enableIntegrityCheck: false,
          cleanupMode: "none",
          completedCleanupPolicy: "package_done"
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "cleanup-package", links: ["https://dummy/cleanup-package"] }]);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 30000);

      const snapshot = manager.getSnapshot();
      const summary = manager.getSummary();
      expect(snapshot.session.packageOrder).toHaveLength(0);
      expect(Object.keys(snapshot.session.items)).toHaveLength(0);
      expect(summary).not.toBeNull();
      expect(summary?.success).toBe(1);
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 35000);

  it("counts queued package cancellations in run summary", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(256 * 1024, 5);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/slow") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      const mid = Math.floor(binary.length / 2);
      res.write(binary.subarray(0, mid));
      setTimeout(() => {
        res.end(binary.subarray(mid));
      }, 600);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/slow`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "episode.mkv",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "cancel-run", links: ["https://dummy/one", "https://dummy/two"] }]);
      manager.start();
      await new Promise((resolve) => setTimeout(resolve, 120));
      const pkgId = manager.getSnapshot().session.packageOrder[0];
      manager.cancelPackage(pkgId);
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const summary = manager.getSummary();
      expect(summary).not.toBeNull();
      expect(summary?.total).toBe(2);
      expect(summary?.cancelled).toBe(2);
      expect(summary?.success).toBe(0);
      expect(summary?.failed).toBe(0);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("finishes run when remaining packages are disabled", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(96 * 1024, 8);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/enabled") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.end(binary);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/enabled`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "enabled.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([
        { name: "enabled", links: ["https://dummy/enabled"] },
        { name: "disabled", links: ["https://dummy/disabled"] }
      ]);
      const initial = manager.getSnapshot();
      const enabledPkgId = initial.session.packageOrder[0];
      const disabledPkgId = initial.session.packageOrder[1];
      const enabledItemId = initial.session.packages[enabledPkgId]?.itemIds[0] || "";
      const disabledItemId = initial.session.packages[disabledPkgId]?.itemIds[0] || "";

      manager.togglePackage(disabledPkgId);
      await manager.start();
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const snapshot = manager.getSnapshot();
      expect(snapshot.session.packages[disabledPkgId]?.enabled).toBe(false);
      expect(snapshot.session.items[enabledItemId]?.status).toBe("completed");
      expect(snapshot.session.items[disabledItemId]?.status).toBe("queued");
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("stops active package and keeps items queued", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(320 * 1024, 15);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/toggle") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      const half = Math.floor(binary.length / 2);
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.write(binary.subarray(0, half));
      setTimeout(() => {
        res.end(binary.subarray(half));
      }, 1200);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/toggle`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "toggle.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "toggle", links: ["https://dummy/toggle"] }]);
      const initial = manager.getSnapshot();
      const pkgId = initial.session.packageOrder[0];
      const itemId = initial.session.packages[pkgId]?.itemIds[0] || "";

      manager.start();
      await waitFor(() => {
        const item = manager.getSnapshot().session.items[itemId];
        return item?.status === "downloading";
      }, 12000);

      manager.togglePackage(pkgId);
      await waitFor(() => !manager.getSnapshot().session.running, 25000);

      const snapshot = manager.getSnapshot();
      const item = snapshot.session.items[itemId];
      expect(snapshot.session.packages[pkgId]?.enabled).toBe(false);
      expect(item?.status).toBe("queued");
      expect(item?.fullStatus).toBe("Paket gestoppt");
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("shows stable ETA while paused", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(320 * 1024, 10);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/pause") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      const chunk = Math.floor(binary.length / 2);
      res.write(binary.subarray(0, chunk));
      setTimeout(() => {
        res.end(binary.subarray(chunk));
      }, 900);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/pause`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "pause.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "pause-case", links: ["https://dummy/pause"] }]);
      manager.start();
      await new Promise((resolve) => setTimeout(resolve, 120));
      manager.togglePause();
      const pausedSnapshot = manager.getSnapshot();
      expect(pausedSnapshot.session.paused).toBe(true);
      expect(pausedSnapshot.etaText).toBe("ETA: --");

      manager.stop();
      await waitFor(() => !manager.getSnapshot().session.running, 15000);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("handles rapid pause/resume toggles without deadlock", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(320 * 1024, 11);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/toggle-stress") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      const range = String(req.headers.range || "");
      const match = range.match(/bytes=(\d+)-/i);
      const start = match ? Number(match[1]) : 0;
      const chunk = binary.subarray(start);

      if (start > 0) {
        res.statusCode = 206;
        res.setHeader("Content-Range", `bytes ${start}-${binary.length - 1}/${binary.length}`);
      } else {
        res.statusCode = 200;
      }

      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(chunk.length));
      const split = Math.max(1, Math.floor(chunk.length / 4));
      res.write(chunk.subarray(0, split));
      setTimeout(() => {
        if (!res.writableEnded && !res.destroyed) {
          res.end(chunk.subarray(split));
        }
      }, 260);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/toggle-stress`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "stress.part01.rar",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "toggle-stress", links: ["https://dummy/toggle-stress"] }]);
      manager.start();

      await waitFor(() => Object.values(manager.getSnapshot().session.items)[0]?.status === "downloading", 12000);
      for (let i = 0; i < 6; i += 1) {
        manager.togglePause();
        await new Promise((resolve) => setTimeout(resolve, 90));
      }

      if (manager.getSnapshot().session.paused) {
        manager.togglePause();
      }

      await waitFor(() => !manager.getSnapshot().session.running, 25000);
      const item = Object.values(manager.getSnapshot().session.items)[0];
      expect(item?.status).toBe("completed");
      expect(fs.existsSync(item?.targetPath || "")).toBe(true);
      expect(fs.statSync(item?.targetPath || "").size).toBe(binary.length);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("keeps active downloads resumable on shutdown preparation", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(480 * 1024, 5);

    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/shutdown") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      res.write(binary.subarray(0, Math.floor(binary.length / 3)));
      setTimeout(() => {
        if (!res.writableEnded && !res.destroyed) {
          res.end(binary.subarray(Math.floor(binary.length / 3)));
        }
      }, 2200);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/shutdown`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "shutdown.part01.rar",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "shutdown-case", links: ["https://dummy/shutdown"] }]);
      const itemId = Object.values(manager.getSnapshot().session.items)[0]?.id || "";
      manager.start();
      await waitFor(() => manager.getSnapshot().session.items[itemId]?.status === "downloading", 12000);

      manager.prepareForShutdown();
      await waitFor(() => {
        const state = manager.getSnapshot();
        return !state.session.running && state.session.items[itemId]?.status === "queued";
      }, 8000);

      const item = manager.getSnapshot().session.items[itemId];
      expect(item?.status).toBe("queued");
      expect(item?.fullStatus).toBe("Wartet");
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("marks extracting items as resumable extraction on shutdown", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const session = emptySession();
    const packageId = "extract-stop-pkg";
    const itemId = "extract-stop-item";
    const createdAt = Date.now() - 20_000;
    const outputDir = path.join(root, "downloads", "extract-stop");
    const extractDir = path.join(root, "extract", "extract-stop");

    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "extract-stop",
      outputDir,
      extractDir,
      status: "extracting",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/extract-stop",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: "extract-stop.part01.rar",
      targetPath: path.join(outputDir, "extract-stop.part01.rar"),
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Entpacken 40%",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    manager.prepareForShutdown();
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]?.status).toBe("queued");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpacken abgebrochen (wird fortgesetzt)");
  });

  it("recovers pending extraction on startup for completed package", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const outputDir = path.join(root, "downloads", "recovery");
    const extractDir = path.join(root, "extract", "recovery");
    fs.mkdirSync(outputDir, { recursive: true });

    const zip = new AdmZip();
    zip.addFile("episode.txt", Buffer.from("ok"));
    const archivePath = path.join(outputDir, "episode.zip");
    zip.writeZip(archivePath);

    const session = emptySession();
    const packageId = "recover-pkg";
    const itemId = "recover-item";
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "recovery",
      outputDir,
      extractDir,
      status: "downloading",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/recover",
      provider: "megadebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: fs.statSync(archivePath).size,
      totalBytes: fs.statSync(archivePath).size,
      progressPercent: 100,
      fileName: "episode.zip",
      targetPath: archivePath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (100 MB)",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        createExtractSubfolder: true,
        autoExtract: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => fs.existsSync(path.join(extractDir, "episode.txt")), 25000);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]?.status).toBe("completed");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpackt - Done");
  });

  it("does not fail startup post-processing when source package dir is missing but extract output exists", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const outputDir = path.join(root, "downloads", "missing-source-ok");
    const extractDir = path.join(root, "extract", "missing-source-ok");
    fs.mkdirSync(extractDir, { recursive: true });
    fs.writeFileSync(path.join(extractDir, "episode.mkv"), "ok", "utf8");

    const session = emptySession();
    const packageId = "missing-source-ok-pkg";
    const itemId = "missing-source-ok-item";
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "missing-source-ok",
      outputDir,
      extractDir,
      status: "downloading",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/missing-source-ok",
      provider: "megadebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: "missing-source-ok.part01.rar",
      targetPath: path.join(outputDir, "missing-source-ok.part01.rar"),
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (123 B)",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => manager.getSnapshot().session.items[itemId]?.fullStatus.startsWith("Entpackt"), 12000);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]?.status).toBe("completed");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpackt - Done");
  });

  it("marks missing source package dir as extracted instead of failed", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const outputDir = path.join(root, "downloads", "missing-source-empty");
    const extractDir = path.join(root, "extract", "missing-source-empty");

    const session = emptySession();
    const packageId = "missing-source-empty-pkg";
    const itemId = "missing-source-empty-item";
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: "missing-source-empty",
      outputDir,
      extractDir,
      status: "downloading",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/missing-source-empty",
      provider: "megadebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: 123,
      totalBytes: 123,
      progressPercent: 100,
      fileName: "missing-source-empty.part01.rar",
      targetPath: path.join(outputDir, "missing-source-empty.part01.rar"),
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig (123 B)",
      createdAt,
      updatedAt: createdAt
    };

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => manager.getSnapshot().session.items[itemId]?.fullStatus.startsWith("Entpackt"), 12000);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]?.status).toBe("completed");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpackt (Quelle fehlt)");
  });

  it("does not delete stale target file when stopping during unrestrict phase", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: false,
        maxParallel: 1
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    manager.addPackages([{ name: "stop-unrestrict", links: ["https://dummy/slow-unrestrict"] }]);
    const initialSnapshot = manager.getSnapshot();
    const pkgId = initialSnapshot.session.packageOrder[0];
    const itemId = initialSnapshot.session.packages[pkgId]?.itemIds[0] || "";
    if (!itemId) {
      throw new Error("item missing");
    }

    const item = manager.getSnapshot().session.items[itemId];
    const staleTargetPath = path.join(path.dirname(item.targetPath), "existing-before-start.mkv");
    fs.mkdirSync(path.dirname(staleTargetPath), { recursive: true });
    fs.writeFileSync(staleTargetPath, "keep", "utf8");

    const mutableSession = manager.getSnapshot().session;
    if (mutableSession.items[itemId]) {
      mutableSession.items[itemId].targetPath = staleTargetPath;
      mutableSession.items[itemId].fileName = path.basename(staleTargetPath);
      mutableSession.items[itemId].downloadedBytes = 0;
      mutableSession.items[itemId].progressPercent = 0;
    }

    globalThis.fetch = (async (input: RequestInfo | URL): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        await new Promise((resolve) => setTimeout(resolve, 260));
        return new Response(
          JSON.stringify({
            download: "https://cdn.example/unused.bin",
            filename: "new-file.mkv",
            filesize: 1024
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return new Response("not-found", { status: 404 });
    }) as typeof fetch;

    manager.start();
    await new Promise((resolve) => setTimeout(resolve, 40));
    manager.stop();
    await waitFor(() => manager.getSnapshot().session.items[itemId]?.status === "cancelled", 12000);
    expect(fs.existsSync(staleTargetPath)).toBe(true);
  });

  it("counts re-enabled package items in run summary totals", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const payload = Buffer.alloc(96 * 1024, 5);
    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/slow") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      setTimeout(() => {
        res.statusCode = 200;
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", String(payload.length));
        res.end(payload);
      }, 180);
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");
    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/slow`;

    globalThis.fetch = (async (input: RequestInfo | URL): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "episode.mkv",
            filesize: payload.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input);
    }) as typeof fetch;

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract"),
          autoExtract: false,
          maxParallel: 1
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([
        { name: "pkg-a", links: ["https://dummy/a"] },
        { name: "pkg-b", links: ["https://dummy/b"] }
      ]);

      const packageIds = manager.getSnapshot().session.packageOrder;
      const packageToToggle = packageIds[0];
      manager.start();
      await new Promise((resolve) => setTimeout(resolve, 40));
      manager.togglePackage(packageToToggle);
      manager.togglePackage(packageToToggle);

      await waitFor(() => !manager.getSnapshot().session.running, 25000);
      const summary = manager.getSnapshot().summary;
      expect(summary?.total).toBe(2);
    } finally {
      server.close();
      await once(server, "close");
    }
  });

  it("auto-renames extracted 4SF scene files to folder format", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Asbest.S02.GERMAN.720p.WEB.AVC-4SF";
    const sourceFileName = "4sf-asbest.web.7p-s02e01.mkv";
    const expectedFileName = "Asbest.S02E01.GERMAN.720p.WEB.AVC-4SF.mkv";
    const { session, packageId, itemId, extractDir, originalExtractedPath } = createCompletedArchiveSession(root, packageName, sourceFileName);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const expectedPath = path.join(extractDir, expectedFileName);
    await waitFor(() => fs.existsSync(expectedPath), 12000);
    const snapshot = manager.getSnapshot();
    expect(snapshot.session.packages[packageId]?.status).toBe("completed");
    expect(snapshot.session.items[itemId]?.fullStatus).toBe("Entpackt - Done");
    expect(fs.existsSync(expectedPath)).toBe(true);
    expect(fs.existsSync(originalExtractedPath)).toBe(false);
  });

  it("adds REPACK marker from rp token and supports 4SJ folders", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Asbest.S02.GERMAN.720p.WEB.AVC-4SJ";
    const sourceFileName = "4sf-asbest.rp.web.7p-s02e01.mkv";
    const expectedFileName = "Asbest.S02E01.GERMAN.REPACK.720p.WEB.AVC-4SJ.mkv";
    const { session, itemId, extractDir, originalExtractedPath } = createCompletedArchiveSession(root, packageName, sourceFileName);

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const expectedPath = path.join(extractDir, expectedFileName);
    await waitFor(() => fs.existsSync(expectedPath), 12000);
    expect(fs.existsSync(expectedPath)).toBe(true);
    expect(fs.existsSync(originalExtractedPath)).toBe(false);
  });

  it("skips auto-rename when no SxxExx token exists in source filename", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Asbest.S02.GERMAN.720p.WEB.AVC-4SF";
    const sourceFileName = "4sf-asbest.rp.web.7p-episode.mkv";
    const unexpectedName = "Asbest.S02.GERMAN.REPACK.720p.WEB.AVC-4SF.mkv";
    const { session, itemId, extractDir, originalExtractedPath } = createCompletedArchiveSession(root, packageName, sourceFileName);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: true,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => manager.getSnapshot().session.items[itemId]?.fullStatus.startsWith("Entpackt"), 12000);
    expect(fs.existsSync(originalExtractedPath)).toBe(true);
    expect(fs.existsSync(path.join(extractDir, unexpectedName))).toBe(false);
  });

  it("does not rename extracted scene files when auto-rename is disabled", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Asbest.S02.GERMAN.720p.WEB.AVC-4SF";
    const sourceFileName = "4sf-asbest.web.7p-s02e01.mkv";
    const unexpectedName = "Asbest.S02E01.GERMAN.720p.WEB.AVC-4SF.mkv";
    const { session, itemId, extractDir, originalExtractedPath } = createCompletedArchiveSession(root, packageName, sourceFileName);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: false,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    await waitFor(() => manager.getSnapshot().session.items[itemId]?.fullStatus.startsWith("Entpackt"), 12000);
    expect(fs.existsSync(originalExtractedPath)).toBe(true);
    expect(fs.existsSync(path.join(extractDir, unexpectedName))).toBe(false);
  });

  it("moves extracted MKV files into a flat library folder per completed package", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Flat-Pack";
    const sourceFileName = "Season 1/Episode01.mkv";
    const { session, packageId, itemId, originalExtractedPath } = createCompletedArchiveSession(root, packageName, sourceFileName);
    const mkvLibraryDir = path.join(root, "mkv-library");

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: false,
        collectMkvToLibrary: true,
        mkvLibraryDir,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const flattenedPath = path.join(mkvLibraryDir, "Episode01.mkv");
    await waitFor(() => fs.existsSync(flattenedPath), 12000);

    expect(manager.getSnapshot().session.packages[packageId]?.status).toBe("completed");
    expect(manager.getSnapshot().session.items[itemId]?.fullStatus).toBe("Entpackt - Done");
    expect(fs.existsSync(flattenedPath)).toBe(true);
    expect(fs.existsSync(originalExtractedPath)).toBe(false);
  });

  it("keeps existing MKV names and appends a suffix while flattening", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Flat-Collision";
    const sourceFileName = "Season 1/Episode01.mkv";
    const { session } = createCompletedArchiveSession(root, packageName, sourceFileName);
    const mkvLibraryDir = path.join(root, "mkv-library");
    fs.mkdirSync(mkvLibraryDir, { recursive: true });
    const existingPath = path.join(mkvLibraryDir, "Episode01.mkv");
    fs.writeFileSync(existingPath, "already-here", "utf8");

    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: false,
        collectMkvToLibrary: true,
        mkvLibraryDir,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const suffixedPath = path.join(mkvLibraryDir, "Episode01 (2).mkv");
    await waitFor(() => fs.existsSync(suffixedPath), 12000);

    expect(fs.existsSync(existingPath)).toBe(true);
    expect(fs.readFileSync(existingPath, "utf8")).toBe("already-here");
    expect(fs.existsSync(suffixedPath)).toBe(true);
  });

  it("removes empty package folders after MKV flattening even with desktop.ini or thumbs.db", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const packageName = "Gotham.S03.GERMAN.5.1.DL.AC3.720p.BDRiP.x264-TvR";
    const outputDir = path.join(root, "downloads", packageName);
    const extractDir = path.join(root, "extract", packageName);
    fs.mkdirSync(outputDir, { recursive: true });

    const nestedFolder = "Gotham.S03E11.Ein.Ungeheuer.namens.Eifersucht.GERMAN.5.1.DL.AC3.720p.BDRiP.x264-TvR";
    const sourceFileName = `${nestedFolder}/tvr-gotham-s03e11-720p.mkv`;
    const zip = new AdmZip();
    zip.addFile(sourceFileName, Buffer.from("video"));
    zip.addFile(`${nestedFolder}/tvr-gotham-s03-720p.nfo`, Buffer.from("info"));
    zip.addFile(`${nestedFolder}/Thumbs.db`, Buffer.from("thumbs"));
    zip.addFile("desktop.ini", Buffer.from("system"));
    const archivePath = path.join(outputDir, "episode.zip");
    zip.writeZip(archivePath);
    const archiveSize = fs.statSync(archivePath).size;

    const session = emptySession();
    const packageId = `${packageName}-pkg`;
    const itemId = `${packageName}-item`;
    const createdAt = Date.now() - 20_000;
    session.packageOrder = [packageId];
    session.packages[packageId] = {
      id: packageId,
      name: packageName,
      outputDir,
      extractDir,
      status: "downloading",
      itemIds: [itemId],
      cancelled: false,
      enabled: true,
      createdAt,
      updatedAt: createdAt
    };
    session.items[itemId] = {
      id: itemId,
      packageId,
      url: "https://dummy/gotham",
      provider: "realdebrid",
      status: "completed",
      retries: 0,
      speedBps: 0,
      downloadedBytes: archiveSize,
      totalBytes: archiveSize,
      progressPercent: 100,
      fileName: "episode.zip",
      targetPath: archivePath,
      resumable: true,
      attempts: 1,
      lastError: "",
      fullStatus: "Fertig",
      createdAt,
      updatedAt: createdAt
    };

    const mkvLibraryDir = path.join(root, "mkv-library");
    new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        autoExtract: true,
        autoRename4sf4sj: false,
        collectMkvToLibrary: true,
        mkvLibraryDir,
        enableIntegrityCheck: false,
        cleanupMode: "none"
      },
      session,
      createStoragePaths(path.join(root, "state"))
    );

    const flattenedPath = path.join(mkvLibraryDir, "tvr-gotham-s03e11-720p.mkv");
    await waitFor(() => fs.existsSync(flattenedPath), 12000);

    expect(fs.existsSync(flattenedPath)).toBe(true);
    expect(fs.existsSync(extractDir)).toBe(false);
  });

  it("throws a controlled error for invalid queue import JSON", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    expect(() => manager.importQueue("{not-json")).toThrow(/Ungultige Queue-Datei/i);
  });

  it("applies global speed limit path when global mode is enabled", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract"),
        speedLimitEnabled: true,
        speedLimitMode: "global",
        speedLimitKbps: 512
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      applySpeedLimit: (chunkBytes: number, localWindowBytes: number, localWindowStarted: number) => Promise<void>;
      globalSpeedLimitNextAt: number;
    };

    const start = Date.now();
    await internal.applySpeedLimit(1024, 0, start);
    expect(internal.globalSpeedLimitNextAt).toBeGreaterThan(start);
  });

  it("resets speed window head when start finds no runnable items", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      speedEvents: Array<{ at: number; bytes: number }>;
      speedEventsHead: number;
      speedBytesLastWindow: number;
    };
    internal.speedEvents = [{ at: Date.now() - 10_000, bytes: 999 }];
    internal.speedEventsHead = 5;
    internal.speedBytesLastWindow = 999;

    await manager.start();
    expect(internal.speedEventsHead).toBe(0);
    expect(internal.speedEvents.length).toBe(0);
    expect(internal.speedBytesLastWindow).toBe(0);
  });

  it("does not trigger global stall abort while write-buffer is disk-blocked", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const previousGlobalWatchdog = process.env.RD_GLOBAL_STALL_TIMEOUT_MS;
    process.env.RD_GLOBAL_STALL_TIMEOUT_MS = "2500";

    try {
      const manager = new DownloadManager(
        {
          ...defaultSettings(),
          token: "rd-token",
          outputDir: path.join(root, "downloads"),
          extractDir: path.join(root, "extract")
        },
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "disk-block-guard", links: ["https://dummy/disk-block-guard"] }]);
      const snapshot = manager.getSnapshot();
      const packageId = snapshot.session.packageOrder[0] || "";
      const itemId = snapshot.session.packages[packageId]?.itemIds[0] || "";

      const internal = manager as unknown as any;
      internal.session.running = true;
      internal.session.paused = false;
      internal.session.reconnectUntil = 0;
      internal.session.totalDownloadedBytes = 0;
      internal.session.items[itemId].status = "downloading";
      internal.lastGlobalProgressBytes = 0;
      internal.lastGlobalProgressAt = Date.now() - 10000;

      const abortController = new AbortController();
      internal.activeTasks.set(itemId, {
        itemId,
        packageId,
        abortController,
        abortReason: "none",
        resumable: true,
        nonResumableCounted: false,
        blockedOnDiskWrite: true,
        blockedOnDiskSince: Date.now() - 5000
      });

      internal.runGlobalStallWatchdog(Date.now());

      expect(abortController.signal.aborted).toBe(false);
      expect(internal.lastGlobalProgressAt).toBeGreaterThan(Date.now() - 2000);
    } finally {
      if (previousGlobalWatchdog === undefined) {
        delete process.env.RD_GLOBAL_STALL_TIMEOUT_MS;
      } else {
        process.env.RD_GLOBAL_STALL_TIMEOUT_MS = previousGlobalWatchdog;
      }
    }
  });

  it("cleans run tracking when start conflict is skipped", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    manager.addPackages([{ name: "conflict-skip", links: ["https://dummy/skip"] }]);
    const snapshot = manager.getSnapshot();
    const packageId = snapshot.session.packageOrder[0];
    const itemId = snapshot.session.packages[packageId]?.itemIds[0] || "";

    const internal = manager as unknown as {
      runItemIds: Set<string>;
      runPackageIds: Set<string>;
      runOutcomes: Map<string, "completed" | "failed" | "cancelled">;
    };
    internal.runItemIds.add(itemId);
    internal.runPackageIds.add(packageId);
    internal.runOutcomes.set(itemId, "completed");

    const result = await manager.resolveStartConflict(packageId, "skip");
    expect(result.skipped).toBe(true);
    expect(internal.runItemIds.has(itemId)).toBe(false);
    expect(internal.runPackageIds.has(packageId)).toBe(false);
    expect(internal.runOutcomes.has(itemId)).toBe(false);
  });

  it("clears stale run outcomes on overwrite conflict resolution", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    manager.addPackages([{ name: "conflict-overwrite", links: ["https://dummy/overwrite"] }]);
    const snapshot = manager.getSnapshot();
    const packageId = snapshot.session.packageOrder[0];
    const itemId = snapshot.session.packages[packageId]?.itemIds[0] || "";

    const internal = manager as unknown as {
      runOutcomes: Map<string, "completed" | "failed" | "cancelled">;
    };
    internal.runOutcomes.set(itemId, "failed");

    const result = await manager.resolveStartConflict(packageId, "overwrite");
    expect(result.overwritten).toBe(true);
    expect(internal.runOutcomes.has(itemId)).toBe(false);
    expect(manager.getSnapshot().session.items[itemId]?.status).toBe("queued");
  });

  it("clears speed display buffers when run finishes", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        token: "rd-token",
        outputDir: path.join(root, "downloads"),
        extractDir: path.join(root, "extract")
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      runItemIds: Set<string>;
      runOutcomes: Map<string, "completed" | "failed" | "cancelled">;
      runCompletedPackages: Set<string>;
      session: { runStartedAt: number; totalDownloadedBytes: number; running: boolean; paused: boolean };
      speedEvents: Array<{ at: number; bytes: number }>;
      speedEventsHead: number;
      speedBytesLastWindow: number;
      finishRun: () => void;
    };

    internal.session.running = true;
    internal.session.paused = false;
    internal.session.runStartedAt = Date.now() - 2000;
    internal.session.totalDownloadedBytes = 4096;
    internal.runItemIds = new Set(["x"]);
    internal.runOutcomes = new Map([["x", "completed"]]);
    internal.runCompletedPackages = new Set();
    internal.speedEvents = [{ at: Date.now(), bytes: 4096 }];
    internal.speedEventsHead = 1;
    internal.speedBytesLastWindow = 4096;

    internal.finishRun();

    expect(internal.speedEvents.length).toBe(0);
    expect(internal.speedEventsHead).toBe(0);
    expect(internal.speedBytesLastWindow).toBe(0);
  });

  it("tracks daily usage on the actual provider key without touching other providers", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        megaLogin: "mega-user",
        megaPassword: "mega-pass",
        megaDebridApiEnabled: true,
        providerDailyUsageDay: getProviderUsageDayKey(),
        providerDailyUsageBytes: { realdebrid: 512 },
        providerTotalUsageBytes: { realdebrid: 2048 }
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      recordProviderDownloadedBytes: (provider: "megadebrid", bytes: number) => void;
      settings: ReturnType<typeof defaultSettings>;
    };

    internal.recordProviderDownloadedBytes("megadebrid", 1024);

    expect(internal.settings.providerDailyUsageBytes.realdebrid).toBe(512);
    expect(internal.settings.providerDailyUsageBytes["megadebrid-api"]).toBe(1024);
    expect((internal.settings.providerDailyUsageBytes as Record<string, number>).megadebrid).toBeUndefined();
    expect(internal.settings.providerTotalUsageBytes.realdebrid).toBe(2048);
    expect(internal.settings.providerTotalUsageBytes["megadebrid-api"]).toBe(1024);
    expect((internal.settings.providerTotalUsageBytes as Record<string, number>).megadebrid).toBeUndefined();
  });

  it("tracks daily usage on the actual Debrid-Link key without touching other keys", () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const [firstKey, secondKey] = parseDebridLinkApiKeys("dl-key-one\ndl-key-two");

    const manager = new DownloadManager(
      {
        ...defaultSettings(),
        debridLinkApiKeys: "dl-key-one\ndl-key-two",
        providerDailyUsageDay: getProviderUsageDayKey(),
        providerDailyUsageBytes: { debridlink: 256 },
        providerTotalUsageBytes: { debridlink: 4096 },
        debridLinkApiKeyDailyUsageBytes: { [secondKey.id]: 512 },
        debridLinkApiKeyTotalUsageBytes: { [secondKey.id]: 2048 }
      },
      emptySession(),
      createStoragePaths(path.join(root, "state"))
    );

    const internal = manager as unknown as {
      recordProviderDownloadedBytes: (provider: "debridlink", bytes: number, providerAccountId?: string) => void;
      settings: ReturnType<typeof defaultSettings>;
    };

    internal.recordProviderDownloadedBytes("debridlink", 1024, firstKey.id);

    expect(internal.settings.providerDailyUsageBytes.debridlink).toBe(1280);
    expect(internal.settings.providerTotalUsageBytes.debridlink).toBe(5120);
    expect(internal.settings.debridLinkApiKeyDailyUsageBytes[firstKey.id]).toBe(1024);
    expect(internal.settings.debridLinkApiKeyDailyUsageBytes[secondKey.id]).toBe(512);
    expect(internal.settings.debridLinkApiKeyTotalUsageBytes[firstKey.id]).toBe(1024);
    expect(internal.settings.debridLinkApiKeyTotalUsageBytes[secondKey.id]).toBe(2048);
  });

  it("does not hang when rapid stop, disable provider, start", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-dm-"));
    tempDirs.push(root);
    const binary = Buffer.alloc(256 * 1024, 7);

    // Slow server: delivers data in chunks with delay
    const server = http.createServer((req, res) => {
      if ((req.url || "") !== "/slow-dl") {
        res.statusCode = 404;
        res.end("not-found");
        return;
      }
      res.statusCode = 200;
      res.setHeader("Accept-Ranges", "bytes");
      res.setHeader("Content-Length", String(binary.length));
      // Send first half, then delay
      res.write(binary.subarray(0, Math.floor(binary.length / 4)));
      const timer = setTimeout(() => {
        if (!res.writableEnded && !res.destroyed) {
          res.end(binary.subarray(Math.floor(binary.length / 4)));
        }
      }, 5000);
      res.on("close", () => clearTimeout(timer));
    });

    server.listen(0, "127.0.0.1");
    await once(server, "listening");

    const address = server.address();
    if (!address || typeof address === "string") {
      throw new Error("server address unavailable");
    }
    const directUrl = `http://127.0.0.1:${address.port}/slow-dl`;

    globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
      const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
      if (url.includes("/unrestrict/link")) {
        return new Response(
          JSON.stringify({
            download: directUrl,
            filename: "test-file.bin",
            filesize: binary.length
          }),
          {
            status: 200,
            headers: { "Content-Type": "application/json" }
          }
        );
      }
      return originalFetch(input, init);
    };

    const settings = {
      ...defaultSettings(),
      token: "rd-token",
      outputDir: path.join(root, "downloads"),
      extractDir: path.join(root, "extract"),
      autoExtract: false,
      maxParallel: 1,
      autoReconnect: false,
      retryLimit: 1
    };

    try {
      const manager = new DownloadManager(
        settings,
        emptySession(),
        createStoragePaths(path.join(root, "state"))
      );

      manager.addPackages([{ name: "hang-test", links: ["https://dummy/hang-test"] }]);

      // Step 1: Start and wait for download to begin
      await manager.start();
      await waitFor(() => {
        const items = Object.values(manager.getSnapshot().session.items);
        return items.some((item) => item.status === "downloading");
      }, 12000);

      // Step 2: Stop — do NOT wait for running=false
      manager.stop();

      // Step 3: Immediately disable the active provider
      manager.setSettings({
        ...settings,
        disabledProviders: ["realdebrid"]
      });

      // Step 4: Start again immediately — must resolve (not hang)
      const startPromise = manager.start();
      const timeout = new Promise<"timeout">((resolve) => setTimeout(() => resolve("timeout"), 8000));
      const result = await Promise.race([startPromise.then(() => "ok" as const), timeout]);
      expect(result).toBe("ok");
    } finally {
      server.close();
      await once(server, "close");
    }
  }, 30000);
});
