"use strict";

importScripts(
  "shared/common.js",
  "shared/update-utils.js",
  "shared/scene-db.js",
  "shared/tvmaze-client.js",
  "shared/tmdb-website-client.js",
  "shared/commonsense-client.js",
  "shared/doesthedogdie-client.js",
  "shared/videoskip-client.js",
  "shared/filmiway-client.js",
  "shared/cleanstream-client.js",
  "shared/skipit-client.js",
  "shared/resolver.js"
);

var Common = globalThis.SafeSceneCommon;
var UpdateUtils = globalThis.SafeSceneUpdateUtils;
var SceneDb = globalThis.SafeSceneDatabase;
var TVMaze = globalThis.SafeSceneTVMaze;
var TMDBWebsite = globalThis.SafeSceneTMDBWebsite;
var CommonSenseMedia = globalThis.SafeSceneCommonSenseMedia;
var DoesTheDogDie = globalThis.SafeSceneDoesTheDogDie;
var VideoSkip = globalThis.SafeSceneVideoSkip;
var Filmiway = globalThis.SafeSceneFilmiway;
var CleanStream = globalThis.SafeSceneCleanStream;
var Skipit = globalThis.SafeSceneSkipit;
var Resolver = globalThis.SafeSceneResolver;
var tabStates = new Map();
var sceneDatabasePromise = null;
var resolutionCache = new Map();
var resolutionInflight = new Map();
var tvMazeClient = new TVMaze.TVMazeClient(fetch.bind(globalThis));
var tmdbWebsiteClient = new TMDBWebsite.TMDBWebsiteClient(fetch.bind(globalThis));
var commonSenseMediaClient = new CommonSenseMedia.CommonSenseMediaClient(fetch.bind(globalThis));
var doesTheDogDieClient = new DoesTheDogDie.DoesTheDogDieClient(fetch.bind(globalThis));
var videoSkipClient = new VideoSkip.VideoSkipClient(fetch.bind(globalThis));
var filmiwayClient = new Filmiway.FilmiwayClient(fetch.bind(globalThis));
var cleanStreamClient = new CleanStream.CleanStreamClient(fetch.bind(globalThis));
var skipitClient = new Skipit.SkipitClient(fetch.bind(globalThis));
var RESOLUTION_CACHE_TTL_MS = 2 * 60 * 1000;
var UPDATE_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
var BACKEND_API_BASE_URL = "https://ta3meya.bals.pro/api";
var updateStateCache = null;

try {
  importScripts("vendor/skipit-auth.js");
} catch (error) {
  console.warn("SafeScene auth helper unavailable:", error);
}

async function ensureDefaultSettings() {
  var stored = await chrome.storage.sync.get("settings");
  if (!stored.settings) {
    await chrome.storage.sync.set({
      settings: Common.clone(Common.DEFAULT_SETTINGS)
    });
  }
}

async function getSettings() {
  var stored = await chrome.storage.sync.get("settings");
  return Object.assign({}, Common.DEFAULT_SETTINGS, stored.settings || {});
}

async function loadSceneDatabase() {
  if (!sceneDatabasePromise) {
    sceneDatabasePromise = fetch(chrome.runtime.getURL("src/data/scene-db.json"))
      .then(function toJson(response) {
        return response.json();
      })
      .then(function toDb(dataset) {
        return SceneDb.createSceneDatabase(dataset);
      });
  }

  return sceneDatabasePromise;
}

async function getSkipitAuthToken() {
  var authApi = globalThis.SafeSceneSkipitAuth;

  if (!authApi || typeof authApi.getAuthToken !== "function") {
    return null;
  }

  try {
    return await authApi.getAuthToken();
  } catch (error) {
    console.warn("SafeScene auth lookup failed:", error);
    return null;
  }
}

async function getProviderStatus() {
  return {
    skipitConnected: Boolean(await getSkipitAuthToken())
  };
}

