/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const { topChromeWindow } = window.browsingContext;

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  GenAI: "resource:///modules/GenAI.sys.mjs",
  LightweightThemeConsumer:
    "resource://gre/modules/LightweightThemeConsumer.sys.mjs",
  SpecialMessageActions:
    "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
});
const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);

XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "providerPref",
  "browser.ml.chat.provider",
  null,
  renderProviders
);
XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "shortcutsPref",
  "browser.ml.chat.shortcuts"
);
XPCOMUtils.defineLazyPreferenceGetter(
  lazy,
  "sidebarRevampPref",
  "sidebar.revamp"
);

ChromeUtils.defineLazyGetter(
  lazy,
  "supportLink",
  () =>
    Services.urlFormatter.formatURLPref("app.support.baseURL") + "ai-chatbot"
);

const node = {};

function closeSidebar() {
  topChromeWindow.SidebarController.hide();
}

function openLink(url) {
  topChromeWindow.openLinkIn(url, "tabshifted", {
    triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
  });
}

function request(url = lazy.providerPref) {
  try {
    node.chat.fixupAndLoadURIString(url, {
      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
        {}
      ),
    });
  } catch (ex) {
    console.error("Failed to request chat provider", ex);
  }
}

function renderChat() {
  const browser = document.createXULElement("browser");
  browser.setAttribute("disableglobalhistory", "true");
  browser.setAttribute("maychangeremoteness", "true");
  browser.setAttribute("nodefaultsrc", "true");
  browser.setAttribute("remote", "true");
  browser.setAttribute("type", "content");
  return document.body.appendChild(browser);
}

async function renderProviders() {
  // Skip potential pref change callback when unloading
  if ((await document.visibilityState) == "hidden") {
    return null;
  }

  const select = document.getElementById("provider");
  select.innerHTML = "";
  let selected = false;

  const addOption = (text = "", val = "") => {
    const option = select.appendChild(document.createElement("option"));
    option.textContent = text;
    option.value = val;
    return option;
  };

  // Add the known providers in order while looking for current selection
  lazy.GenAI.chatProviders.forEach((data, url) => {
    const option = addOption(data.name, url);
    if (lazy.providerPref == url) {
      option.selected = true;
      selected = true;
    } else if (data.hidden) {
      option.hidden = true;
    }
  });

  // Must be a custom preference if provider wasn't found
  if (!selected) {
    const option = addOption(lazy.providerPref, lazy.providerPref);
    option.selected = true;
    if (!lazy.providerPref) {
      showOnboarding();
    }
  }

  // Add extra controls after the providers
  select.appendChild(document.createElement("hr"));
  document.l10n.setAttributes(addOption(), "genai-provider-view-details");

  // Update provider telemetry
  const providerId = lazy.GenAI.getProviderId(lazy.providerPref);
  Glean.genaiChatbot.provider.set(providerId);
  if (renderProviders.lastId && document.hasFocus()) {
    Glean.genaiChatbot.providerChange.record({
      current: providerId,
      previous: renderProviders.lastId,
      surface: "panel",
    });
  }
  renderProviders.lastId = providerId;

  // Load the requested provider
  request();
  return select;
}

function renderMore() {
  const button = document.getElementById("header-more");
  button.addEventListener("click", () => {
    const topDoc = topChromeWindow.document;
    let menu = topDoc.getElementById("chatbot-menupopup");
    if (!menu) {
      menu = topDoc
        .getElementById("mainPopupSet")
        .appendChild(topDoc.createXULElement("menupopup"));
      menu.id = "chatbot-menupopup";
      node.menu = menu;
      menu.addEventListener("popuphidden", () => {
        button.setAttribute("aria-expanded", false);
      });
    }
    menu.innerHTML = "";

    const provider = lazy.GenAI.chatProviders.get(lazy.providerPref)?.name;
    const providerId = lazy.GenAI.getProviderId();
    [
      [
        "menuitem",
        [
          provider
            ? "genai-options-reload-provider"
            : "genai-options-reload-generic",
          { provider },
        ],
        function reload() {
          request();
        },
      ],
      ["menuseparator"],
      [
        "menuitem",
        ["genai-options-show-shortcut"],
        function show_shortcuts() {
          Services.prefs.setBoolPref("browser.ml.chat.shortcuts", true);
        },
        lazy.shortcutsPref,
      ],
      [
        "menuitem",
        ["genai-options-hide-shortcut"],
        function hide_shortcuts() {
          Services.prefs.setBoolPref("browser.ml.chat.shortcuts", false);
        },
        !lazy.shortcutsPref,
      ],
      ["menuseparator"],
      [
        "menuitem",
        ["genai-options-about-chatbot"],
        function about() {
          openLink(lazy.supportLink);
        },
      ],
    ].forEach(([type, l10n, command, checked]) => {
      const item = menu.appendChild(topDoc.createXULElement(type));
      if (type == "menuitem") {
        document.l10n.setAttributes(item, ...l10n);
        item.addEventListener("command", () => {
          command();
          Glean.genaiChatbot.sidebarMoreMenuClick.record({
            action: command.name,
            provider: providerId,
          });
        });
        if (checked) {
          item.setAttribute("checked", true);
        }
      }
    });
    menu.openPopup(button, "after_start");
    button.setAttribute("aria-expanded", true);
    Glean.genaiChatbot.sidebarMoreMenuDisplay.record({ provider: providerId });
  });
}

