import { api } from "../../scripts/api.js";
import { ensureLmStyles } from "./lm_styles_loader.js";

const LICENSE_ICON_PATH = "/loras_static/images/tabler/";
const LICENSE_FLAG_BITS = {
  allowNoCredit: 1 << 0,
  allowOnImages: 1 << 1,
  allowOnCivitai: 1 << 2,
  allowRental: 1 << 3,
  allowSellingModels: 1 << 4,
  allowDerivatives: 1 << 5,
  allowRelicense: 1 << 6,
};

// ── Set 1 (classic) icon definitions ──

const LICENSE_ICON_COPY = {
  credit: "Creator credit required",
  image: "No selling generated content",
  rentcivit: "No Civitai generation",
  rent: "No generation services",
  sell: "No selling models",
  derivatives: "No sharing merges",
  relicense: "Same permissions required",
};

const COMMERCIAL_ICON_CONFIG = [
  { bit: LICENSE_FLAG_BITS.allowOnImages, icon: "photo-off.svg", label: LICENSE_ICON_COPY.image },
  { bit: LICENSE_FLAG_BITS.allowOnCivitai, icon: "brush-off.svg", label: LICENSE_ICON_COPY.rentcivit },
  { bit: LICENSE_FLAG_BITS.allowRental, icon: "world-off.svg", label: LICENSE_ICON_COPY.rent },
  { bit: LICENSE_FLAG_BITS.allowSellingModels, icon: "shopping-cart-off.svg", label: LICENSE_ICON_COPY.sell },
];

// ── Set 2 (new CivitAI-style) icon definitions ──

const LNI = LICENSE_ICON_PATH; // alias for brevity

const NEW_LICENSE_ICON_COPY = {
  commercial: { allowed: "Commercial use allowed", denied: "No commercial use" },
  genServices: { allowed: "Generation services allowed", denied: "No generation services" },
  credit: { allowed: "No credit required", denied: "Creator credit required" },
  derivatives: { allowed: "Merges allowed", denied: "No merges allowed" },
  relicense: { allowed: "Different permissions allowed on merges", denied: "Same permissions required on merges" },
};

const NEW_ICON_CONFIG = [
  {
    bitCombo: [LICENSE_FLAG_BITS.allowOnImages, LICENSE_FLAG_BITS.allowSellingModels],
    icon: "currency-dollar.svg",
    labelKey: "commercial",
    allowedFn: (flags) => (flags & LICENSE_FLAG_BITS.allowOnImages) !== 0 || (flags & LICENSE_FLAG_BITS.allowSellingModels) !== 0,
  },
  {
    bitCombo: [LICENSE_FLAG_BITS.allowOnCivitai, LICENSE_FLAG_BITS.allowRental],
    icon: "brush.svg",
    labelKey: "genServices",
    allowedFn: (flags) => (flags & LICENSE_FLAG_BITS.allowOnCivitai) !== 0 || (flags & LICENSE_FLAG_BITS.allowRental) !== 0,
  },
  {
    bitCombo: [LICENSE_FLAG_BITS.allowNoCredit],
    icon: "user.svg",
    labelKey: "credit",
    allowedFn: (flags) => (flags & LICENSE_FLAG_BITS.allowNoCredit) !== 0,
  },
  {
    bitCombo: [LICENSE_FLAG_BITS.allowDerivatives],
    icon: "git-merge.svg",
    labelKey: "derivatives",
    allowedFn: (flags) => (flags & LICENSE_FLAG_BITS.allowDerivatives) !== 0,
  },
  {
    bitCombo: [LICENSE_FLAG_BITS.allowRelicense],
    icon: "license.svg",
    labelKey: "relicense",
    allowedFn: (flags) => (flags & LICENSE_FLAG_BITS.allowRelicense) !== 0,
  },
];

function parseLicenseFlags(value) {
  if (typeof value === "number") {
    return Number.isFinite(value) ? value : null;
  }
  if (typeof value === "string" && value.trim() !== "") {
    const parsed = Number.parseInt(value, 10);
    return Number.isNaN(parsed) ? null : parsed;
  }
  return null;
}

function buildLicenseIconData(licenseFlags) {
  if (licenseFlags == null) {
    return [];
  }

  const icons = [];

  if ((licenseFlags & LICENSE_FLAG_BITS.allowNoCredit) === 0) {
    icons.push({ icon: "user-check.svg", label: LICENSE_ICON_COPY.credit });
  }

  COMMERCIAL_ICON_CONFIG.forEach((config) => {
    if ((licenseFlags & config.bit) === 0) {
      icons.push({ icon: config.icon, label: config.label });
    }
  });

  if ((licenseFlags & LICENSE_FLAG_BITS.allowDerivatives) === 0) {
    icons.push({ icon: "exchange-off.svg", label: LICENSE_ICON_COPY.derivatives });
  }

  if ((licenseFlags & LICENSE_FLAG_BITS.allowRelicense) === 0) {
    icons.push({ icon: "rotate-2.svg", label: LICENSE_ICON_COPY.relicense });
  }

  return icons;
}