async function fetchBackendJson(pathname) {
  var response = await fetch(BACKEND_API_BASE_URL + pathname, {
    headers: {
      "accept": "application/json",
      "x-ta3meya-extension-id": chrome.runtime.id,
      "x-ta3meya-extension-version": chrome.runtime.getManifest().version
    }
  });

  if (!response.ok) {
    throw new Error("Backend request failed: " + response.status + " " + pathname);
  }

  return response.json();
}

async function getUpdateInfo(forceRefresh) {
  var now = Date.now();

  if (!forceRefresh && updateStateCache && updateStateCache.expiresAt > now) {
    return updateStateCache.value;
  }

  var manifestVersion = chrome.runtime.getManifest().version;

  try {
    var payload = await fetchBackendJson("/update");
    var latestVersion = payload && payload.latestVersion ? String(payload.latestVersion) : manifestVersion;
    var websiteUrl = payload && payload.websiteUrl ? String(payload.websiteUrl) : "https://ta3meya.bals.pro";
    var hasUpdate = UpdateUtils.isRemoteVersionNewer(manifestVersion, latestVersion);
    var updateInfo = {
      hasUpdate: hasUpdate,
      currentVersion: manifestVersion,
      latestVersion: latestVersion,
      websiteUrl: websiteUrl,
      message: payload && payload.message ? String(payload.message) : "There's an update. Please go to the website.",
      checkedAt: payload && payload.checkedAt ? payload.checkedAt : new Date().toISOString()
    };

    updateStateCache = {
      value: updateInfo,
      expiresAt: now + UPDATE_CACHE_TTL_MS
    };

    return updateInfo;
  } catch (error) {
    var fallbackValue = updateStateCache ? updateStateCache.value : {
      hasUpdate: false,
      currentVersion: manifestVersion,
      latestVersion: manifestVersion,
      websiteUrl: "https://ta3meya.bals.pro",
      message: "There's an update. Please go to the website.",
      checkedAt: null,
      error: error && error.message ? error.message : String(error)
    };

    updateStateCache = {
      value: fallbackValue,
      expiresAt: now + (15 * 60 * 1000)
    };

    return fallbackValue;
  }
}

function getSettingsCacheKey(settings) {
  return (settings && Array.isArray(settings.skipCategories) ? settings.skipCategories : [])
    .map(Common.normalizeText)
    .sort()
    .join(",");
}

function getAuthCacheKey(token) {
  return token ? "auth:" + String(token).slice(-12) : "guest";
}

function buildResolutionCacheKey(pageContext, settings, token) {
  return JSON.stringify({
    hostname: pageContext && pageContext.hostname ? pageContext.hostname : "",
    pathname: pageContext && pageContext.pathname ? pageContext.pathname : "",
    title: pageContext && pageContext.title ? pageContext.title : "",
    ogTitle: pageContext && pageContext.ogTitle ? pageContext.ogTitle : "",
    twitterTitle: pageContext && pageContext.twitterTitle ? pageContext.twitterTitle : "",
    heading: pageContext && pageContext.heading ? pageContext.heading : "",
    settings: getSettingsCacheKey(settings),
    auth: getAuthCacheKey(token)
  });
}

function cloneResolutionWithMeta(resolution, meta) {
  var cloned = Common.clone(resolution || {});
  cloned.meta = Object.assign({}, cloned.meta || {}, meta || {});
  return cloned;
}

async function broadcastSettings(settings) {
  var tabs = await chrome.tabs.query({});
  var sendPromises = tabs.map(function mapTab(tab) {
    return chrome.tabs.sendMessage(tab.id, {
      type: "settingsChanged",
      settings: settings
    }).catch(function ignoreMissingReceiver() {
      return null;
    });
  });

  await Promise.all(sendPromises);
}