function handleChange({ target }) {
  const { value } = target;
  switch (target) {
    case node.provider:
      // Special behavior to show first screen of onboarding
      if (value == "") {
        target.value = lazy.providerPref;
        showOnboarding(1);
        Glean.genaiChatbot.sidebarProviderMenuClick.record({
          action: "details",
          provider: lazy.GenAI.getProviderId(),
        });
      } else {
        Services.prefs.setStringPref("browser.ml.chat.provider", value);
      }
      break;
  }
}
addEventListener("change", handleChange);

// Expose a promise for loading and rendering the chat browser element
var browserPromise = new Promise((resolve, reject) => {
  addEventListener("load", async () => {
    new lazy.LightweightThemeConsumer(document);
    try {
      node.chat = renderChat();
      node.provider = await renderProviders();
      renderMore();
      resolve(node.chat);
      document.getElementById("header-close").addEventListener("click", () => {
        closeSidebar();
        Glean.genaiChatbot.sidebarCloseClick.record({
          provider: lazy.GenAI.getProviderId(),
        });
      });
    } catch (ex) {
      console.error("Failed to render on load", ex);
      reject(ex);
    }

    Glean.genaiChatbot.sidebarToggle.record({
      opened: true,
      provider: lazy.GenAI.getProviderId(),
      reason: "load",
      version: lazy.sidebarRevampPref ? "new" : "old",
    });
  });
});

addEventListener("unload", () => {
  node.menu?.remove();
  Glean.genaiChatbot.sidebarToggle.record({
    opened: false,
    provider: lazy.GenAI.getProviderId(),
    reason: "unload",
    version: lazy.sidebarRevampPref ? "new" : "old",
  });
});

/**
 * Show onboarding screens
 *
 * @param {number} length optional show fewer screens
 */
