const DEFAULT_LIMIT = 30;   // used only if auto can't compute
const MIN_LIMIT = 5;
const ELLIPSIS = "...";
const FILL = 0.90;          // use 90% of available width

// Cache average char width per font+letterSpacing
const avgCharWidthCache = new Map();

function toChars(s) {
    return Array.from(s);
} // unicode-safe

function truncateMiddle(str, maxLen) {
    maxLen = parseInt(maxLen, 10);
    if (!Number.isFinite(maxLen)) maxLen = DEFAULT_LIMIT;
    maxLen = Math.max(MIN_LIMIT, maxLen);

    const full = (str || "").trim();
    const chars = toChars(full);
    if (chars.length <= maxLen) return full;

    const keep = maxLen - ELLIPSIS.length; // remaining visible chars besides "..."
    const left = Math.ceil(keep / 2);
    const right = Math.floor(keep / 2);

    return chars.slice(0, left).join("") + ELLIPSIS + chars.slice(chars.length - right).join("");
}

function ensureTextSpan($a) {
    let $span = $a.find("span.js-midcut").first();
    if ($span.length) return $span;

    const textNodes = $a.contents().filter(function () {
        return this.nodeType === 3 && this.nodeValue && this.nodeValue.trim().length > 0;
    });
    if (!textNodes.length) return null;

    const full = textNodes.map(function () {
        return this.nodeValue;
    }).get().join(" ").trim();
    textNodes.remove();

    $span = $("<span/>", {class: "js-midcut", text: full})
        .attr("data-full", full)
        .attr("title", full);

    $a.append($span);
    return $span;
}

function getAvgCharWidthPx(el) {
    const cs = window.getComputedStyle(el);

    // Include letter-spacing because it affects width
    const key = [
        cs.fontStyle, cs.fontVariant, cs.fontWeight,
        cs.fontSize, cs.fontFamily, cs.letterSpacing
    ].join("|");

    if (avgCharWidthCache.has(key)) return avgCharWidthCache.get(key);

    // Measure using a hidden span with same font + letter spacing
    const sample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    const repeat = 6; // 62*6 = 372 chars -> stable average
    const text = sample.repeat(repeat);

    const meas = document.createElement("span");
    meas.style.position = "absolute";
    meas.style.visibility = "hidden";
    meas.style.whiteSpace = "nowrap";
    meas.style.left = "-99999px";
    meas.style.top = "-99999px";
    meas.style.fontStyle = cs.fontStyle;
    meas.style.fontVariant = cs.fontVariant;
    meas.style.fontWeight = cs.fontWeight;
    meas.style.fontSize = cs.fontSize;
    meas.style.fontFamily = cs.fontFamily;
    meas.style.letterSpacing = cs.letterSpacing;
    meas.textContent = text;

    document.body.appendChild(meas);
    const width = meas.getBoundingClientRect().width;
    document.body.removeChild(meas);

    const avg = width / text.length || 8; // fallback if something weird
    avgCharWidthCache.set(key, avg);
    return avg;
}

// Auto maxLen from width (px) → chars, using 90% available width
function autoMaxLenForAnchor($a) {
    const aEl = $a[0];
    const $truncate = $a.closest(".truncate");
    const containerEl = $truncate[0] || aEl;

    const containerW = containerEl.getBoundingClientRect().width;
    if (!containerW) return null;

    // Subtract widths of non-text children (icons etc.), exclude our span
    let nonTextW = 0;
    $a.children().not("span.js-midcut").each(function () {
        nonTextW += $(this).outerWidth(true) || 0;
    });

    const availablePx = Math.max(0, (containerW - nonTextW) * FILL);
    if (!availablePx) return null;

    const avgCharW = getAvgCharWidthPx(aEl);
    if (!avgCharW) return null;

    const est = Math.floor(availablePx / avgCharW);
    return Math.max(MIN_LIMIT, est);
}

// limitOverride: optional manual max length for all elements in this call
function applyMiddleCut(selector, limitOverride) {
    $(selector).each(function () {
        const $el = $(this);

        const $span = ensureTextSpan($el);
        if (!$span) return;

        const full = $span.attr("data-full") || $span.text();

        // 1) per-element override (if you can add attributes)
        // <div class="truncate" data-maxlen="20"> OR <a data-maxlen="20">
        const perEl =
            $el.closest(".truncate").attr("data-maxlen") ||
            $el.attr("data-maxlen");

        // 2) manual override passed to function
        // 3) auto-detected from width
        // 4) fallback default
        const autoLen = autoMaxLenForAnchor($el);
        const limit = perEl || limitOverride || autoLen || DEFAULT_LIMIT;

        const trimmed = truncateMiddle(full, limit);
        if ($span.text() !== trimmed) $span.text(trimmed);
    });
}

function trimRepoTitleName() {
    const $repoNameTitle = $("#repo-name-title");
    let text = $repoNameTitle.text();
    let arr = text.split(": ");
    if (arr.length < 2) {
        return;
    }
    let repoName = arr[arr.length - 1];
    let symbolsCnt = autoMaxLenForAnchor($repoNameTitle);
    const trimmedName = truncateMiddle(repoName, symbolsCnt);
    $repoNameTitle.text(arr[0] + ": " + trimmedName);
}