async function updateBadge(tabId, resolution) {
  if (!tabId) {
    return;
  }

  var count = 0;

  if (resolution && resolution.sceneMatch) {
    if (Array.isArray(resolution.sceneMatch.exactRanges) && resolution.sceneMatch.exactRanges.length) {
      count = resolution.sceneMatch.exactRanges.length;
    } else {
      count = Number(resolution.sceneMatch.matchingSceneCount || 0);
    }
  }

  await chrome.action.setBadgeBackgroundColor({
    color: count > 0 ? "#0f6bff" : "#666666",
    tabId: tabId
  });

  await chrome.action.setBadgeText({
    text: count > 0 ? String(count) : "",
    tabId: tabId
  });
}

async function resolveEpisodeForPage(pageContext, sender) {
  var sceneDatabase = await loadSceneDatabase();
  var settings = await getSettings();
  var skipitAuthToken = await getSkipitAuthToken();
  var cacheKey = buildResolutionCacheKey(pageContext, settings, skipitAuthToken);
  var now = Date.now();
  var cachedEntry = resolutionCache.get(cacheKey);
  var resolution = null;

  if (cachedEntry && cachedEntry.expiresAt > now) {
    resolution = cloneResolutionWithMeta(cachedEntry.resolution, {
      cacheHit: true,
      resolvedAt: cachedEntry.resolvedAt
    });
  } else {
    if (!resolutionInflight.has(cacheKey)) {
      resolutionInflight.set(cacheKey, (async function resolveWithFallback() {
        try {
          var backendResponse = await fetch(BACKEND_API_BASE_URL + "/resolve-episode", {
            method: "POST",
            headers: {
              "content-type": "application/json",
              "x-ta3meya-extension-id": chrome.runtime.id,
              "x-ta3meya-extension-version": chrome.runtime.getManifest().version
            },
            body: JSON.stringify({
              pageContext: pageContext,
              settings: settings,
              skipitAuthToken: skipitAuthToken
            })
          });

          if (!backendResponse.ok) {
            throw new Error("Backend resolve failed: " + backendResponse.status);
          }

          var backendPayload = await backendResponse.json();
          if (!backendPayload || !backendPayload.ok || !backendPayload.resolution) {
            throw new Error("Backend resolve returned no resolution.");
          }

          return cloneResolutionWithMeta(backendPayload.resolution, {
            backendUsed: true,
            backendSource: BACKEND_API_BASE_URL
          });
        } catch (backendError) {
          var localResolution = await Resolver.resolveEpisode({
            pageContext: pageContext,
            sceneDatabase: sceneDatabase,
            tvMazeClient: tvMazeClient,
            tmdbWebsiteClient: tmdbWebsiteClient,
            commonSenseMediaClient: commonSenseMediaClient,
            doesTheDogDieClient: doesTheDogDieClient,
            videoSkipClient: videoSkipClient,
            filmiwayClient: filmiwayClient,
            cleanStreamClient: cleanStreamClient,
            skipitClient: skipitClient,
            settings: settings,
            skipitAuthToken: skipitAuthToken
          });

          return cloneResolutionWithMeta(localResolution, {
            backendUsed: false,
            backendError: backendError && backendError.message ? backendError.message : String(backendError)
          });
        }
      }()).then(function cacheResolved(freshResolution) {
        resolutionCache.set(cacheKey, {
          resolution: freshResolution,
          resolvedAt: new Date().toISOString(),
          expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS
        });
        return freshResolution;
      }).finally(function clearInflight() {
        resolutionInflight.delete(cacheKey);
      }));
    }

    var freshResolution = await resolutionInflight.get(cacheKey);
    var resolvedEntry = resolutionCache.get(cacheKey);
    resolution = cloneResolutionWithMeta(freshResolution, {
      cacheHit: false,
      resolvedAt: resolvedEntry ? resolvedEntry.resolvedAt : new Date().toISOString()
    });
  }

  if (sender && sender.tab && sender.tab.id) {
    tabStates.set(sender.tab.id, {
      pageContext: pageContext,
      resolution: resolution,
      cacheKey: cacheKey,
      updatedAt: new Date().toISOString()
    });
    await updateBadge(sender.tab.id, resolution);
  }

  return resolution;
}

