import type { PageStatus, RuntimeMessage } from "../shared/types";
import "./style.css";

const statusPanel = document.querySelector<HTMLElement>("#status-panel");
const definitionLabel = document.querySelector<HTMLElement>("#definition");
const pauseButton = document.querySelector<HTMLButtonElement>("#pause");
const settingsButton = document.querySelector<HTMLButtonElement>("#settings");

let activeTabId: number | undefined;
let currentStatus: PageStatus | undefined;

void loadStatus();

pauseButton?.addEventListener("click", async () => {
  if (activeTabId === undefined || !currentStatus?.definition || !pauseButton) return;
  pauseButton.disabled = true;
  try {
    const response = await chrome.runtime.sendMessage({
      type: "SET_DEFINITION_PAUSED",
      definitionId: currentStatus.definition.id,
      paused: !currentStatus.paused
    } satisfies RuntimeMessage);
    if (response?.error) throw new Error(response.error);
    await loadStatus();
  } catch (error) {
    showError(error instanceof Error ? error.message : String(error));
    pauseButton.disabled = false;
  }
});

settingsButton?.addEventListener("click", () => void chrome.runtime.openOptionsPage());

async function loadStatus(): Promise<void> {
  setLoading(true);
  try {
    // Firefox exposes the `chrome` namespace with callback-style APIs in some
    // versions. Supplying a callback keeps this working there as well as in
    // browsers that return a Promise when no callback is supplied.
    const [tab] = await queryTabs({ active: true, currentWindow: true });
    activeTabId = tab?.id;
    console.debug("[VibeGuard popup] Active tab selected", { id: tab?.id, url: tab?.url, status: tab?.status });
    if (activeTabId === undefined) {
      console.warn("[VibeGuard popup] Active tab has no usable ID", { tab });
      showUnsupported("The active tab is unavailable.");
      return;
    }
    console.debug("[VibeGuard popup] Requesting page status", { tabId: activeTabId });
    const response = await sendTabMessage(activeTabId, { type: "GET_PAGE_STATUS" } satisfies RuntimeMessage);
    console.debug("[VibeGuard popup] Page status response", { tabId: activeTabId, response });
    if (!response?.status) throw new Error("No page status was returned.");
    renderStatus(response.status);
  } catch (error) {
    console.warn("[VibeGuard popup] Unable to read page status", { tabId: activeTabId, error });
    showUnsupported("VibeGuard is not available on this page.");
  } finally {
    setLoading(false);
  }
}

function queryTabs(query: chrome.tabs.QueryInfo): Promise<chrome.tabs.Tab[]> {
  return new Promise((resolve, reject) => {
    chrome.tabs.query(query, (tabs) => {
      const error = chrome.runtime.lastError;
      if (error) {
        reject(new Error(error.message));
        return;
      }
      resolve(tabs);
    });
  });
}

function sendTabMessage(tabId: number, message: RuntimeMessage): Promise<{ type?: string; status?: PageStatus } | undefined> {
  return sendTabMessageOnce(tabId, message).catch(async (error: unknown) => {
    // A tab that was already open when the extension was loaded may not have
    // received the content script yet. Firefox reports that as a missing
    // receiving end; inject the MV2 bundle and retry once.
    if (!isMissingReceiver(error) || typeof chrome.tabs.executeScript !== "function") throw error;
    await executeContentScript(tabId);
    return sendTabMessageOnce(tabId, message);
  });
}

function sendTabMessageOnce(tabId: number, message: RuntimeMessage): Promise<{ type?: string; status?: PageStatus } | undefined> {
  return new Promise((resolve, reject) => {
    chrome.tabs.sendMessage(tabId, message, (response) => {
      const error = chrome.runtime.lastError;
      if (error) {
        reject(new Error(error.message));
        return;
      }
      resolve(response as { type?: string; status?: PageStatus } | undefined);
    });
  });
}

function executeContentScript(tabId: number): Promise<void> {
  return new Promise((resolve, reject) => {
    chrome.tabs.executeScript(tabId, { file: "content.js" }, () => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve();
    });
  });
}

function isMissingReceiver(error: unknown): boolean {
  return error instanceof Error && error.message.includes("Receiving end does not exist");
}

function renderStatus(status: PageStatus): void {
  console.debug("[VibeGuard popup] Rendering page status", status);
  currentStatus = status;
  if (!status.supported || !status.definition) {
    showUnsupported("No supported site definition matches this page.");
    return;
  }
  statusPanel!.className = `notification ${status.paused ? "is-warning" : "is-success"}`;
  statusPanel!.textContent = status.paused ? "Filtering is paused" : "Filtering is active";
  definitionLabel!.textContent = `Site definition: ${status.definition.name}`;
  if (pauseButton) {
    pauseButton.hidden = false;
    pauseButton.disabled = false;
    pauseButton.textContent = status.paused ? "Resume on this site" : "Pause on this site";
  }
}

function showUnsupported(message: string): void {
  currentStatus = undefined;
  statusPanel!.className = "notification is-light";
  statusPanel!.textContent = message;
  definitionLabel!.textContent = "";
  if (pauseButton) {
    pauseButton.hidden = true;
    pauseButton.disabled = true;
  }
}

function showError(message: string): void {
  statusPanel!.className = "notification is-danger is-light";
  statusPanel!.textContent = message;
}

function setLoading(loading: boolean): void {
  if (loading) {
    statusPanel!.className = "notification is-light";
    statusPanel!.textContent = "Checking this page…";
  }
  if (pauseButton) pauseButton.disabled = loading;
}
