/** German (de) is the canonical source language for questionnaire content. */
export const SOURCE_LANG = 'de';

export function normalizeTranslations(raw) {
    if (!raw) return {};
    if (Array.isArray(raw)) {
        const map = {};
        raw.forEach(row => {
            if (row && row.languageCode) map[row.languageCode] = row.text ?? '';
        });
        return map;
    }
    return typeof raw === 'object' ? { ...raw } : {};
}

export function normalizeEntry(entry) {
    const translations = normalizeTranslations(entry.translations);
    const germanText = (entry.germanText || translations[SOURCE_LANG] || entry.key || '').trim()
        || entry.key
        || '';
    return { ...entry, translations, germanText };
}

export function germanFor(entry) {
    return entry.germanText || normalizeTranslations(entry.translations)[SOURCE_LANG] || entry.key || '';
}

const HEX_ID_RE = /^[a-f0-9]{32}$/i;

/** Key column label (short id or readable preview — not the app lookup key). */
export function entryDisplayKey(entry) {
    const dk = entry.displayKey || '';
    if (dk && !HEX_ID_RE.test(dk)) return dk;
    if (entry.type === 'question' || entry.type === 'answer_option') {
        const key = (entry.key || '').trim();
        if (key) {
            return key.length > 56 ? `${key.slice(0, 56)}…` : key;
        }
    }
    if (entry.type === 'question' || entry.type === 'answer_option') {
        const id = entry.entityId || '';
        const sep = id.lastIndexOf('__');
        if (sep >= 0) return id.slice(sep + 2);
    }
    return dk || entry.key || '';
}

export function translationFor(entry, lang) {
    return normalizeTranslations(entry.translations)[lang] || '';
}

export function isEntryMissing(entry, lang) {
    return !translationFor(entry, lang).trim();
}

export function countMissing(entries, lang) {
    return entries.filter(e => isEntryMissing(e, lang)).length;
}

export function sectionEntries(allEntries, section) {
    let entries = allEntries.slice(section.startIdx, section.startIdx + section.entryCount);
    if (section.isApp) {
        entries = entries.filter(e => e.type === 'app_string');
    }
    return entries;
}

export function targetLanguages(languages) {
    return (languages || []).filter(l => l.languageCode !== SOURCE_LANG);
}

export function isLanguageGloballyEnabled(lang) {
    if (!lang) return false;
    if (lang.languageCode === SOURCE_LANG) return true;
    return Number(lang.enabled ?? 1) === 1;
}

export function globallyEnabledLanguages(languages) {
    return (languages || []).filter(isLanguageGloballyEnabled);
}

/** Sort rows within a group: missing translations first, then questionnaire order */
export function sortGroupItems(items, targetLang) {
    return [...items].sort((a, b) => {
        const aMissing = !translationFor(a.entry, targetLang).trim();
        const bMissing = !translationFor(b.entry, targetLang).trim();
        if (aMissing !== bMissing) return aMissing ? -1 : 1;

        const orderA = a.entry.sortOrder ?? 0;
        const orderB = b.entry.sortOrder ?? 0;
        if (orderA !== orderB) return orderA - orderB;

        const typeCmp = (a.entry.type === 'question' ? 0 : 1) - (b.entry.type === 'question' ? 0 : 1);
        if (typeCmp !== 0) return typeCmp;

        return germanFor(a.entry).localeCompare(germanFor(b.entry), 'de');
    });
}

/**
 * Groups for one questionnaire: UI strings, then questions + options (interleaved).
 * @param {number} indexOffset - global index for save handlers
 */
export function buildTranslationGroups(entries, targetLang, indexOffset = 0) {
    const items = entries.map((entry, i) => ({ entry, idx: indexOffset + i }));
    const appStrings = items.filter(({ entry }) => entry.type === 'app_string');
    const qStrings = items.filter(({ entry }) => entry.type === 'string');
    const content = items.filter(({ entry }) =>
        entry.type !== 'string' && entry.type !== 'app_string'
    );
    const groups = [];

    if (appStrings.length) {
        groups.push({
            id: 'app_strings',
            title: 'App strings',
            items: sortGroupItems(appStrings, targetLang),
        });
    }
    if (qStrings.length) {
        groups.push({
            id: 'strings',
            title: 'UI strings',
            items: sortGroupItems(qStrings, targetLang),
        });
    }
    if (content.length) {
        groups.push({
            id: 'content',
            title: 'Questions',
            items: sortGroupItems(content, targetLang),
        });
    }
    return groups;
}

/** Render grouped sections (optional questionnaire wrapper title). */
export function renderGroupedTranslationsHTML(groups, targetLang, sourceLabel, editable, options = {}) {
    const { showColumnHeader = true, questionnaireTitle = null } = options;
    if (!groups.length) return '';

    const header = showColumnHeader ? translationListHeaderHTML(
        targetLang,
        sourceLabel
    ) : '';

    const qnHeader = questionnaireTitle
        ? `<h2 class="trans-qn-title">${escHtml(questionnaireTitle)}</h2>`
        : '';

    const body = groups.map(g => `
        <section class="trans-group" data-group="${escHtml(g.id)}">
            <h3 class="trans-group-title">${escHtml(g.title)}</h3>
            <div class="trans-list trans-list-in-group">
                ${g.items.map(({ entry, idx }) =>
                    translationRowHTML(entry, idx, { targetLang, editable })
                ).join('')}
            </div>
        </section>
    `).join('');

    const wrapClass = questionnaireTitle ? 'trans-qn-block' : 'trans-groups-wrap';
    return `<div class="${wrapClass}">${qnHeader}${header}${body}</div>`;
}

