const NATIVE_HOST = "run.lunatunnel.core";
const DEFAULT_STATE = {
  subscriptionUrl: "",
  deviceId: "",
  profiles: [],
  selectedProfile: "",
  connected: false,
  connectedProfile: "",
  localPort: null,
  subscriptionStatus: null,
  configUpdatedAt: null,
  probeResults: [],
  probeUpdatedAt: null,
  lastError: ""
};

let nativePort = null;
let nativeSequence = 0;
const pendingNative = new Map();
let intentionalNativeDisconnect = false;
let operationQueue = Promise.resolve();
let popupCount = 0;
let probeInProgress = false;
let probeCancelled = false;

function serialize(operation) {
  const current = operationQueue.then(operation, operation);
  operationQueue = current.catch(() => {});
  return current;
}

function storageGet(defaults) {
  return new Promise((resolve) => chrome.storage.local.get(defaults, resolve));
}

function storageSet(values) {
  return new Promise((resolve) => chrome.storage.local.set(values, resolve));
}

function proxySet(value) {
  return new Promise((resolve, reject) => {
    chrome.proxy.settings.set({ value, scope: "regular" }, () => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve();
    });
  });
}

function proxyGet() {
  return new Promise((resolve, reject) => {
    chrome.proxy.settings.get({ incognito: false }, (details) => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve(details || {});
    });
  });
}

function proxyClear() {
  return new Promise((resolve) => {
    chrome.proxy.settings.clear({ scope: "regular" }, () => resolve());
  });
}

function privacySet(value) {
  return new Promise((resolve) => {
    const setting = chrome.privacy?.network?.webRTCIPHandlingPolicy;
    if (!setting) return resolve();
    setting.set({ value, scope: "regular" }, () => resolve());
  });
}

function privacyClear() {
  return new Promise((resolve) => {
    const setting = chrome.privacy?.network?.webRTCIPHandlingPolicy;
    if (!setting) return resolve();
    setting.clear({ scope: "regular" }, () => resolve());
  });
}

function friendlyNativeError(message) {
  const value = String(message || "");
  if (/host.*not found|native messaging host/i.test(value)) {
    return "Компонент LunaTunnel Core не установлен. Запустите install.cmd из комплекта LunaTunnel.";
  }
  if (/access.*forbidden|not allowed/i.test(value)) {
    return "Chrome не разрешил запуск LunaTunnel Core. Переустановите Core для этой версии расширения.";
  }
  return value || "LunaTunnel Core неожиданно завершился.";
}

async function markDisconnected(error = "") {
  await Promise.all([proxyClear(), privacyClear()]);
  await storageSet({
    connected: false,
    connectedProfile: "",
    localPort: null,
    lastError: error
  });
  chrome.action.setBadgeText({ text: "" });
}

function rejectPending(error) {
  for (const entry of pendingNative.values()) {
    clearTimeout(entry.timer);
    entry.reject(error);
  }
  pendingNative.clear();
}

function ensureNativePort() {
  if (nativePort) return nativePort;

  intentionalNativeDisconnect = false;
  const port = chrome.runtime.connectNative(NATIVE_HOST);
  nativePort = port;

  port.onMessage.addListener((message) => {
    const id = message?.id;
    const entry = pendingNative.get(id);
    if (!entry) return;
    pendingNative.delete(id);
    clearTimeout(entry.timer);
    if (message.ok) entry.resolve(message.result || {});
    else entry.reject(new Error(message.error || "Ошибка LunaTunnel Core"));
  });

  port.onDisconnect.addListener(() => {
    const reason = friendlyNativeError(chrome.runtime.lastError?.message);
    nativePort = null;
    rejectPending(new Error(reason));
    if (!intentionalNativeDisconnect) {
      storageGet(DEFAULT_STATE).then((state) => {
        if (state.connected) markDisconnected(reason);
      });
    }
    intentionalNativeDisconnect = false;
  });

  return port;
}

function sendNative(action, payload = {}, timeoutMs = 45000) {
  return new Promise((resolve, reject) => {
    const port = ensureNativePort();
    const id = ++nativeSequence;
    const timer = setTimeout(() => {
      pendingNative.delete(id);
      reject(new Error("LunaTunnel Core не ответил вовремя."));
    }, timeoutMs);
    pendingNative.set(id, { resolve, reject, timer });
    try {
      port.postMessage({ id, action, ...payload });
    } catch (error) {
      clearTimeout(timer);
      pendingNative.delete(id);
      reject(new Error(friendlyNativeError(error.message)));
    }
  });
}

async function closeNativePort() {
  if (!nativePort) return;
  intentionalNativeDisconnect = true;
  const port = nativePort;
  nativePort = null;
  try {
    port.disconnect();
  } catch (_) {
    intentionalNativeDisconnect = false;
  }
}

async function setBrowserProxy(port) {
  const control = await proxyGet();
  if (["not_controllable", "controlled_by_other_extensions"].includes(control.levelOfControl)) {
    throw new Error("Прокси Chrome управляется политикой или другим расширением. Отключите другое прокси-расширение и повторите.");
  }
  await proxySet({
    mode: "fixed_servers",
    rules: {
      singleProxy: {
        scheme: "http",
        host: "127.0.0.1",
        port: Number(port)
      },
      bypassList: ["<local>", "localhost", "127.0.0.1", "[::1]"]
    }
  });
  const applied = await proxyGet();
  const configuredPort = applied?.value?.rules?.singleProxy?.port;
  if (applied.levelOfControl !== "controlled_by_this_extension" || Number(configuredPort) !== Number(port)) {
    await proxyClear();
    throw new Error("Chrome не подтвердил применение локального прокси LunaTunnel.");
  }
  await privacySet("disable_non_proxied_udp");
}

