/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

const { PdfJsTelemetry } = ChromeUtils.importESModule(
  "resource://pdf.js/PdfJsTelemetry.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
  "resource://testing-common/Sinon.sys.mjs"
);

const RELATIVE_DIR = "toolkit/components/pdfjs/test/";
const TESTROOT = "https://example.com/browser/" + RELATIVE_DIR;

Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
  this
);

const MockFilePicker = SpecialPowers.MockFilePicker;
const file = new FileUtils.File(getTestFilePath("moz.png"));
const altTextPref = "pdfjs.enableAltText";
const guessAltTextPref = "pdfjs.enableGuessAltText";
const newFlowPref = "pdfjs.enableUpdatedAddImage";
const browserMLPref = "browser.ml.enable";
const altTextModelDownloadPref = "pdfjs.enableAltTextModelDownload";

add_setup(async function () {
  MockFilePicker.init();
  MockFilePicker.setFiles([file]);
  MockFilePicker.returnValue = MockFilePicker.returnOK;
  registerCleanupFunction(function () {
    MockFilePicker.cleanup();
  });
});

const sandbox = sinon.createSandbox();
registerCleanupFunction(() => {
  sandbox.restore();
});

const original = PdfJsTelemetry.report.bind(PdfJsTelemetry);
const resolvers = new Map();
sandbox.stub(PdfJsTelemetry, "report").callsFake(aData => {
  // console.log("Telemetry", aData);
  const name = aData.data?.action?.split(".").pop();
  resolvers.get(name)?.resolve();
  original(aData);
});
const getPromise = name => {
  const resolver = Promise.withResolvers();
  resolvers.set(name, resolver);
  return resolver.promise;
};
let telemetryPromise;

// Test telemetry for new alt-text flow.

add_task(async function test_telemetry_new_alt_text_settings() {
  makePDFJSHandler();

  await BrowserTestUtils.withNewTab(
    { gBrowser, url: "about:blank" },
    async function (browser) {
      await SpecialPowers.pushPrefEnv({
        set: [
          ["pdfjs.annotationEditorMode", 0],
          [altTextPref, true],
          [guessAltTextPref, true],
          [newFlowPref, true],
          [browserMLPref, true],
          [altTextModelDownloadPref, true],
        ],
      });

      await Services.fog.testFlushAllChildren();
      Services.fog.testResetFOG();

      telemetryPromise = getPromise("alt_text_edit");

      // check that PDF is opened with internal viewer
      await waitForPdfJSAllLayers(browser, TESTROOT + "file_empty_test.pdf", [
        [
          "annotationEditorLayer",
          "annotationLayer",
          "textLayer",
          "canvasWrapper",
        ],
      ]);

      await telemetryPromise;
      await Services.fog.testFlushAllChildren();
      let value = Glean.pdfjsImage.altTextEdit.ai_generation.testGetValue();
      Assert.ok(value, "Should have ai_generation enabled");

      value = Glean.pdfjsImage.altTextEdit.ask_to_edit.testGetValue();
      Assert.ok(value, "Should have ask_to_edit enabled");

      // We test the telemetry for the settings dialog.
      await clickOn(browser, "#secondaryToolbarToggle");

      telemetryPromise = getPromise("settings_displayed");
      await clickOn(browser, "#imageAltTextSettings");
      await telemetryPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.settingsDisplayed, [
        {},
      ]);

      await waitForSelector(browser, "#altTextSettingsDialog");

      for (const [id, record, name] of [
        [
          "createModelButton",
          Glean.pdfjsImageAltText.settingsAiGenerationCheck,
          "settings_ai_generation_check",
        ],
        [
          "showAltTextDialogButton",
          Glean.pdfjsImageAltText.settingsEditAltTextCheck,
          "settings_edit_alt_text_check",
        ],
      ]) {
        telemetryPromise = getPromise(name);
        await clickOn(browser, `#${id}`);
        await telemetryPromise;

        await testTelemetryEventExtra(record, [{ status: "false" }]);

        telemetryPromise = getPromise(name);
        await clickOn(browser, `#${id}`);
        await telemetryPromise;
        await Services.fog.testFlushAllChildren();
        await testTelemetryEventExtra(record, [{ status: "true" }]);
      }

      await SpecialPowers.spawn(browser, [], async () => {
        const a = content.document.querySelector("#altTextSettingsLearnMore");
        a.href = "#";
        a.target = "";
      });
      telemetryPromise = getPromise("info");
      await clickOn(browser, `#altTextSettingsLearnMore`);
      await telemetryPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.info, [
        { topic: "ai_generation" },
      ]);

      telemetryPromise = getPromise("model_deleted");
      await clickOn(browser, "#createModelButton");
      await telemetryPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.modelDeleted, [{}]);

      telemetryPromise = getPromise("model_download_complete");
      await clickOn(browser, "#createModelButton");
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.modelDownloadStart,
        [{}],
        false
      );
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.modelDownloadComplete,
        [{}]
      );

      await clickOn(browser, "#altTextSettingsCloseButton");

      await waitForPdfJSClose(browser);
      await SpecialPowers.popPrefEnv();
    }
  );
});

