import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

const {
  mockCookiesSet,
  mockFetch,
  mockClearStorageData,
  mockClearCache,
  mockFromPartition,
  mockSession
} = vi.hoisted(() => {
  const cookiesSet = vi.fn();
  const fetch = vi.fn();
  const clearStorageData = vi.fn();
  const clearCache = vi.fn();
  const fromPartition = vi.fn();
  return {
    mockCookiesSet: cookiesSet,
    mockFetch: fetch,
    mockClearStorageData: clearStorageData,
    mockClearCache: clearCache,
    mockFromPartition: fromPartition,
    mockSession: {
      cookies: {
        set: cookiesSet
      },
      fetch,
      clearStorageData,
      clearCache
    }
  };
});

vi.mock("electron", () => ({
  session: {
    fromPartition: mockFromPartition
  }
}));

vi.mock("../src/main/logger", () => ({
  logger: {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn()
  }
}));

import { BestDebridWebFallback } from "../src/main/bestdebrid-web";

function createCookieFile(contents: string): string {
  const filePath = path.join(os.tmpdir(), `bestdebrid-cookies-${Date.now()}-${Math.random().toString(16).slice(2)}.txt`);
  fs.writeFileSync(filePath, contents, "utf8");
  return filePath;
}

describe("bestdebrid-web", () => {
  const tempFiles: string[] = [];

  beforeEach(() => {
    mockFromPartition.mockReturnValue(mockSession);
  });

  afterEach(() => {
    vi.clearAllMocks();
    mockFromPartition.mockReturnValue(mockSession);
    while (tempFiles.length > 0) {
      const filePath = tempFiles.pop();
      if (!filePath) {
        continue;
      }
      try {
        fs.rmSync(filePath, { force: true });
      } catch {
      }
    }
  });

  it("imports HttpOnly Netscape cookies instead of skipping them as comments", async () => {
    const filePath = createCookieFile([
      "# Netscape HTTP Cookie File",
      "#HttpOnly_.bestdebrid.com\tTRUE\t/\tTRUE\t1803585385\tPHPSESSID\tsecret-session",
      ".bestdebrid.com\tTRUE\t/\tFALSE\t1806720721\t_ga\ttracking"
    ].join("\n"));
    tempFiles.push(filePath);

    const fallback = new BestDebridWebFallback(() => true);
    const count = await fallback.importCookiesFromFile(filePath);

    expect(count).toBe(2);
    expect(mockClearStorageData).toHaveBeenCalledTimes(1);
    expect(mockClearStorageData).toHaveBeenCalledWith({
      storages: ["cookies", "indexdb", "localstorage", "serviceworkers", "cachestorage"]
    });
    expect(mockCookiesSet).toHaveBeenCalledTimes(2);
    expect(mockCookiesSet).toHaveBeenCalledWith(expect.objectContaining({
      name: "PHPSESSID",
      domain: ".bestdebrid.com",
      httpOnly: true,
      secure: true
    }));
  });

  it("deduplicates conflicting session cookies and prefers the HttpOnly variant", async () => {
    const filePath = createCookieFile([
      "# Netscape HTTP Cookie File",
      "bestdebrid.com\tFALSE\t/\tTRUE\t1803585384\tPHPSESSID\tnon-http-only",
      "#HttpOnly_.bestdebrid.com\tTRUE\t/\tTRUE\t1803585385\tPHPSESSID\thttp-only"
    ].join("\n"));
    tempFiles.push(filePath);

    const fallback = new BestDebridWebFallback(() => true);
    const count = await fallback.importCookiesFromFile(filePath);

    expect(count).toBe(1);
    expect(mockCookiesSet).toHaveBeenCalledTimes(1);
    expect(mockCookiesSet).toHaveBeenCalledWith(expect.objectContaining({
      name: "PHPSESSID",
      value: "http-only",
      httpOnly: true,
      domain: ".bestdebrid.com"
    }));
  });

  it("rejects cookie files that only contain tracking cookies", async () => {
    const filePath = createCookieFile([
      "# Netscape HTTP Cookie File",
      ".bestdebrid.com\tTRUE\t/\tTRUE\t1803585385\t__stripe_mid\tstripe",
      ".bestdebrid.com\tTRUE\t/\tFALSE\t1806720721\t_ga\ttracking"
    ].join("\n"));
    tempFiles.push(filePath);

    const fallback = new BestDebridWebFallback(() => true);

    await expect(fallback.importCookiesFromFile(filePath))
      .rejects.toThrow("Login-Cookie");
    expect(mockCookiesSet).not.toHaveBeenCalled();
  });

  it("treats BestDebrid free-user errors as logged-out sessions when the account page is guest-only", async () => {
    const filePath = createCookieFile([
      "# Netscape HTTP Cookie File",
      "bestdebrid.com\tFALSE\t/\tTRUE\t1803585385\tPHPSESSID\tsecret-session"
    ].join("\n"));
    tempFiles.push(filePath);

    mockFetch
      .mockResolvedValueOnce(new Response(JSON.stringify({
        error: 1,
        message: "Free users are not allowed to download using a VPN or proxy. Please purchase a premium plan."
      }), { status: 200 }))
      .mockResolvedValueOnce(new Response("<div class=\"font-medium\">Guest</div>", { status: 200 }));

    const fallback = new BestDebridWebFallback(() => true);
    await fallback.importCookiesFromFile(filePath);

    await expect(fallback.unrestrict("https://1fichier.com/?abc"))
      .rejects.toThrow("Nicht eingeloggt");
    await expect(fallback.unrestrict("https://1fichier.com/?abc"))
      .rejects.toThrow("Keine Cookies importiert");

    expect(mockFetch).toHaveBeenCalledTimes(2);
    expect(mockFetch.mock.calls[0]?.[0]).toBe("https://bestdebrid.com/api/v1/generateLink");
    expect(mockFetch.mock.calls[1]?.[0]).toBe("https://bestdebrid.com/en/downloader/");
  });
});