async function getState() {
  const state = await storageGet(DEFAULT_STATE);
  if (!state.deviceId) {
    state.deviceId = crypto.randomUUID();
    await storageSet({ deviceId: state.deviceId });
  }
  return state;
}

async function getReconciledState() {
  const state = await getState();
  if (!state.connected) return state;
  try {
    const status = await sendNative("status", {}, 5000);
    if (!status.connected || !status.port) {
      await markDisconnected("Предыдущее подключение уже завершено.");
      await closeNativePort();
      return getState();
    }
    await setBrowserProxy(status.port);
    if (Number(status.port) !== Number(state.localPort)) {
      await storageSet({ localPort: status.port });
    }
    return getState();
  } catch (error) {
    const reason = friendlyNativeError(error?.message);
    await markDisconnected(reason);
    await closeNativePort();
    return getState();
  }
}

async function importSubscription(subscriptionUrl) {
  const state = await getState();
  let result;
  try {
    result = await sendNative("import_subscription", {
      subscription_url: subscriptionUrl,
      device_id: state.deviceId
    });
  } finally {
    if (!state.connected) await closeNativePort();
  }
  const profiles = Array.isArray(result.profiles) ? result.profiles : [];
  if (!profiles.length) throw new Error("В подписке нет доступных VLESS, TUIC или Hysteria 2 серверов.");

  const selectedStillExists = profiles.some((item) => item.tag === state.selectedProfile);
  const selectedProfile = selectedStillExists ? state.selectedProfile : profiles[0].tag;
  await storageSet({
    subscriptionUrl,
    profiles,
    selectedProfile,
    subscriptionStatus: result.subscription_status || null,
    configUpdatedAt: Date.now(),
    probeResults: [],
    probeUpdatedAt: null,
    lastError: ""
  });
  return getState();
}

async function probeProfiles() {
  const state = await getState();
  if (!state.subscriptionUrl || !state.profiles.length) return state;

  probeInProgress = true;
  probeCancelled = false;
  try {
    const result = await sendNative("probe_profiles", {
      subscription_url: state.subscriptionUrl,
      device_id: state.deviceId
    }, 15000);
    if (probeCancelled) return getState();
    await storageSet({
      probeResults: Array.isArray(result.probes) ? result.probes : [],
      probeUpdatedAt: Date.now()
    });
    return getState();
  } catch (error) {
    if (probeCancelled) return getState();
    throw error;
  } finally {
    probeInProgress = false;
    const latest = await storageGet(DEFAULT_STATE);
    if (!latest.connected) await closeNativePort();
  }
}

async function connect() {
  const state = await getState();
  if (!state.subscriptionUrl) throw new Error("Сначала вставьте и загрузите ссылку подписки.");
  if (!state.selectedProfile) throw new Error("Выберите сервер.");

  const result = await sendNative("connect", {
    subscription_url: state.subscriptionUrl,
    device_id: state.deviceId,
    selected_profile: state.selectedProfile
  }, 60000);

  try {
    await setBrowserProxy(result.port);
  } catch (error) {
    try { await sendNative("disconnect"); } catch (_) {}
    throw new Error(`Не удалось назначить прокси в Chrome: ${error.message}`);
  }

  await storageSet({
    profiles: result.profiles || state.profiles,
    connected: true,
    connectedProfile: result.selected_profile || state.selectedProfile,
    localPort: result.port,
    subscriptionStatus: result.subscription_status || state.subscriptionStatus,
    lastError: ""
  });
  chrome.action.setBadgeBackgroundColor({ color: "#7c3aed" });
  chrome.action.setBadgeText({ text: "ON" });
  return getState();
}

async function disconnect() {
  await Promise.all([proxyClear(), privacyClear()]);
  try { await sendNative("disconnect", {}, 10000); } catch (_) {}
  await markDisconnected("");
  await closeNativePort();
  return getState();
}

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  serialize(async () => {
    switch (message?.action) {
      case "get_state":
        return getReconciledState();
      case "import_subscription":
        return importSubscription(String(message.subscriptionUrl || "").trim());
      case "probe_profiles":
        return probeProfiles();
      case "select_profile":
        await storageSet({ selectedProfile: String(message.profile || "") });
        return getState();
      case "connect":
        return connect();
      case "disconnect":
        return disconnect();
      default:
        throw new Error("Неизвестная команда расширения.");
    }
  }).then(
    (state) => sendResponse({ ok: true, state }),
    async (error) => {
      const messageText = friendlyNativeError(error?.message);
      if (popupCount > 0) await storageSet({ lastError: messageText });
      sendResponse({ ok: false, error: messageText });
    }
  );
  return true;
});

chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== "popup-lifecycle") return;
  popupCount += 1;
  port.onDisconnect.addListener(() => {
    popupCount = Math.max(0, popupCount - 1);
    if (popupCount === 0) {
      if (probeInProgress) probeCancelled = true;
      storageGet(DEFAULT_STATE).then((state) => {
        if (!state.connected) closeNativePort();
      });
    }
  });
});

chrome.runtime.onInstalled.addListener(() => {
  markDisconnected("");
});

chrome.runtime.onStartup.addListener(() => {
  markDisconnected("");
});

chrome.runtime.onSuspend.addListener(() => {
  storageGet(DEFAULT_STATE).then((state) => {
    if (!state.connected) closeNativePort();
  });
});