add_task(async function test_telemetry_new_alt_text_dialog() {
  makePDFJSHandler();

  await BrowserTestUtils.withNewTab(
    { gBrowser, url: "about:blank" },
    async function (browser) {
      await SpecialPowers.pushPrefEnv({
        set: [
          ["pdfjs.annotationEditorMode", 0],
          [altTextPref, true],
          [guessAltTextPref, true],
          [newFlowPref, true],
          [browserMLPref, true],
          [altTextModelDownloadPref, true],
        ],
      });

      await Services.fog.testFlushAllChildren();
      Services.fog.testResetFOG();

      // check that PDF is opened with internal viewer
      await waitForPdfJSAllLayers(browser, TESTROOT + "file_empty_test.pdf", [
        [
          "annotationEditorLayer",
          "annotationLayer",
          "textLayer",
          "canvasWrapper",
        ],
      ]);

      telemetryPromise = getPromise("icon_click");
      await enableEditor(browser, "Stamp", 0);
      await telemetryPromise;
      await testTelemetryEventExtra(Glean.pdfjsImage.iconClick, [{}]);

      // We test the telemetry for the alt-text dialog.

      telemetryPromise = getPromise("add_image_click");
      const imageSelectedPromise = getPromise("image_selected");
      const modelResultPromise = getPromise("model_result");
      await clickOn(browser, `#editorStampAddImage`);
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImage.addImageClick,
        [{}],
        false
      );

      await imageSelectedPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImage.imageSelected,
        [{ alt_text_modal: "true" }],
        false
      );

      await modelResultPromise;
      await Services.fog.testFlushAllChildren();
      let values = Glean.pdfjsImageAltText.modelResult.testGetValue();
      Assert.equal(values.length, 1, "Should have 1 model result");
      const extra = values[0].extra;
      Assert.greater(Number(extra.time), 0, "time must be a positive number");
      Assert.ok(!!extra.length, "length must be a positive number");

      await waitForSelector(browser, "#newAltTextDialog");

      await SpecialPowers.spawn(browser, [], async () => {
        const a = content.document.querySelector("#newAltTextLearnMore");
        a.href = "#";
        a.target = "";
      });
      telemetryPromise = getPromise("info");
      await clickOn(browser, `#newAltTextLearnMore`);
      await telemetryPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.info, [
        { topic: "alt_text" },
      ]);

      await SpecialPowers.spawn(browser, [], async function () {
        const { ContentTaskUtils } = ChromeUtils.importESModule(
          "resource://testing-common/ContentTaskUtils.sys.mjs"
        );
        const { document } = content;
        await ContentTaskUtils.waitForCondition(
          () =>
            document.querySelector("#newAltTextDescriptionTextarea").value !==
            "",
          "Textarea mustn't be empty"
        );
      });

      telemetryPromise = getPromise("image_added");
      const savePromise = getPromise("save");
      const statusPromise = getPromise("image_status_label_displayed");
      await clickOn(browser, `#newAltTextSave`);
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImage.imageAdded,
        [
          {
            alt_text_modal: "true",
            alt_text_type: "present",
          },
        ],
        false
      );
      await savePromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.save,
        [
          {
            alt_text_type: "present",
            flow: "image_add",
          },
        ],
        false
      );
      await statusPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.imageStatusLabelDisplayed,
        [
          {
            label: "added",
          },
        ]
      );

      telemetryPromise = getPromise("image_status_label_clicked");
      await clickOn(browser, ".altText.new");
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.imageStatusLabelClicked,
        [
          {
            label: "added",
          },
        ]
      );

      await SpecialPowers.spawn(browser, [], async () => {
        const textarea = content.document.querySelector(
          "#newAltTextDescriptionTextarea"
        );
        textarea.value = "Hello Pdf.js World";
      });

      telemetryPromise = getPromise("image_status_label_displayed");
      const userEditPromise = getPromise("user_edit");
      await clickOn(browser, "#newAltTextSave");
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.imageStatusLabelDisplayed,
        [
          {
            label: "added",
          },
        ],
        false
      );
      await userEditPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.userEdit, [
        { total_words: 2, words_removed: 2, words_added: 4 },
      ]);

      // Edit again in order to delete the alt text.
      await clickOn(browser, ".altText.new");
      await SpecialPowers.spawn(browser, [], async () => {
        const textarea = content.document.querySelector(
          "#newAltTextDescriptionTextarea"
        );
        textarea.value = "";
      });

      telemetryPromise = getPromise("image_status_label_displayed");
      await clickOn(browser, "#newAltTextSave");
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.imageStatusLabelDisplayed,
        [
          {
            label: "missing",
          },
        ]
      );

      // Remove the image.
      await clickOn(browser, ".deleteButton");

      await clickOn(browser, `#editorStampAddImage`);

      await SpecialPowers.spawn(browser, [], async () => {
        const { ContentTaskUtils } = ChromeUtils.importESModule(
          "resource://testing-common/ContentTaskUtils.sys.mjs"
        );
        const { document } = content;
        await ContentTaskUtils.waitForCondition(
          () =>
            !!document.querySelector("#newAltTextDescriptionTextarea")?.value,
          "text area must be displayed"
        );
      });
      Services.fog.testResetFOG();

      telemetryPromise = getPromise("image_status_label_displayed");
      const dismissPromise = getPromise("dismiss");
      await clickOn(browser, "#newAltTextNotNow");
      await telemetryPromise;
      await testTelemetryEventExtra(
        Glean.pdfjsImageAltText.imageStatusLabelDisplayed,
        [
          {
            label: "review",
          },
        ],
        false
      );
      await dismissPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.dismiss, [
        {
          alt_text_type: "present",
          flow: "image_add",
        },
      ]);

      await waitForPdfJSClose(browser);
      await SpecialPowers.popPrefEnv();
    }
  );
});