function showOnboarding(length) {
  const ACTIONS = Object.freeze({
    OPEN_URL: "OPEN_URL",
    CHATBOT_SELECT: "chatbot:select",
    CHATBOT_PERSIST: "chatbot:persist",
    CHATBOT_REVERT: "chatbot:revert",
  });

  // Insert onboarding container and render with script
  const root = document.createElement("div");
  root.id = "multi-stage-message-root";
  document.getElementById(root.id)?.remove();
  document.body.prepend(root);
  history.replaceState("", "");
  const script = document.head.appendChild(document.createElement("script"));
  script.src = "chrome://browser/content/aboutwelcome/aboutwelcome.bundle.js";

  // Convert provider data for lookup by id
  const providerConfigs = new Map();
  lazy.GenAI.chatProviders.forEach((data, url) => {
    if (!data.hidden) {
      providerConfigs.set(data.id, { ...data, url });
    }
  });

  // Define various AW* functions to control aboutwelcome bundle behavior
  Object.assign(window, {
    AWEvaluateScreenTargeting(screens) {
      return screens;
    },
    AWFinish() {
      if (lazy.providerPref == "") {
        closeSidebar();
      }
      root.remove();
    },
    AWGetFeatureConfig() {
      return {
        id: "chatbot",
        template: "multistage",
        transitions: true,
        screens: [
          {
            id: "chat_pick",
            content: {
              fullscreen: true,
              hide_secondary_section: "responsive",
              narrow: true,
              position: "split",

              title: {
                fontWeight: 400,
                string_id: "genai-onboarding-header",
              },
              cta_paragraph: {
                text: {
                  string_id: "genai-onboarding-description",
                  string_name: "learn-more",
                },
                action: {
                  data: {
                    args: lazy.supportLink,
                    where: "tabshifted",
                  },
                  type: ACTIONS.OPEN_URL,
                },
              },
              tiles: {
                action: { picker: "<event>" },
                data: [...providerConfigs.values()].map(config => ({
                  action: { type: ACTIONS.CHATBOT_SELECT, config },
                  id: config.id,
                  label: config.name,
                  tooltip: { string_id: config.tooltipId },
                })),
                // Default to nothing selected
                selected: " ",
                type: "single-select",
              },
              above_button_content: [
                // Placeholder to inject on provider change
                {
                  text: " ",
                  type: "text",
                },
              ],
              primary_button: {
                action: {
                  navigate: true,
                  type: ACTIONS.CHATBOT_PERSIST,
                },
                label: { string_id: "genai-onboarding-primary" },
              },
              additional_button: {
                action: { dismiss: true, type: ACTIONS.CHATBOT_REVERT },
                label: { string_id: "genai-onboarding-secondary" },
                style: "link",
              },
              progress_bar: true,
            },
          },
          {
            id: "chat_suggest",
            content: {
              fullscreen: true,
              hide_secondary_section: "responsive",
              narrow: true,
              position: "split",

              title: {
                fontWeight: 400,
                string_id: "genai-onboarding-select-header",
              },
              subtitle: { string_id: "genai-onboarding-select-description" },
              above_button_content: [
                {
                  height: "172px",
                  type: "image",
                  width: "307px",
                },
                {
                  text: " ",
                  type: "text",
                },
              ],
              primary_button: {
                action: { navigate: true },
                label: { string_id: "genai-onboarding-select-primary" },
              },
              progress_bar: true,
            },
          },
        ].slice(0, length),
      };
    },
    AWGetInstalledAddons() {},
    AWGetSelectedTheme() {
      document.querySelector(".primary").disabled = true;

      // Specially handle links to open out of the sidebar
      const handleLink = ev => {
        const { href } = ev.target;
        if (href) {
          ev.preventDefault();
          openLink(href);
        }
      };
      const links = document.querySelector(".link-paragraph");
      links.addEventListener("click", handleLink);

      [...document.querySelectorAll("fieldset label")].forEach(label => {
        // Add content that is hidden with 0 height until selected
        const div = label
          .querySelector(".text")
          .appendChild(document.createElement("div"));
        div.style.maxHeight = 0;
        div.tabIndex = -1;
        const ul = div.appendChild(document.createElement("ul"));
        const config = providerConfigs.get(label.querySelector("input").value);
        config.choiceIds?.forEach(id => {
          const li = ul.appendChild(document.createElement("li"));
          document.l10n.setAttributes(li, id);
        });
        if (config.learnLink && config.learnId) {
          const a = div.appendChild(document.createElement("a"));
          a.href = config.learnLink;
          a.tabIndex = -1;
          a.addEventListener("click", ev => {
            handleLink(ev);
            Glean.genaiChatbot.onboardingProviderLearn.record({
              provider: config.id,
              step: 1,
            });
          });
          document.l10n.setAttributes(a, config.learnId);
        }
      });
    },
    AWSendEventTelemetry({ event, event_context: { source }, message_id }) {
      const { provider } = window.AWSendEventTelemetry;
      const step = message_id.match(/chat_pick/) ? 1 : 2;
      switch (true) {
        case step == 1 && event == "IMPRESSION":
          Glean.genaiChatbot.onboardingProviderChoiceDisplayed.record({
            provider: lazy.GenAI.getProviderId(lazy.providerPref),
            step,
          });
          break;
        case step == 1 && source == "cta_paragraph":
          Glean.genaiChatbot.onboardingLearnMore.record({ provider, step });
          break;
        case step == 1 && source == "primary_button":
          Glean.genaiChatbot.onboardingContinue.record({ provider, step });
          break;
        case step == 1 && source == "additional_button":
          Glean.genaiChatbot.onboardingClose.record({ provider, step });
          break;
        case step == 1 && source.startsWith("link"):
          Glean.genaiChatbot.onboardingProviderTerms.record({
            provider,
            step,
            text: source,
          });
          break;
        // Assume generic click not yet handled above single select of provider
        case step == 1 && event == "CLICK_BUTTON":
          window.AWSendEventTelemetry.provider = source;
          Glean.genaiChatbot.onboardingProviderSelection.record({
            provider: source,
            step,
          });
          break;
        case step == 2 && event == "IMPRESSION":
          Glean.genaiChatbot.onboardingTextHighlightDisplayed.record({
            provider,
            step,
          });
          break;
        case step == 2 && source == "primary_button":
          Glean.genaiChatbot.onboardingFinish.record({ provider, step });
          break;
      }
    },
    AWSendToParent(_message, action) {
      switch (action.type) {
        case ACTIONS.OPEN_URL:
          lazy.SpecialMessageActions.handleAction(action, topChromeWindow);
          return;
        case ACTIONS.CHATBOT_PERSIST: {
          const { value } = document.querySelector(
            "label:has(.selected) input"
          );
          Services.prefs.setStringPref(
            "browser.ml.chat.provider",
            providerConfigs.get(value).url
          );
          break;
        }
        case ACTIONS.CHATBOT_REVERT: {
          request();
          break;
        }
        // Handle single select provider choice
        case ACTIONS.CHATBOT_SELECT: {
          const { config } = action;
          if (!config) {
            break;
          }

          request(config.url);
          document.querySelector(".primary").disabled = false;

          // Set max-height to trigger transition
          document.querySelectorAll("label .text div").forEach(div => {
            const selected =
              div.closest("label").querySelector("input").value == config.id;
            div.style.maxHeight = selected ? div.scrollHeight + "px" : 0;
            const a = div.querySelector("a");
            if (a) {
              a.tabIndex = selected ? 0 : -1;
            }
          });

          // Update potentially multiple links for the provider
          const links = document.querySelector(".link-paragraph");
          if (links.dataset.l10nId != config.linksId) {
            links.innerHTML = "";
            for (let i = 1; i <= 3; i++) {
              const link = links.appendChild(document.createElement("a"));
              const name = (link.dataset.l10nName = `link${i}`);
              link.href = config[name];
              link.setAttribute("value", name);
            }
            document.l10n.setAttributes(links, config.linksId);
          }

          break;
        }
      }
    },
  });
}