function createLicenseIconElement({ icon, label }) {
  const element = document.createElement("span");
  element.className = "lm-tooltip__license-icon";
  element.setAttribute("role", "img");
  element.setAttribute("aria-label", label);
  element.title = label;
  element.style.setProperty("--license-icon-image", `url('${LICENSE_ICON_PATH}${icon}')`);
  return element;
}

// ── Set 2 (new style) helpers ──

function buildNewLicenseIconData(licenseFlags) {
  if (licenseFlags == null) {
    return [];
  }

  return NEW_ICON_CONFIG.map((config) => {
    const allowed = config.allowedFn(licenseFlags);
    const label = allowed
      ? NEW_LICENSE_ICON_COPY[config.labelKey].allowed
      : NEW_LICENSE_ICON_COPY[config.labelKey].denied;
    return {
      icon: config.icon,
      label,
      allowed,
    };
  });
}

function createNewLicenseIconElement({ icon, label, allowed }) {
  const element = document.createElement("span");
  element.className = `lm-tooltip__license-icon-new ${allowed ? "allowed" : "denied"}`;
  element.setAttribute("role", "img");
  element.setAttribute("aria-label", label);
  element.title = label;
  element.style.setProperty("--license-icon-image", `url('${LICENSE_ICON_PATH}${icon}')`);
  return element;
}

const LICENSE_ICON_STORAGE_KEY = "lm_license_icon_new_style";

// Module-level cache: null = not yet initialized
let _useNewIconsCached = null;

// Fetch the setting from the LoRA Manager backend API via the proper
// ComfyUI api helper (handles base URL, credentials, etc.).
// Stores the result in both the in-memory cache and localStorage so the
// value survives page reloads even before the API responds.
async function _fetchLicenseIconSetting() {
  try {
    const response = await api.fetchApi("/lm/settings");
    if (response.ok) {
      const data = await response.json();
      const value = data.use_new_license_icons !== false;
      _useNewIconsCached = value;
      try { localStorage.setItem(LICENSE_ICON_STORAGE_KEY, String(value)); } catch (_) {}
    }
  } catch (_) {
    // API not available; cached/localStorage fallback stays in place
  }
}

function getUseNewLicenseIcons() {
  // 1) In-memory cache hit
  if (_useNewIconsCached !== null) {
    return _useNewIconsCached;
  }

  // 2) localStorage — survives page reloads
  try {
    const stored = localStorage.getItem(LICENSE_ICON_STORAGE_KEY);
    if (stored !== null) {
      _useNewIconsCached = stored === "true";
      // Refresh from API in background for next time
      _fetchLicenseIconSetting();
      return _useNewIconsCached;
    }
  } catch (_) {}

  // 3) First-ever run: kick off API fetch, default to new style
  _fetchLicenseIconSetting();
  return true;
}

/**
 * Lightweight preview tooltip that can display images or videos for different model types.
 */
export class PreviewTooltip {
  constructor(options = {}) {
    const {
      modelType = "loras",
      previewUrlResolver,
      displayNameFormatter,
    } = options;

    this.modelType = modelType;
    this.previewUrlResolver =
      typeof previewUrlResolver === "function"
        ? previewUrlResolver
        : (name) => this.defaultPreviewUrlResolver(name);
    this.displayNameFormatter =
      typeof displayNameFormatter === "function"
        ? displayNameFormatter
        : (name) => name;

    ensureLmStyles();

    // Pre-fetch license icon style from LM backend so the tooltip
    // respects the standalone settings toggle as early as possible.
    _fetchLicenseIconSetting();

    this.element = document.createElement("div");
    this.element.className = "lm-tooltip";
    document.body.appendChild(this.element);
    this.hideTimeout = null;
    this.isFromAutocomplete = false;
    this.currentModelName = null;

    this.globalClickHandler = (event) => {
      if (!event.target.closest(".comfy-autocomplete-dropdown")) {
        this.hide();
      }
    };
    document.addEventListener("click", this.globalClickHandler);

    this.globalScrollHandler = () => this.hide();
    document.addEventListener("scroll", this.globalScrollHandler, true);
  }

  async defaultPreviewUrlResolver(modelName) {
    const response = await api.fetchApi(
      `/lm/${this.modelType}/preview-url?name=${encodeURIComponent(modelName)}&license_flags=true`,
      { method: "GET" }
    );
    if (!response.ok) {
      throw new Error("Failed to fetch preview URL");
    }
    const data = await response.json();
    if (!data.success || !data.preview_url) {
      throw new Error("No preview available");
    }
    return {
      previewUrl: data.preview_url,
      displayName: data.display_name ?? modelName,
      licenseFlags: parseLicenseFlags(data.license_flags),
      useNewLicenseIcons: data.use_new_license_icons,
    };
  }