add_task(async function test_telemetry_new_alt_text_count() {
  makePDFJSHandler();

  await BrowserTestUtils.withNewTab(
    { gBrowser, url: "about:blank" },
    async function (browser) {
      await SpecialPowers.pushPrefEnv({
        set: [
          ["pdfjs.annotationEditorMode", 0],
          [altTextPref, true],
          [guessAltTextPref, true],
          [newFlowPref, true],
          [browserMLPref, true],
          [altTextModelDownloadPref, true],
        ],
      });

      await Services.fog.testFlushAllChildren();
      Services.fog.testResetFOG();

      // check that PDF is opened with internal viewer
      await waitForPdfJSAllLayers(browser, TESTROOT + "file_empty_test.pdf", [
        [
          "annotationEditorLayer",
          "annotationLayer",
          "textLayer",
          "canvasWrapper",
        ],
      ]);

      await enableEditor(browser, "Stamp", 0);
      await clickOn(browser, `#editorStampAddImage`);

      await SpecialPowers.spawn(browser, [], async () => {
        const { ContentTaskUtils } = ChromeUtils.importESModule(
          "resource://testing-common/ContentTaskUtils.sys.mjs"
        );
        const { document } = content;
        await ContentTaskUtils.waitForCondition(
          () =>
            !!document.querySelector("#newAltTextDescriptionTextarea")?.value,
          "text area must be displayed"
        );
      });
      Services.fog.testResetFOG();

      telemetryPromise = getPromise("alt_text_edit");
      const aiGenCheckPromise = getPromise("ai_generation_check");
      await clickOn(browser, "#newAltTextCreateAutomaticallyButton");
      await telemetryPromise;

      await Services.fog.testFlushAllChildren();
      let value = Glean.pdfjsImage.altTextEdit.ai_generation.testGetValue();
      Assert.ok(!value, "Should have ai_generation disabled");

      await aiGenCheckPromise;
      await testTelemetryEventExtra(Glean.pdfjsImageAltText.aiGenerationCheck, [
        { status: "false" },
      ]);

      await clickOn(browser, "#newAltTextNotNow");

      // Delete the editor and create a new one but without AI.
      await clickOn(browser, ".deleteButton");
      Services.fog.testResetFOG();

      for (const string of ["Hello", "", "World", "", ""]) {
        info("Adding image with alt text: " + (string || "(empty)"));
        await clickOn(browser, `#editorStampAddImage`);
        await waitForSelector(browser, "#newAltTextDescriptionTextarea");
        await SpecialPowers.spawn(browser, [string], async text => {
          content.document.querySelector(
            "#newAltTextDescriptionTextarea"
          ).value = text;
        });
        await clickOn(browser, `#newAltTextSave`);
      }

      await clickOn(browser, "#printButton");

      await waitForPreviewVisible();
      await hitKey(browser, "VK_ESCAPE");

      await Services.fog.testFlushAllChildren();
      value = Glean.pdfjsImage.added.with_alt_text.testGetValue();
      Assert.equal(value, 2, "Should have 2 images with alt text");

      value = Glean.pdfjsImage.added.with_no_alt_text.testGetValue();
      Assert.equal(value, 3, "Should have 3 images without alt text");

      await waitForPdfJSClose(browser);
      await SpecialPowers.popPrefEnv();
    }
  );
});
