const { expect } = require("chai");

/**
 * Tests for AnonymizedFile pure logic: extension(), isImage(),
 * isFileSupported().
 *
 * These methods rely only on the file name / anonymizedPath and
 * repository options, so they can be tested without a database.
 */

// ---------------------------------------------------------------------------
// Replicated logic from src/core/AnonymizedFile.ts
// ---------------------------------------------------------------------------

function extension(filename) {
  const extensions = filename.split(".").reverse();
  return extensions[0].toLowerCase();
}

const IMAGE_EXTENSIONS = [
  "png",
  "jpg",
  "jpeg",
  "gif",
  "svg",
  "ico",
  "bmp",
  "tiff",
  "tif",
  "webp",
  "avif",
  "heif",
  "heic",
];

function isImage(filename) {
  const ext = extension(filename);
  return IMAGE_EXTENSIONS.includes(ext);
}

function isFileSupported(filename, options) {
  const ext = extension(filename);
  if (!options.pdf && ext === "pdf") {
    return false;
  }
  if (!options.image && isImage(filename)) {
    return false;
  }
  return true;
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe("AnonymizedFile.extension()", function () {
  it("extracts a simple extension", function () {
    expect(extension("file.txt")).to.equal("txt");
  });

  it("extracts the last extension from multi-dot files", function () {
    expect(extension("archive.tar.gz")).to.equal("gz");
  });

  it("lowercases the extension", function () {
    expect(extension("document.PDF")).to.equal("pdf");
    expect(extension("photo.JpEg")).to.equal("jpeg");
  });

  it("handles dotfiles", function () {
    expect(extension(".gitignore")).to.equal("gitignore");
  });

  it("handles files with no extension", function () {
    // "Makefile".split(".").reverse() → ["Makefile"]
    // [0].toLowerCase() → "makefile"
    expect(extension("Makefile")).to.equal("makefile");
  });

  it("handles files with trailing dot", function () {
    // "file.".split(".").reverse() → ["", "file"]
    expect(extension("file.")).to.equal("");
  });

  it("handles deeply nested extensions", function () {
    expect(extension("a.b.c.d.e.f")).to.equal("f");
  });

  it("handles uppercase mixed with numbers", function () {
    expect(extension("data.JSON5")).to.equal("json5");
  });
});

describe("AnonymizedFile.isImage()", function () {
  it("recognizes png as image", function () {
    expect(isImage("photo.png")).to.be.true;
  });

  it("recognizes jpg as image", function () {
    expect(isImage("photo.jpg")).to.be.true;
  });

  it("recognizes jpeg as image", function () {
    expect(isImage("photo.jpeg")).to.be.true;
  });

  it("recognizes gif as image", function () {
    expect(isImage("anim.gif")).to.be.true;
  });

  it("recognizes svg as image", function () {
    expect(isImage("icon.svg")).to.be.true;
  });

  it("recognizes ico as image", function () {
    expect(isImage("favicon.ico")).to.be.true;
  });

  it("recognizes bmp as image", function () {
    expect(isImage("old.bmp")).to.be.true;
  });

  it("recognizes tiff as image", function () {
    expect(isImage("scan.tiff")).to.be.true;
  });

  it("recognizes tif as image", function () {
    expect(isImage("scan.tif")).to.be.true;
  });

  it("recognizes webp as image", function () {
    expect(isImage("web.webp")).to.be.true;
  });

  it("recognizes avif as image", function () {
    expect(isImage("modern.avif")).to.be.true;
  });

  it("recognizes heif as image", function () {
    expect(isImage("apple.heif")).to.be.true;
  });

  it("recognizes heic as image", function () {
    expect(isImage("iphone.heic")).to.be.true;
  });

  it("is case-insensitive", function () {
    expect(isImage("photo.PNG")).to.be.true;
    expect(isImage("photo.Jpg")).to.be.true;
  });

  it("rejects non-image extensions", function () {
    expect(isImage("file.txt")).to.be.false;
    expect(isImage("file.pdf")).to.be.false;
    expect(isImage("file.js")).to.be.false;
    expect(isImage("file.html")).to.be.false;
    expect(isImage("file.md")).to.be.false;
  });

  it("rejects files containing image extension names but with different ext", function () {
    expect(isImage("my-png-converter.exe")).to.be.false;
  });
});

describe("AnonymizedFile.isFileSupported()", function () {
  it("supports all files when all options are enabled", function () {
    const opts = { pdf: true, image: true };
    expect(isFileSupported("file.pdf", opts)).to.be.true;
    expect(isFileSupported("file.png", opts)).to.be.true;
    expect(isFileSupported("file.txt", opts)).to.be.true;
  });

  it("rejects PDF when pdf option is false", function () {
    expect(isFileSupported("file.pdf", { pdf: false, image: true })).to.be
      .false;
  });

  it("accepts PDF when pdf option is true", function () {
    expect(isFileSupported("file.pdf", { pdf: true, image: true })).to.be.true;
  });

  it("rejects images when image option is false", function () {
    expect(isFileSupported("photo.png", { pdf: true, image: false })).to.be
      .false;
    expect(isFileSupported("photo.jpg", { pdf: true, image: false })).to.be
      .false;
    expect(isFileSupported("icon.svg", { pdf: true, image: false })).to.be
      .false;
  });

  it("accepts images when image option is true", function () {
    expect(isFileSupported("photo.png", { pdf: true, image: true })).to.be
      .true;
  });

  it("accepts non-image, non-PDF files regardless of options", function () {
    expect(isFileSupported("file.js", { pdf: false, image: false })).to.be
      .true;
    expect(isFileSupported("file.md", { pdf: false, image: false })).to.be
      .true;
    expect(isFileSupported("file.html", { pdf: false, image: false })).to.be
      .true;
  });

  it("rejects both PDF and images when both are disabled", function () {
    const opts = { pdf: false, image: false };
    expect(isFileSupported("doc.pdf", opts)).to.be.false;
    expect(isFileSupported("pic.png", opts)).to.be.false;
    expect(isFileSupported("code.ts", opts)).to.be.true;
  });

  it("is case-insensitive for PDF", function () {
    expect(isFileSupported("file.PDF", { pdf: false, image: true })).to.be
      .false;
  });

  it("is case-insensitive for images", function () {
    expect(isFileSupported("photo.PNG", { pdf: true, image: false })).to.be
      .false;
  });
});

// ---------------------------------------------------------------------------
// Replicated logic from AnonymizedFile.recoverTruncatedFile (#738): a file
// missing from the database is only recovered from GitHub when its directory
// is under a folder whose tree listing was truncated.
// ---------------------------------------------------------------------------

function isUnderTruncatedFolder(fileDir, truncatedFolders) {
  return truncatedFolders.some(
    (folder) =>
      folder === "" || fileDir === folder || fileDir.startsWith(folder + "/")
  );
}

describe("AnonymizedFile truncated-folder matching (#738)", function () {
  it("matches files directly inside a truncated folder", function () {
    expect(isUnderTruncatedFolder("data", ["data"])).to.be.true;
  });

  it("matches files nested under a truncated folder", function () {
    expect(isUnderTruncatedFolder("data/sub/dir", ["data"])).to.be.true;
  });

  it("matches everything when the root listing was truncated", function () {
    expect(isUnderTruncatedFolder("", [""])).to.be.true;
    expect(isUnderTruncatedFolder("any/dir", [""])).to.be.true;
  });

  it("does not match sibling folders sharing a prefix", function () {
    expect(isUnderTruncatedFolder("database", ["data"])).to.be.false;
    expect(isUnderTruncatedFolder("database/sub", ["data"])).to.be.false;
  });

  it("does not match when nothing was truncated", function () {
    expect(isUnderTruncatedFolder("data", [])).to.be.false;
  });
});