/** Only global app UI rows (never questionnaire questions/options). */
export function filterGlobalAppEntries(entries) {
    return (entries || []).filter(e => e && e.type === 'app_string');
}

/** Flatten app strings + questionnaires from ?all=1 */
export function flattenAllQuestionnaires(questionnaires, appStrings = []) {
    const allEntries = [];
    const sections = [];

    const appNorm = filterGlobalAppEntries((appStrings || []).map(normalizeEntry));
    if (appNorm.length) {
        const startIdx = 0;
        appNorm.forEach(e => allEntries.push(e));
        sections.push({
            questionnaireID: '__app__',
            name: 'App UI',
            startIdx,
            entryCount: appNorm.length,
            isApp: true,
        });
    }

    for (const qn of questionnaires) {
        const normalized = (qn.entries || []).map(normalizeEntry);
        if (!normalized.length) continue;
        const startIdx = allEntries.length;
        normalized.forEach(e => allEntries.push(e));
        sections.push({
            questionnaireID: qn.questionnaireID,
            name: qn.name || 'Questionnaire',
            startIdx,
            entryCount: normalized.length,
            isApp: false,
        });
    }

    return { allEntries, sections };
}

export function typeBadge(type) {
    const map = { question: 'Q', answer_option: 'Opt', string: 'Str', app_string: 'App' };
    const cls = { question: 'badge-q', answer_option: 'badge-opt', string: 'badge-str', app_string: 'badge-app' };
    return `<span class="trans-type-badge ${cls[type] || ''}">${map[type] || type}</span>`;
}

export function escHtml(s) {
    if (s == null) return '';
    return String(s)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
}

export function sourceLanguageLabel(languages) {
    const row = (languages || []).find(l => l.languageCode === SOURCE_LANG);
    return row?.name ? `${row.name} (${SOURCE_LANG})` : 'German';
}

/** Column headers: label | main language (German) | target language */
export function translationListHeaderHTML(targetLangLabel, sourceLabel = 'German') {
    return `
        <div class="trans-list-header trans-list-header-sticky">
            <span class="trans-col-key">Label</span>
            <span class="trans-col-source">${escHtml(sourceLabel)}</span>
            <span class="trans-col-target">${escHtml(targetLangLabel)}</span>
        </div>`;
}

export function translationRowHTML(entry, idx, { targetLang, editable = true }) {
    const lookupKey = entry.key || '';
    const label = entryDisplayKey(entry);
    const german = germanFor(entry);
    const val = translationFor(entry, targetLang);
    const missing = !val.trim();
    const disabled = editable ? '' : 'disabled';

    const sourceCell = editable
        ? `<input type="text" class="trans-cell-input" data-idx="${idx}" data-field="german"
                value="${escHtml(german)}" placeholder="German text…" autocomplete="off">`
        : `<span class="trans-source-text" title="${escHtml(german)}">${escHtml(german)}</span>`;

    const germanPreview = german.length > 72 ? `${german.slice(0, 72)}…` : german;

    return `
        <div class="trans-list-item ${missing ? 'trans-list-item-missing' : ''}"
            data-idx="${idx}" data-type="${entry.type}" data-key="${escHtml(lookupKey)}" data-label="${escHtml(label)}" data-missing="${missing ? '1' : '0'}">
            <div class="trans-list-key">
                ${typeBadge(entry.type)}
                <div class="trans-key-labels">
                    <span class="trans-key-primary" title="${escHtml(german)}">${escHtml(germanPreview || label)}</span>
                    <span class="trans-key-secondary" title="${escHtml(lookupKey)}">${escHtml(lookupKey)}</span>
                </div>
            </div>
            <div class="trans-list-source">${sourceCell}</div>
            <input type="text" class="trans-cell-input ${missing ? 'trans-input-missing' : ''}"
                data-idx="${idx}" data-field="translation" value="${escHtml(val)}"
                placeholder="Translation…" ${disabled} autocomplete="off">
        </div>`;
}

/**
 * Grouped rows with collapsible sections and missing counts in summaries.
 */
export function renderCollapsibleGroupedTranslationsHTML(groups, targetLang, sourceLabel, editable, options = {}) {
    const { openGroupsWithMissing = false } = options;
    if (!groups.length) return '';

    const body = groups.map(g => {
        const missing = g.items.filter(({ entry }) => isEntryMissing(entry, targetLang)).length;
        const total = g.items.length;
        const open = openGroupsWithMissing && missing > 0;
        const summary = missing
            ? `${g.title} · ${missing} missing`
            : `${g.title} · complete (${total})`;

        return `
        <details class="trans-group-details"${open ? ' open' : ''}>
            <summary class="trans-group-summary">${escHtml(summary)}</summary>
            <div class="trans-group-rows">
                ${g.items.map(({ entry, idx }) =>
                    translationRowHTML(entry, idx, { targetLang, editable })
                ).join('')}
            </div>
        </details>`;
    }).join('');

    return `<div class="trans-groups-flat">${body}</div>`;
}