  async resolvePreviewData(modelName) {
    const raw = await this.previewUrlResolver(modelName);
    if (!raw) {
      throw new Error("No preview data returned");
    }
    if (typeof raw === "string") {
      return {
        previewUrl: raw,
        displayName: this.displayNameFormatter(modelName),
      };
    }

    const { previewUrl, displayName, licenseFlags, useNewLicenseIcons } = raw;
    if (!previewUrl) {
      throw new Error("No preview URL available");
    }
    return {
      previewUrl,
      displayName:
        displayName !== undefined
          ? displayName
          : this.displayNameFormatter(modelName),
      licenseFlags: parseLicenseFlags(licenseFlags),
      useNewLicenseIcons,
    };
  }

  async show(modelName, x, y, fromAutocomplete = false) {
    try {
      if (this.hideTimeout) {
        clearTimeout(this.hideTimeout);
        this.hideTimeout = null;
      }

      this.isFromAutocomplete = fromAutocomplete;

      if (
        this.element.style.display === "block" &&
        this.currentModelName === modelName
      ) {
        this.position(x, y);
        return;
      }

      this.currentModelName = modelName;
      const { previewUrl, displayName, licenseFlags, useNewLicenseIcons } = await this.resolvePreviewData(
        modelName
      );

      while (this.element.firstChild) {
        this.element.removeChild(this.element.firstChild);
      }

      const mediaContainer = document.createElement("div");
      mediaContainer.className = "lm-tooltip__media-container";

      const isVideo = previewUrl.endsWith(".mp4");
      const mediaElement = isVideo
        ? document.createElement("video")
        : document.createElement("img");
      mediaElement.classList.add("lm-tooltip__media");

      if (isVideo) {
        mediaElement.autoplay = true;
        mediaElement.loop = true;
        mediaElement.muted = true;
        mediaElement.controls = false;
      }

      const nameLabel = document.createElement("div");
      nameLabel.textContent = displayName;
      nameLabel.className = "lm-tooltip__label";

      mediaContainer.appendChild(mediaElement);
      this.renderLicenseOverlay(mediaContainer, licenseFlags, useNewLicenseIcons);
      mediaContainer.appendChild(nameLabel);
      this.element.appendChild(mediaContainer);

      this.element.style.opacity = "0";
      this.element.style.display = "block";

      const waitForLoad = () =>
        new Promise((resolve) => {
          if (isVideo) {
            if (mediaElement.readyState >= 2) {
              resolve();
            } else {
              mediaElement.addEventListener("loadeddata", resolve, {
                once: true,
              });
              mediaElement.addEventListener("error", resolve, { once: true });
            }
          } else if (mediaElement.complete) {
            resolve();
          } else {
            mediaElement.addEventListener("load", resolve, { once: true });
            mediaElement.addEventListener("error", resolve, { once: true });
          }

          setTimeout(resolve, 1000);
        });

      mediaElement.src = previewUrl;
      await waitForLoad();

      requestAnimationFrame(() => {
        this.position(x, y);
        this.element.style.transition = "opacity 0.15s ease";
        this.element.style.opacity = "1";
      });
    } catch (error) {
      console.warn("Failed to load preview:", error);
    }
  }

  position(x, y) {
    const rect = this.element.getBoundingClientRect();
    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;

    let left = x + 10;
    let top = y + 10;

    if (left + rect.width > viewportWidth) {
      left = x - rect.width - 10;
    }

    if (top + rect.height > viewportHeight) {
      top = y - rect.height - 10;
    }

    left = Math.max(10, Math.min(left, viewportWidth - rect.width - 10));
    top = Math.max(10, Math.min(top, viewportHeight - rect.height - 10));

    Object.assign(this.element.style, {
      left: `${left}px`,
      top: `${top}px`,
    });
  }

  hide() {
    if (this.element.style.display === "block") {
      this.element.style.opacity = "0";
      this.hideTimeout = setTimeout(() => {
        this.element.style.display = "none";
        this.currentModelName = null;
        this.isFromAutocomplete = false;
        const video = this.element.querySelector("video");
        if (video) {
          video.pause();
        }
        this.hideTimeout = null;
      }, 150);
    }
  }

  renderLicenseOverlay(container, licenseFlags, useNewLicenseIcons) {
    const useNew = useNewLicenseIcons !== undefined ? useNewLicenseIcons : getUseNewLicenseIcons();
    const icons = useNew
      ? buildNewLicenseIconData(licenseFlags)
      : buildLicenseIconData(licenseFlags);
    if (!icons.length) {
      return;
    }

    const overlay = document.createElement("div");
    overlay.className = useNew
      ? "lm-tooltip__license-overlay-new"
      : "lm-tooltip__license-overlay";
    icons.forEach((descriptor) => {
      overlay.appendChild(
        useNew
          ? createNewLicenseIconElement(descriptor)
          : createLicenseIconElement(descriptor)
      );
    });
    container.appendChild(overlay);
  }

  cleanup() {
    if (this.hideTimeout) {
      clearTimeout(this.hideTimeout);
    }
    document.removeEventListener("click", this.globalClickHandler);
    document.removeEventListener("scroll", this.globalScrollHandler, true);
    this.element.remove();
  }
}