chrome.runtime.onInstalled.addListener(function onInstalled() {
  ensureDefaultSettings().catch(function logError(error) {
    console.error("SafeScene install error:", error);
  });
  getUpdateInfo(true).catch(function ignore() {
    return null;
  });
});

chrome.tabs.onRemoved.addListener(function onRemoved(tabId) {
  tabStates.delete(tabId);
});

chrome.runtime.onMessage.addListener(function onMessage(message, sender, sendResponse) {
  (async function handleMessage() {
    if (message.type === "getSettings") {
      sendResponse({
        settings: await getSettings(),
        providerStatus: await getProviderStatus(),
        updateInfo: await getUpdateInfo(false)
      });
      return;
    }

    if (message.type === "saveSettings") {
      var mergedSettings = Object.assign({}, await getSettings(), message.settings || {});
      await chrome.storage.sync.set({ settings: mergedSettings });
      await broadcastSettings(mergedSettings);
      sendResponse({
        settings: mergedSettings
      });
      return;
    }

    if (message.type === "resolveEpisode") {
      sendResponse({
        settings: await getSettings(),
        providerStatus: await getProviderStatus(),
        updateInfo: await getUpdateInfo(false),
        resolution: await resolveEpisodeForPage(message.pageContext || {}, sender)
      });
      return;
    }

    if (message.type === "getProviderStatus") {
      sendResponse({
        providerStatus: await getProviderStatus()
      });
      return;
    }

    if (message.type === "openSkipitConnect") {
      await chrome.tabs.create({
        url: "https://getskipit.com/extension-auth"
      });
      sendResponse({ ok: true });
      return;
    }

    if (message.type === "openUpdateWebsite") {
      var updateInfo = await getUpdateInfo(false);
      await chrome.tabs.create({
        url: updateInfo.websiteUrl || "https://ta3meya.bals.pro"
      });
      sendResponse({ ok: true });
      return;
    }

    if (message.type === "stateUpdated") {
      if (sender && sender.tab && sender.tab.id) {
        tabStates.set(sender.tab.id, {
          pageContext: message.pageContext || null,
          resolution: message.resolution || null,
          contentState: message.state || null,
          updatedAt: new Date().toISOString()
        });
      }
      sendResponse({ ok: true });
      return;
    }

    if (message.type === "getPopupState") {
      var tabs = await chrome.tabs.query({
        active: true,
        currentWindow: true
      });
      var activeTab = tabs[0] || null;
      var popupState = activeTab ? tabStates.get(activeTab.id) || null : null;

      if (activeTab) {
        try {
          var contentState = await chrome.tabs.sendMessage(activeTab.id, { type: "getState" });
          popupState = contentState || popupState;
        } catch (error) {
          // No content script receiver on the current tab.
        }
      }

      sendResponse({
        activeTab: activeTab ? {
          id: activeTab.id,
          title: activeTab.title || "",
          url: activeTab.url || ""
        } : null,
        state: popupState,
        settings: await getSettings(),
        providerStatus: await getProviderStatus(),
        updateInfo: await getUpdateInfo(false)
      });
      return;
    }

    if (message.type === "refreshActiveTab") {
      var activeTabs = await chrome.tabs.query({
        active: true,
        currentWindow: true
      });
      if (activeTabs[0]) {
        await chrome.tabs.sendMessage(activeTabs[0].id, { type: "refreshDetection" }).catch(function ignore() {
          return null;
        });
      }
      sendResponse({ ok: true });
      return;
    }

    sendResponse({ ok: false, error: "Unknown message type." });
  }()).catch(function reportError(error) {
    sendResponse({
      ok: false,
      error: error && error.message ? error.message : String(error)
    });
  });

  return true;
});
