// Facebook Post Tracker Extension
// Uses API_BASE_URL from config.js

const EXTENSION_VERSION = '1.2.0';
const PROCESSED_ATTR = 'data-fb-tracker-processed';
const PENDING_ATTR = 'data-fb-tracker-pending';
const DIALOG_ROOT_SELECTOR = '[role="dialog"], [data-pagelet*="Modal"], [data-pagelet="StoriesRecentStoriesFeedSection"]';
const API_URL = `${API_BASE_URL}/api`;
const WEBAPP_BASE_URL = API_BASE_URL.replace(/\/+$/, '');
const MAX_SELECTION_LENGTH = 5000;
const postSelectionCache = new WeakMap();
const LAST_SELECTION_MAX_AGE = 5000;
let selectionCacheTimeout = null;
let lastGlobalSelection = { text: '', timestamp: 0 };
const processedPostUrls = new Map();
const SEARCH_RESULTS_PATH_PREFIX = '/search';
const FEED_HOME_PATHS = ['/', '/home.php'];
const sessionSearchRecordedUrls = new Set();
const sessionSearchInfoCache = new Map();
let profileSelectionNoticeShown = false;

function isOnSearchResultsPage() {
  try {
    const pathname = window.location && window.location.pathname;
    return typeof pathname === 'string' && pathname.startsWith(SEARCH_RESULTS_PATH_PREFIX);
  } catch (error) {
    return false;
  }
}

const trackerElementsByPost = new WeakMap();
const postAdditionalNotes = new WeakMap();

const REELS_PATH_PREFIX = '/reel/';
const POST_TEXT_LOG_TAG = '[FB PostText]';

function isOnReelsPage() {
  try {
    const pathname = window.location && window.location.pathname;
    return typeof pathname === 'string' && pathname.startsWith(REELS_PATH_PREFIX);
  } catch (error) {
    return false;
  }
}

function maybeRedirectPageReelsToMain() {
  try {
    const { location } = window;
    const pathname = location && location.pathname;
    if (typeof pathname !== 'string') {
      return false;
    }
    if (pathname.toLowerCase() === '/profile.php') {
      const params = new URLSearchParams(location.search || '');
      const profileId = params.get('id');
      const sk = params.get('sk');
      if (profileId && sk && sk.toLowerCase().startsWith('reel')) {
        const targetUrl = `${location.origin}/profile.php?id=${encodeURIComponent(profileId)}`;
        if (location.href !== targetUrl) {
          location.replace(targetUrl);
          return true;
        }
        return false;
      }
    }
    const match = pathname.match(/^\/([^/]+)\/reels\/?$/i);
    if (!match) {
      return false;
    }
    const pageSlug = match[1];
    if (!pageSlug) {
      return false;
    }
    const targetUrl = `${location.origin}/${pageSlug}/`;
    if (location.href === targetUrl) {
      return false;
    }
    location.replace(targetUrl);
    return true;
  } catch (error) {
    return false;
  }
}

let debugLoggingEnabled = false;

const originalConsoleLog = console.log.bind(console);
const originalConsoleDebug = console.debug ? console.debug.bind(console) : null;
const originalConsoleInfo = console.info ? console.info.bind(console) : null;

function shouldSuppressTrackerLog(args) {
  if (!args || args.length === 0) {
    return false;
  }
  const [first] = args;
  if (typeof first === 'string' && first.startsWith('[FB Tracker]')) {
    return !debugLoggingEnabled;
  }
  return false;
}

console.log = (...args) => {
  if (shouldSuppressTrackerLog(args)) {
    return;
  }
  originalConsoleLog(...args);
};

if (originalConsoleDebug) {
  console.debug = (...args) => {
    if (shouldSuppressTrackerLog(args)) {
      return;
    }
    originalConsoleDebug(...args);
  };
}

if (originalConsoleInfo) {
  console.info = (...args) => {
    if (shouldSuppressTrackerLog(args)) {
      return;
    }
    originalConsoleInfo(...args);
  };
}

function applyDebugLoggingPreference(value) {
  debugLoggingEnabled = Boolean(value);
  if (debugLoggingEnabled) {
    originalConsoleLog('[FB Tracker] Debug logging enabled');
  }
}

chrome.storage.sync.get(['debugLoggingEnabled'], (result) => {
  applyDebugLoggingPreference(result && typeof result.debugLoggingEnabled !== 'undefined'
    ? result.debugLoggingEnabled
    : false);
});

chrome.storage.onChanged.addListener((changes, area) => {
  if (area === 'sync' && changes && Object.prototype.hasOwnProperty.call(changes, 'debugLoggingEnabled')) {
    applyDebugLoggingPreference(changes.debugLoggingEnabled.newValue);
  }
});

function getTrackerElementForPost(postElement) {
  if (!postElement) {
    return null;
  }

  const tracker = trackerElementsByPost.get(postElement);
  if (tracker && tracker.isConnected) {
    return tracker;
  }

  if (tracker) {
    trackerElementsByPost.delete(postElement);
  }

  return null;
}

function setTrackerElementForPost(postElement, trackerElement) {
  if (!postElement) {
    return;
  }

  if (trackerElement && trackerElement.isConnected) {
    trackerElementsByPost.set(postElement, trackerElement);
  } else {
    trackerElementsByPost.delete(postElement);
  }
}

function clearTrackerElementForPost(postElement, trackerElement = null) {
  if (!postElement) {
    return;
  }

  if (!trackerElementsByPost.has(postElement)) {
    return;
  }

  const current = trackerElementsByPost.get(postElement);
  if (trackerElement && current && current !== trackerElement) {
    return;
  }

  trackerElementsByPost.delete(postElement);
}

function getTrackerScopeRoot(element) {
  return element ? element.closest(DIALOG_ROOT_SELECTOR) : null;
}

function getTrackersForUrlInScope(encodedUrl, scopeRoot) {
  if (!encodedUrl) {
    return [];
  }

  const selector = `.fb-tracker-ui[data-post-url="${encodedUrl}"]`;
  const trackers = Array.from(document.querySelectorAll(selector)).filter((tracker) => tracker && tracker.isConnected);

  return trackers.filter((tracker) => {
    const trackerScopeRoot = getTrackerScopeRoot(tracker);
    if (scopeRoot) {
      return trackerScopeRoot === scopeRoot;
    }
    return !trackerScopeRoot;
  });
}

function dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, preferredTracker = null) {
  const trackers = getTrackersForUrlInScope(encodedUrl, scopeRoot);
  if (!trackers.length) {
    return null;
  }

  const keep = preferredTracker && trackers.includes(preferredTracker)
    ? preferredTracker
    : trackers[trackers.length - 1];

  trackers.forEach((tracker) => {
    if (tracker !== keep) {
      tracker.remove();
    }
  });

  return keep;
}

function getTrackerInsertionAnchorInPost(postElement, buttonBar) {
  if (!postElement || !buttonBar || !postElement.contains(buttonBar)) {
    return null;
  }

  let anchor = buttonBar;
  while (anchor.parentElement && anchor.parentElement !== postElement) {
    anchor = anchor.parentElement;
  }

  return anchor && anchor.parentElement === postElement ? anchor : null;
}

function resolveActionButtonBar(postElement, buttonBar) {
  if (!postElement) {
    return null;
  }

  if (buttonBar && buttonBar.isConnected && postElement.contains(buttonBar)) {
    return buttonBar;
  }

  const resolved = findButtonBar(postElement);
  if (resolved && resolved.isConnected && postElement.contains(resolved)) {
    return resolved;
  }

  return null;
}

function getDirectActionAnchorInPost(postElement, buttonBar) {
  if (!postElement) {
    return null;
  }

  const resolvedButtonBar = resolveActionButtonBar(postElement, buttonBar);
  if (!resolvedButtonBar) {
    return null;
  }

  return getTopLevelAnchorWithinPost(postElement, resolvedButtonBar)
    || getTrackerInsertionAnchorInPost(postElement, resolvedButtonBar)
    || resolvedButtonBar;
}

function getTopLevelAnchorWithinPost(postElement, element) {
  if (!postElement || !element || !postElement.contains(element)) {
    return null;
  }

  let anchor = element;
  while (anchor.parentElement && anchor.parentElement !== postElement) {
    anchor = anchor.parentElement;
  }

  return anchor && anchor.parentElement === postElement ? anchor : null;
}

function isLikelyCommentSortNode(node) {
  if (!node || !isElementVisible(node) || node.closest('.fb-tracker-ui')) {
    return false;
  }

  const keywords = ['relevantes zuerst', 'most relevant', 'neueste', 'newest'];
  const text = (node.textContent || '').trim().toLowerCase();
  const ariaLabel = (node.getAttribute('aria-label') || '').trim().toLowerCase();

  if (ariaLabel && keywords.some((keyword) => ariaLabel.includes(keyword))) {
    return true;
  }

  if (text && text.length <= 120 && keywords.some((keyword) => text.includes(keyword))) {
    return true;
  }

  return false;
}

function hasCommentContentSignal(node) {
  if (!node) {
    return false;
  }

  if (node.matches && node.matches('[data-testid*="comment" i], [data-scope="comment"]')) {
    return true;
  }

  return Boolean(
    node.querySelector(
      '[data-testid*="comment" i], [data-scope="comment"], [aria-roledescription*="comment" i], [aria-roledescription*="kommentar" i], div[role="textbox"][contenteditable="true"]'
    )
  );
}

function findAdjacentCommentSortAnchor(postElement, actionAnchor = null) {
  if (!postElement || !actionAnchor || !postElement.contains(actionAnchor)) {
    return null;
  }

  let sibling = actionAnchor.nextElementSibling;
  for (let i = 0; i < 4 && sibling; i++) {
    if (sibling.classList && sibling.classList.contains('fb-tracker-ui')) {
      sibling = sibling.nextElementSibling;
      continue;
    }

    if (isLikelyCommentSortNode(sibling)) {
      return sibling;
    }

    const innerSortNode = sibling.querySelector && sibling.querySelector([
      '[aria-label*="relevantes zuerst" i]',
      '[aria-label*="most relevant" i]',
      '[aria-label*="neueste" i]',
      '[aria-label*="newest" i]',
      '[role="button"]',
      'button'
    ].join(', '));
    if (innerSortNode && isLikelyCommentSortNode(innerSortNode)) {
      return sibling;
    }

    if (hasCommentContentSignal(sibling)) {
      break;
    }

    sibling = sibling.nextElementSibling;
  }

  return null;
}

const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache
const aiCredentialCache = {
  data: null,
  timestamp: 0,
  pending: null
};
const MODERATION_SETTINGS_CACHE_TTL = 5 * 60 * 1000;
const moderationSettingsCache = {
  data: null,
  timestamp: 0,
  pending: null
};
const SPORTS_SCORING_DEFAULTS = {
  threshold: 5,
  weights: {
    scoreline: 3,
    scoreEmoji: 2,
    sportEmoji: 2,
    sportVerb: 1.5,
    sportNoun: 2,
    hashtag: 1.5,
    teamToken: 2,
    competition: 2,
    celebration: 1,
    location: 1
  }
};

const DEFAULT_SPORT_TERMS = {
  nouns: [
    'auswärtssieg', 'heimsieg', 'derbysieg', 'revanche', 'spiel', 'spieltag', 'match', 'derby', 'finale',
    'cup', 'pokal', 'liga', 'bundesliga', 'oberliga', 'kreisliga', 'bezirksliga', 'meisterschaft',
    'turnier', 'halbzeit', 'tabellenplatz', 'tabelle', 'tor', 'tore', 'treffer', 'stadion', 'arena', 'halle',
    'trainerteam', 'mannschaft', 'fans', 'fanblock', 'jugend', 'u17', 'u19', 'u15'
  ],
  verbs: [
    'gewinnen', 'siegen', 'geholt', 'erkämpfen', 'erkämpft', 'erkämpfen', 'drehen', 'punkten',
    'trifft', 'treffen', 'schießt', 'schiesst', 'schießen', 'schiessen', 'verteidigen', 'stürmen', 'kämpfen'
  ],
  competitions: [
    'bundesliga', 'liga', 'serie a', 'premier league', 'champions league', 'europa league', 'dfb-pokal',
    'cup', 'pokal', 'qualifikation', 'qualirunde', 'halbfinale', 'viertelfinale', 'achtelfinale', 'relegation'
  ],
  celebrations: [
    'sieg', 'siege', 'auswärtssieg', 'heimsieg', 'auswärtsspiel', 'punkte', 'punkte geholt', 'man of the match', 'motm',
    'tabellenführung', 'tabellenplatz', 'tabellendritter', 'tabellenzweiter'
  ],
  locations: [
    'auswärts', 'heimspiel', 'derby', 'arena', 'stadion', 'halle', 'bolle', 'bölle', 'mosel'
  ],
  negatives: [
    'rezept', 'kochen', 'politik', 'wahl', 'bundestag', 'landtag', 'software', 'release', 'update', 'konzert',
    'album', 'tour', 'podcast', 'karriere', 'job', 'stellenangebot', 'bewerbung'
  ]
};

console.log(`[FB Tracker v${EXTENSION_VERSION}] Extension loaded, API URL:`, API_URL);

function ensureTrackerActionsContainer(container) {
  if (!container) {
    return null;
  }

  let actionsContainer = container.querySelector('.fb-tracker-actions-end');
  if (actionsContainer && actionsContainer.isConnected) {
    return actionsContainer;
  }

  actionsContainer = document.createElement('div');
  actionsContainer.className = 'fb-tracker-actions-end';
  actionsContainer.style.cssText = `
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
  `;
  container.appendChild(actionsContainer);
  return actionsContainer;
}

function backendFetch(url, options = {}) {
  const config = {
    ...options,
    credentials: 'include'
  };

  if (options && options.headers) {
    config.headers = { ...options.headers };
  }

  return fetch(url, config);
}

async function fetchActiveAICredentials(forceRefresh = false) {
  const now = Date.now();
  if (!forceRefresh && aiCredentialCache.data && (now - aiCredentialCache.timestamp < AI_CREDENTIAL_CACHE_TTL)) {
    return aiCredentialCache.data;
  }

  if (aiCredentialCache.pending) {
    try {
      return await aiCredentialCache.pending;
    } catch (error) {
      // fall through to retry below
    }
  }

  aiCredentialCache.pending = (async () => {
    const response = await backendFetch(`${API_URL}/ai-credentials`);
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.error || 'AI-Provider konnten nicht geladen werden');
    }

    const credentials = await response.json();
    let active = Array.isArray(credentials)
      ? credentials.filter(entry => entry && Number(entry.is_active) === 1)
      : [];

    if (active.length === 0 && Array.isArray(credentials)) {
      active = credentials.slice();
    }

    aiCredentialCache.data = active;
    aiCredentialCache.timestamp = Date.now();
    return active;
  })();

  try {
    return await aiCredentialCache.pending;
  } finally {
    aiCredentialCache.pending = null;
  }
}

function normalizeModerationSettings(payload) {
  if (!payload || typeof payload !== 'object') {
    return {
      sports_scoring_enabled: true,
      sports_score_threshold: SPORTS_SCORING_DEFAULTS.threshold,
      sports_score_weights: SPORTS_SCORING_DEFAULTS.weights,
      sports_terms: DEFAULT_SPORT_TERMS,
      sports_auto_hide_enabled: false
    };
  }
  const threshold = (() => {
    const parsed = parseFloat(payload.sports_score_threshold);
    if (Number.isNaN(parsed) || parsed < 0) {
      return SPORTS_SCORING_DEFAULTS.threshold;
    }
    return Math.min(50, Math.max(0, parsed));
  })();

  const weightsSource = payload.sports_score_weights && typeof payload.sports_score_weights === 'object'
    ? payload.sports_score_weights
    : {};
  const normalizedWeights = { ...SPORTS_SCORING_DEFAULTS.weights };
  for (const key of Object.keys(SPORTS_SCORING_DEFAULTS.weights)) {
    const raw = weightsSource[key];
    const parsed = typeof raw === 'number' ? raw : parseFloat(raw);
    if (Number.isFinite(parsed)) {
      normalizedWeights[key] = Math.max(0, Math.min(10, parsed));
    }
  }

  const normalizeTerms = (terms) => {
    const base = { ...DEFAULT_SPORT_TERMS };
    const src = terms && typeof terms === 'object' ? terms : {};
    const normalizeList = (list, fallback) => {
      if (!Array.isArray(list)) return fallback;
      const cleaned = list
        .map((entry) => typeof entry === 'string' ? entry.trim().toLowerCase() : '')
        .filter((entry) => entry);
      const unique = Array.from(new Set(cleaned)).slice(0, 200);
      return unique.length ? unique : fallback;
    };
    return {
      nouns: normalizeList(src.nouns, base.nouns),
      verbs: normalizeList(src.verbs, base.verbs),
      competitions: normalizeList(src.competitions, base.competitions),
      celebrations: normalizeList(src.celebrations, base.celebrations),
      locations: normalizeList(src.locations, base.locations),
      negatives: normalizeList(src.negatives, base.negatives)
    };
  };

  return {
    sports_scoring_enabled: payload.sports_scoring_enabled !== false,
    sports_score_threshold: threshold,
    sports_score_weights: normalizedWeights,
    sports_terms: normalizeTerms(payload.sports_terms),
    sports_auto_hide_enabled: !!payload.sports_auto_hide_enabled
  };
}

async function fetchModerationSettings(forceRefresh = false) {
  const now = Date.now();
  if (!forceRefresh && moderationSettingsCache.data && (now - moderationSettingsCache.timestamp < MODERATION_SETTINGS_CACHE_TTL)) {
    return moderationSettingsCache.data;
  }

  if (moderationSettingsCache.pending) {
    try {
      return await moderationSettingsCache.pending;
    } catch (error) {
      // fallthrough to retry
    }
  }

  moderationSettingsCache.pending = (async () => {
    const response = await backendFetch(`${API_URL}/moderation-settings`);
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.error || 'Moderations-Einstellungen konnten nicht geladen werden');
    }
    const data = await response.json();
    const normalized = normalizeModerationSettings(data);
    moderationSettingsCache.data = normalized;
    moderationSettingsCache.timestamp = Date.now();
    return normalized;
  })();

  try {
    return await moderationSettingsCache.pending;
  } finally {
    moderationSettingsCache.pending = null;
  }
}

function formatAICredentialLabel(credential) {
  if (!credential || typeof credential !== 'object') {
    return 'Unbekannte AI';
  }

  const name = (credential.name || '').trim();
  const provider = (credential.provider || '').trim();
  const model = (credential.model || '').trim();

  if (name) {
    if (provider && model) {
      return `${name} · ${provider}/${model}`;
    }
    if (provider) {
      return `${name} · ${provider}`;
    }
    if (model) {
      return `${name} · ${model}`;
    }
    return name;
  }

  if (provider && model) {
    return `${provider}/${model}`;
  }
  if (model) {
    return model;
  }
  if (provider) {
    return provider;
  }

  return 'AI Provider';
}

document.addEventListener('selectionchange', () => {
  if (selectionCacheTimeout) {
    clearTimeout(selectionCacheTimeout);
  }
  selectionCacheTimeout = setTimeout(() => {
    cacheCurrentSelection();
    selectionCacheTimeout = null;
  }, 50);
});

// Profile state helpers
async function fetchBackendProfileNumber() {
  try {
    const response = await backendFetch(`${API_URL}/profile-state`);
    if (!response.ok) {
      return null;
    }
    const data = await response.json();
    if (data && typeof data.profile_number !== 'undefined') {
      const parsed = parseInt(data.profile_number, 10);
      if (!Number.isNaN(parsed)) {
        return parsed;
      }
    }
  } catch (error) {
    console.warn('[FB Tracker] Failed to fetch profile state from backend:', error);
  }
  return null;
}

function extractAuthorName(postElement) {
  if (!postElement) {
    return null;
  }

  const selectors = [
    'h2 strong a[role="link"]',
    'h2 span a[role="link"]',
    'a[role="link"][tabindex="0"] strong',
    'a[role="link"][tabindex="0"]'
  ];

  for (const selector of selectors) {
    const node = postElement.querySelector(selector);
    if (node && node.textContent) {
      const text = node.textContent.trim();
      if (text) {
        return text;
      }
    }
  }

  const ariaLabelNode = postElement.querySelector('[aria-label]');
  if (ariaLabelNode) {
    const ariaLabel = ariaLabelNode.getAttribute('aria-label');
    if (ariaLabel && ariaLabel.trim()) {
      return ariaLabel.trim();
    }
  }

  return null;
}

async function getProfileNumber() {
  try {
    const backendProfile = await fetchBackendProfileNumber();
    if (backendProfile) {
      console.log('[FB Tracker] Profile number (backend):', backendProfile);
      return backendProfile;
    }
  } catch (error) {
    console.warn('[FB Tracker] Failed to resolve profile number from backend:', error);
  }

  if (!profileSelectionNoticeShown) {
    profileSelectionNoticeShown = true;
    showToast('Bitte zuerst ein Profil im Tracker auswählen.', 'error');
  }
  return null;
}

// Extract post URL from post element
function cleanPostUrl(rawUrl) {
  if (!rawUrl) {
    return '';
  }

  const cftIndex = rawUrl.indexOf('__cft__');
  let trimmed = cftIndex !== -1 ? rawUrl.slice(0, cftIndex) : rawUrl;
  trimmed = trimmed.replace(/[?&]$/, '');
  return trimmed;
}

function toAbsoluteFacebookUrl(rawUrl) {
  if (!rawUrl) {
    return null;
  }

  const cleaned = cleanPostUrl(rawUrl);
  let url;

  try {
    url = new URL(cleaned);
  } catch (error) {
    try {
      url = new URL(cleaned, window.location.origin);
    } catch (innerError) {
      return null;
    }
  }

  const host = url.hostname.toLowerCase();
  if (!host.endsWith('facebook.com')) {
    return null;
  }

  return url;
}

function isValidFacebookPostUrl(url) {
  if (!url) {
    return false;
  }

  const path = url.pathname.toLowerCase();
  const searchParams = url.searchParams;

  const postPathPatterns = [
    '/posts/',
    '/permalink/',
    '/photos/',
    '/videos/',
    '/reel/',
    '/watch/'
  ];

  if (postPathPatterns.some(pattern => path.includes(pattern))) {
    return true;
  }

  if (path.startsWith('/photo') && searchParams.has('fbid')) {
    return true;
  }

  if (path.startsWith('/photo.php') && searchParams.has('fbid')) {
    return true;
  }

  if (path === '/permalink.php' && searchParams.has('story_fbid')) {
    return true;
  }

  if (path.startsWith('/groups/') && searchParams.has('multi_permalinks')) {
    return true;
  }

  // /watch/ URLs with video ID parameter
  if (path.startsWith('/watch') && searchParams.has('v')) {
    return true;
  }

  return false;
}

function formatFacebookPostUrl(url) {
  if (!url) {
    return '';
  }

  let search = url.search;
  if (search.endsWith('?') || search.endsWith('&')) {
    search = search.slice(0, -1);
  }

  return `${url.origin}${url.pathname}${search}`;
}

function extractPostUrlCandidate(rawUrl) {
  const absoluteUrl = toAbsoluteFacebookUrl(rawUrl);
  if (!absoluteUrl || !isValidFacebookPostUrl(absoluteUrl)) {
    return '';
  }

  const formatted = formatFacebookPostUrl(absoluteUrl);
  return normalizeFacebookPostUrl(formatted);
}

function getPostUrl(postElement, postNum = '?') {
  console.log('[FB Tracker] Post #' + postNum + ' - Extracting URL from:', postElement);

  const allCandidates = [];

  // Strategy 1: Look for attribution links inside post
  const attributionLinks = postElement.querySelectorAll('a[attributionsrc*="/privacy_sandbox/comet/"][href]');
  for (const link of attributionLinks) {
    const candidate = extractPostUrlCandidate(link.href);
    if (candidate && !allCandidates.includes(candidate)) {
      allCandidates.push(candidate);
    }
  }

  // Strategy 2: Look in parent elements (timestamp might be outside container)
  let current = postElement;
  for (let i = 0; i < 5 && current; i++) {
    const parentLinks = current.querySelectorAll('a[attributionsrc*="/privacy_sandbox/comet/"][href]');
    for (const link of parentLinks) {
      const candidate = extractPostUrlCandidate(link.href);
      if (candidate && !allCandidates.includes(candidate)) {
        allCandidates.push(candidate);
      }
    }
    current = current.parentElement;
  }

  // Strategy 3: Check all links in post
  const links = postElement.querySelectorAll('a[href]');
  for (const link of links) {
    const candidate = extractPostUrlCandidate(link.href);
    if (candidate && !allCandidates.includes(candidate)) {
      allCandidates.push(candidate);
    }
  }

  // Prefer main post links over photo/video links
  const mainPostLink = allCandidates.find(url =>
    url.includes('/posts/') || url.includes('/permalink/') || url.includes('/permalink.php')
  );

  if (mainPostLink) {
    console.log('[FB Tracker] Post #' + postNum + ' - Found main post URL:', mainPostLink, postElement);
    return { url: mainPostLink, allCandidates, mainUrl: mainPostLink };
  }

  // Fallback to first candidate
  if (allCandidates.length > 0) {
    console.log('[FB Tracker] Post #' + postNum + ' - Using first candidate URL:', allCandidates[0], postElement);
    return { url: allCandidates[0], allCandidates, mainUrl: '' };
  }

  const fallbackCandidate = extractPostUrlCandidate(window.location.href);
  if (fallbackCandidate) {
    console.log('[FB Tracker] Post #' + postNum + ' - Using fallback URL:', fallbackCandidate, postElement);
    return { url: fallbackCandidate, allCandidates: [fallbackCandidate], mainUrl: '' };
  }

  console.log('[FB Tracker] Post #' + postNum + ' - No valid URL found for:', postElement);
  return { url: '', allCandidates: [], mainUrl: '' };
}

function expandPhotoUrlHostVariants(url) {
  if (typeof url !== 'string' || !url) {
    return [];
  }

  try {
    const parsed = new URL(url);
    const hostname = parsed.hostname.toLowerCase();
    if (!hostname.endsWith('facebook.com')) {
      return [];
    }

    const pathname = parsed.pathname.toLowerCase();
    if (!pathname.startsWith('/photo')) {
      return [];
    }

    const search = parsed.search || '';
    const protocol = parsed.protocol || 'https:';
    const hosts = ['www.facebook.com', 'facebook.com', 'm.facebook.com'];
    const variants = [];

    for (const candidateHost of hosts) {
      if (candidateHost === hostname) {
        continue;
      }
      const candidateUrl = `${protocol}//${candidateHost}${parsed.pathname}${search}`;
      const normalizedVariant = normalizeFacebookPostUrl(candidateUrl);
      if (
        normalizedVariant
        && normalizedVariant !== url
        && !variants.includes(normalizedVariant)
      ) {
        variants.push(normalizedVariant);
      }
    }

    return variants;
  } catch (error) {
    return [];
  }
}

async function fetchPostByUrl(url) {
  const normalizedUrl = normalizeFacebookPostUrl(url);
  if (!normalizedUrl) {
    return null;
  }
  const response = await backendFetch(`${API_URL}/posts/by-url?url=${encodeURIComponent(normalizedUrl)}`);
  if (!response.ok) {
    return null;
  }
  const data = await response.json();
  return data && data.id ? data : null;
}

async function fetchPostById(postId) {
  if (!postId) {
    return null;
  }
  try {
    const response = await backendFetch(`${API_URL}/posts`);
    if (!response.ok) {
      return null;
    }
    const posts = await response.json();
    if (!Array.isArray(posts)) {
      return null;
    }
    return posts.find(post => post && post.id === postId) || null;
  } catch (error) {
    return null;
  }
}

async function buildSimilarityPayload(postElement) {
  let postText = null;
  try {
    postText = extractPostText(postElement) || null;
  } catch (error) {
    console.debug('[FB Tracker] Failed to extract post text for similarity:', error);
  }
  const imageInfo = await getFirstPostImageInfo(postElement);
  return {
    postText,
    firstImageHash: imageInfo.hash,
    firstImageUrl: imageInfo.url
  };
}

async function findSimilarPost({ url, postText, firstImageHash }) {
  if (!url) {
    return null;
  }
  if (!postText && !firstImageHash) {
    return null;
  }
  try {
    const payload = {
      url,
      post_text: postText || null,
      first_image_hash: firstImageHash || null
    };
    const response = await backendFetch(`${API_URL}/posts/similar`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });
    if (!response.ok) {
      return null;
    }
    const data = await response.json();
    return data && data.match ? data : null;
  } catch (error) {
    console.warn('[FB Tracker] Similarity check failed:', error);
    return null;
  }
}

function shortenInline(text, maxLength = 64) {
  if (!text) {
    return '';
  }
  if (text.length <= maxLength) {
    return text;
  }
  return `${text.slice(0, maxLength - 3)}...`;
}

function formatSimilarityLabel(similarity) {
  if (!similarity || !similarity.match) {
    return '';
  }
  const match = similarity.match;
  const base = match.title || match.created_by_name || match.url || 'Beitrag';
  const details = [];
  if (similarity.similarity && typeof similarity.similarity.text === 'number') {
    details.push(`Text ${Math.round(similarity.similarity.text * 100)}%`);
  }
  if (similarity.similarity && typeof similarity.similarity.image_distance === 'number') {
    details.push(`Bild Δ${similarity.similarity.image_distance}`);
  }
  const detailText = details.length ? ` (${details.join(', ')})` : '';
  return `Ähnlich zu: ${shortenInline(base, 64)}${detailText}`;
}

async function attachUrlToExistingPost(postId, urls, payload = {}) {
  if (!postId) {
    return false;
  }
  try {
    const body = {
      urls: Array.isArray(urls) ? urls : [],
      skip_content_key_check: true
    };
    if (payload.firstImageHash) {
      body.first_image_hash = payload.firstImageHash;
    }
    if (payload.firstImageUrl) {
      body.first_image_url = payload.firstImageUrl;
    }
    const response = await backendFetch(`${API_URL}/posts/${postId}/urls`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    });
    return response.ok;
  } catch (error) {
    console.warn('[FB Tracker] Failed to attach URL to existing post:', error);
    return false;
  }
}

// Check if post is already tracked (checks all URL candidates to avoid duplicates)
async function checkPostStatus(postUrl, allUrlCandidates = []) {
  try {
    const normalizedUrl = normalizeFacebookPostUrl(postUrl);
    if (!normalizedUrl) {
      console.warn('[FB Tracker] Überspringe Statusabfrage, URL ungültig:', postUrl);
      return null;
    }

    // Build list of URLs to check (primary + all candidates)
    const urlsToCheck = [normalizedUrl];

    console.log('[FB Tracker] Received candidates to check:', allUrlCandidates);

    for (const candidate of allUrlCandidates) {
      const normalized = normalizeFacebookPostUrl(candidate);
      if (normalized && !urlsToCheck.includes(normalized)) {
        urlsToCheck.push(normalized);
      }
    }

    const photoHostVariants = [];
    for (const candidateUrl of urlsToCheck) {
      const variants = expandPhotoUrlHostVariants(candidateUrl);
      for (const variant of variants) {
        if (!urlsToCheck.includes(variant) && !photoHostVariants.includes(variant)) {
          photoHostVariants.push(variant);
        }
      }
    }

    const allUrlsToCheck = photoHostVariants.length
      ? urlsToCheck.concat(photoHostVariants)
      : urlsToCheck;

    if (photoHostVariants.length) {
      console.log('[FB Tracker] Added photo host variants for status check:', photoHostVariants);
    }
    console.log('[FB Tracker] Checking post status for URLs:', allUrlsToCheck);

    let foundPost = null;
    let foundUrl = null;

    // Check each URL
    for (const url of allUrlsToCheck) {
      const response = await backendFetch(`${API_URL}/posts/by-url?url=${encodeURIComponent(url)}`);

      if (response.ok) {
        const data = await response.json();
        if (data && data.id) {
          console.log('[FB Tracker] Post found with URL:', url, data);
          foundPost = data;
          foundUrl = url;
          break;
        } else {
          console.log('[FB Tracker] URL not found in backend:', url);
        }
      } else {
        console.log('[FB Tracker] Backend error for URL:', url, response.status);
      }
    }

    // If post found and we have a better main post URL, update it
    if (foundPost && foundUrl !== normalizedUrl) {
      const isMainPostUrl = normalizedUrl.includes('/posts/') || normalizedUrl.includes('/permalink/');
      const isPhotoUrl = foundUrl.includes('/photo');

      if (isMainPostUrl && isPhotoUrl) {
        console.log('[FB Tracker] Updating post URL from photo link to main post link:', foundUrl, '->', normalizedUrl);
        await updatePostUrl(foundPost.id, normalizedUrl);
        foundPost.url = normalizedUrl; // Update local copy
      }
    }

    if (foundPost) {
      const urlsForPersistence = allUrlsToCheck.filter(urlValue => urlValue && urlValue !== foundPost.url);
      if (urlsForPersistence.length) {
        await persistAlternatePostUrls(foundPost.id, urlsForPersistence);
      }
      return foundPost;
    }

    console.log('[FB Tracker] Post not tracked yet (checked', allUrlsToCheck.length, 'URLs)');
    return null;
  } catch (error) {
    console.error('[FB Tracker] Error checking post status:', error);
    return null;
  }
}

async function recordSearchResultPost(primaryUrl, allUrlCandidates = [], options = {}) {
  try {
    if (!primaryUrl) {
      return null;
    }

    const { skipIncrement = false, forceHide = false, sportsAutoHide = false } = options || {};

    const payload = {
      url: primaryUrl,
      candidates: Array.isArray(allUrlCandidates) ? allUrlCandidates : [],
      skip_increment: !!skipIncrement,
      force_hide: !!forceHide,
      sports_auto_hide: !!sportsAutoHide
    };

    const response = await backendFetch(`${API_URL}/search-posts`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      console.warn('[FB Tracker] Failed to record search post occurrence:', response.status);
      return null;
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('[FB Tracker] Error recording search result post:', error);
    return null;
  }
}

// Update post URL
async function updatePostUrl(postId, newUrl) {
  try {
    const response = await backendFetch(`${API_URL}/posts/${postId}`, {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url: newUrl })
    });

    if (response.ok) {
      console.log('[FB Tracker] Post URL updated successfully');
      return true;
    } else {
      console.error('[FB Tracker] Failed to update post URL:', response.status);
      return false;
    }
  } catch (error) {
    console.error('[FB Tracker] Error updating post URL:', error);
    return false;
  }
}

async function persistAlternatePostUrls(postId, urls = []) {
  if (!postId || !Array.isArray(urls) || urls.length === 0) {
    return;
  }

  const uniqueUrls = Array.from(new Set(urls.filter(url => typeof url === 'string' && url.trim())));
  if (!uniqueUrls.length) {
    return;
  }

  try {
    await backendFetch(`${API_URL}/posts/${postId}/urls`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ urls: uniqueUrls })
    });
  } catch (error) {
    console.debug('[FB Tracker] Persisting alternate URLs failed:', error);
  }
}

// Add post to tracking
async function markPostChecked(postId, profileNumber, options = {}) {
  try {
    const ignoreOrder = options && options.ignoreOrder === true;
    const returnError = options && options.returnError === true;
    const allowOutOfOrderOnConflict = options && options.allowOutOfOrderOnConflict !== false;
    console.log('[FB Tracker] Marking post as checked:', postId, 'Profile:', profileNumber);
    const response = await backendFetch(`${API_URL}/posts/${postId}/check`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        profile_number: profileNumber,
        ignore_order: ignoreOrder
      })
    });

    if (response.ok) {
      const data = await response.json();
      console.log('[FB Tracker] Post marked as checked:', data);
      return data;
    }

    if (response.status === 409) {
      const payload = await response.json().catch(() => ({}));
      const message = payload && payload.error ? payload.error : 'Beitrag kann aktuell nicht bestätigt werden.';
      console.log('[FB Tracker] Post check blocked:', message);
      if (!ignoreOrder && allowOutOfOrderOnConflict && Array.isArray(payload.missing_profiles) && payload.missing_profiles.length) {
        return markPostChecked(postId, profileNumber, {
          ...options,
          ignoreOrder: true,
          returnError: true,
          allowOutOfOrderOnConflict: false
        });
      }
      return returnError ? { error: message, status: response.status } : null;
    }

    console.error('[FB Tracker] Failed to mark post as checked:', response.status);
    return returnError
      ? { error: 'Beitrag konnte nicht bestätigt werden.', status: response.status }
      : null;
  } catch (error) {
    console.error('[FB Tracker] Error marking post as checked:', error);
    return (options && options.returnError)
      ? { error: 'Beitrag konnte nicht bestätigt werden.', status: 0 }
      : null;
  }
}

async function addPostToTracking(postUrl, targetCount, profileNumber, options = {}) {
  try {
    console.log('[FB Tracker] Adding post:', postUrl, 'Target:', targetCount, 'Profile:', profileNumber);

    let createdByName = null;
    if (options && options.postElement) {
      createdByName = extractAuthorName(options.postElement) || null;
    }

    let postText = null;
    if (options && typeof options.postText === 'string') {
      postText = options.postText;
    } else if (options && options.postElement) {
      try {
        postText = extractPostText(options.postElement) || null;
      } catch (error) {
        console.debug('[FB Tracker] Failed to extract post text:', error);
      }
    }

    let deadlineIso = null;
    if (options && typeof options.deadline === 'string' && options.deadline.trim()) {
      const parsedDeadline = new Date(options.deadline.trim());
      if (!Number.isNaN(parsedDeadline.getTime())) {
        deadlineIso = parsedDeadline.toISOString();
      }
    }

    const alternateCandidates = Array.isArray(options && options.candidates) ? options.candidates : [];

    const normalizedUrl = normalizeFacebookPostUrl(postUrl);
    if (!normalizedUrl) {
      console.error('[FB Tracker] Post URL konnte nicht normalisiert werden:', postUrl);
      return null;
    }

    const payload = {
      url: normalizedUrl,
      target_count: targetCount,
      profile_number: profileNumber,
      created_by_profile: profileNumber
    };

    if (alternateCandidates.length) {
      payload.alternate_urls = alternateCandidates;
    }

    if (createdByName) {
      payload.created_by_name = createdByName;
    }

    if (deadlineIso) {
      payload.deadline_at = deadlineIso;
    }

    if (postText) {
      payload.post_text = postText;
    }

    if (options && typeof options.firstImageHash === 'string' && options.firstImageHash.trim()) {
      payload.first_image_hash = options.firstImageHash.trim();
    }
    if (options && typeof options.firstImageUrl === 'string' && options.firstImageUrl.trim()) {
      payload.first_image_url = options.firstImageUrl.trim();
    }

    const response = await backendFetch(`${API_URL}/posts`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload)
    });

    if (response.ok) {
      const data = await response.json();
      console.log('[FB Tracker] Post added successfully:', data);

      if (data && data.id) {
        await captureAndUploadScreenshot(data.id, options.postElement || null);
      }

      return data;
    } else {
      console.error('[FB Tracker] Failed to add post:', response.status);
      return null;
    }
  } catch (error) {
    console.error('[FB Tracker] Error adding post:', error);
    return null;
  }
}

function normalizeButtonLabel(button) {
  const aria = button.getAttribute('aria-label');
  if (aria) {
    return aria.trim().toLowerCase();
  }

  const title = button.getAttribute('title');
  if (title) {
    return title.trim().toLowerCase();
  }

  return (button.textContent || '').trim().toLowerCase();
}

const LIKE_LABEL_KEYWORDS = ['gefällt mir', 'like', 'mag ich'];
const COMMENT_LABEL_KEYWORDS = ['kommentieren', 'comment', 'kommentar', 'antwort hinterlassen'];
const SHARE_LABEL_KEYWORDS = ['teilen', 'share', 'senden', 'posten', 'profil posten'];
const REPLY_LABEL_KEYWORDS = ['antworten', 'reply'];

const LIKE_ROLE_KEYWORDS = ['gefällt mir', 'like'];
const COMMENT_ROLE_KEYWORDS = ['comment'];
const SHARE_ROLE_KEYWORDS = ['share'];
const REPLY_ROLE_KEYWORDS = ['reply'];

function matchesKeyword(label, keywords) {
  return keywords.some((keyword) => label.includes(keyword));
}

function styleIndicatesLiked(styleValue) {
  if (!styleValue || typeof styleValue !== 'string') {
    return false;
  }
  const normalized = styleValue.toLowerCase();
  return (
    normalized.includes('reaction-like')
    || normalized.includes('#0866ff')
    || normalized.includes('rgb(8, 102, 255)')
    || normalized.includes('--reaction-like')
  );
}

function elementIndicatesLiked(element) {
  if (!element) {
    return false;
  }

  const inlineStyle = (element.getAttribute('style') || '').trim();
  if (styleIndicatesLiked(inlineStyle)) {
    return true;
  }

  try {
    const computed = window.getComputedStyle(element);
    if (computed && computed.color && styleIndicatesLiked(computed.color)) {
      return true;
    }
  } catch (error) {
    console.debug('[FB Tracker] Unable to inspect computed style:', error);
  }

  return false;
}

function isPostLikedByCurrentUser(likeButton, postElement) {
  const candidates = [];
  if (likeButton) {
    candidates.push(likeButton);
  }
  if (postElement) {
    postElement.querySelectorAll('[data-ad-rendering-role*="gefällt" i], [aria-label*="gefällt" i]').forEach((node) => {
      if (node && !candidates.includes(node)) {
        candidates.push(node);
      }
    });
  }

  for (const candidate of candidates) {
    if (!candidate) {
      continue;
    }

    if (elementIndicatesLiked(candidate)) {
      return true;
    }

    const styleTarget = candidate.matches('[data-ad-rendering-role*="gefällt" i]')
      ? candidate
      : candidate.querySelector && candidate.querySelector('[data-ad-rendering-role*="gefällt" i]');

    if (styleTarget) {
      if (elementIndicatesLiked(styleTarget)) {
        return true;
      }
    }

    const styledDescendant = candidate.querySelector && candidate.querySelector('[style*="reaction-like"], [style*="#0866FF"], [style*="rgb(8, 102, 255)"], [style*="--reaction-like"]');
    if (styledDescendant && elementIndicatesLiked(styledDescendant)) {
      return true;
    }

    const pressedAncestor = candidate.closest && candidate.closest('[aria-pressed="true"]');
    if (pressedAncestor && pressedAncestor !== candidate) {
      return true;
    }

    const ariaPressed = candidate.getAttribute && candidate.getAttribute('aria-pressed');
    if (ariaPressed && ariaPressed.toLowerCase() === 'true') {
      return true;
    }

    const ariaLabel = (candidate.getAttribute && candidate.getAttribute('aria-label')) || '';
    const buttonText = candidate.textContent || '';
    const combined = `${ariaLabel} ${buttonText}`.toLowerCase();

    const likedIndicators = ['gefällt dir', 'gefällt dir nicht mehr', 'unlike', 'remove like', 'nicht mehr gefällt'];
    if (likedIndicators.some(indicator => combined.includes(indicator))) {
      return true;
    }
  }

  return false;
}

function hidePostElement(postElement) {
  if (!postElement) {
    return;
  }

  const postContainer = ensurePrimaryPostElement(postElement);
  if (postContainer && postContainer.closest(DIALOG_ROOT_SELECTOR)) {
    console.log('[FB Tracker] Skipping hide for dialog/modal context');
    return;
  }
  if (postContainer && !isMainPost(postContainer, null)) {
    console.log('[FB Tracker] Skipping hide for comment container');
    return;
  }
  const removalSelectors = [
    'div[role="complementary"]',
    '[role="listitem"][aria-posinset]',
    'div[aria-posinset]',
    'div[data-ad-comet-feed-verbose-tracking]',
    'div[data-ad-preview]',
    'article[role="article"]',
    'article'
  ];

  let elementToRemove = null;
  for (const selector of removalSelectors) {
    const candidate = postElement.closest(selector);
    if (candidate && candidate !== document.body && candidate !== document.documentElement) {
      elementToRemove = candidate;
      break;
    }
  }

  if (!elementToRemove) {
    elementToRemove = postElement;
  }

  let removalRoot = elementToRemove;
  let parentForTimestampCheck = removalRoot.parentElement;
  while (parentForTimestampCheck && parentForTimestampCheck !== document.body && parentForTimestampCheck !== document.documentElement) {
    const siblings = Array.from(parentForTimestampCheck.children);
    const nonRootSiblings = siblings.filter((sibling) => sibling !== removalRoot);
    if (!nonRootSiblings.length) {
      break;
    }
    const hasOnlyTimestampSiblings = nonRootSiblings.every((sibling) => isTimestampArtifactNode(sibling));
    if (hasOnlyTimestampSiblings) {
      removalRoot = parentForTimestampCheck;
      parentForTimestampCheck = removalRoot.parentElement;
      continue;
    }
    const hasTimestampSibling = nonRootSiblings.some((sibling) => isTimestampArtifactNode(sibling));
    if (hasTimestampSibling && siblings.length <= 3) {
      removalRoot = parentForTimestampCheck;
      parentForTimestampCheck = removalRoot.parentElement;
      continue;
    }
    break;
  }

  let parent = removalRoot.parentElement;

  while (parent && parent !== document.body && parent !== document.documentElement) {
    if (parent.childElementCount > 1) {
      break;
    }

    if (parent.matches('[role="feed"], [role="main"], [role="region"], [data-pagelet], [data-testid="SEARCH_RESULT_CONTAINER"], #ssrb_feed_start')) {
      break;
    }

    removalRoot = parent;
    parent = parent.parentElement;
  }

  removalRoot.setAttribute('data-fb-tracker-hidden', '1');

  const removalParent = removalRoot.parentElement;
  if (removalParent) {
    removalParent.removeChild(removalRoot);

    let current = removalParent;
    while (current && current !== document.body && current !== document.documentElement) {
      if (current.childElementCount > 0) {
        break;
      }
      const nextParent = current.parentElement;
      if (!nextParent) {
        break;
      }
      if (nextParent === document.body || nextParent === document.documentElement) {
        current.remove();
        break;
      }
      if (nextParent.childElementCount > 1) {
        current.remove();
        break;
      }
      current.remove();
      current = nextParent;
    }
  } else {
    removalRoot.style.display = 'none';
  }

  if (removalParent) {
    cleanupDanglingSearchArtifacts(removalParent);
  } else {
    cleanupDanglingSearchArtifacts(document);
  }
}

function collectButtonMeta(button) {
  const textParts = [];
  const roleParts = [];

  const label = normalizeButtonLabel(button);
  if (label) {
    textParts.push(label);
  }

  const collectRole = (value) => {
    if (!value) {
      return;
    }
    const lower = value.toLowerCase();
    roleParts.push(lower);

    const tokens = lower.split(/[_\-\s]+/);
    tokens.forEach((token) => {
      if (token) {
        roleParts.push(token);
      }
    });
  };

  collectRole(button.getAttribute('data-ad-rendering-role'));

  const descendantRoles = button.querySelectorAll('[data-ad-rendering-role]');
  descendantRoles.forEach((el) => {
    collectRole(el.getAttribute('data-ad-rendering-role'));
    if (el.textContent) {
      textParts.push(normalizeButtonLabel(el));
    }
  });

  return {
    text: textParts.join(' '),
    roles: roleParts.join(' ')
  };
}

function buttonClassification(button) {
  const meta = collectButtonMeta(button);
  const text = meta.text;
  const roles = meta.roles;
  const combined = `${text} ${roles}`;
  return {
    isLike: matchesKeyword(combined, LIKE_LABEL_KEYWORDS) || matchesKeyword(roles, LIKE_ROLE_KEYWORDS),
    isComment: matchesKeyword(combined, COMMENT_LABEL_KEYWORDS) || matchesKeyword(roles, COMMENT_ROLE_KEYWORDS),
    isShare: matchesKeyword(combined, SHARE_LABEL_KEYWORDS) || matchesKeyword(roles, SHARE_ROLE_KEYWORDS),
    isReply: matchesKeyword(combined, REPLY_LABEL_KEYWORDS) || matchesKeyword(roles, REPLY_ROLE_KEYWORDS)
  };
}

function findShareButtonForArticle(article) {
  const direct = article.querySelector('[data-ad-rendering-role="share_button"]');
  if (direct) {
    return direct;
  }

  const shareButtons = document.querySelectorAll('[data-ad-rendering-role="share_button"]');
  for (const button of shareButtons) {
    const owningArticle = button.closest('[role="article"]');
    if (owningArticle === article) {
      return button;
    }
  }

  return null;
}

function hasInteractionButtons(container) {
  if (!container) {
    return false;
  }

  const buttons = container.querySelectorAll('[role="button"], button');
  if (!buttons.length) {
    return false;
  }

  let hasLike = false;
  let hasComment = false;
  let hasShare = false;
  let hasReply = false;

  for (const button of buttons) {
    const info = buttonClassification(button);
    if (info.isLike) {
      hasLike = true;
    }
    if (info.isComment) {
      hasComment = true;
    }
    if (info.isShare) {
      hasShare = true;
    }
    if (info.isReply) {
      hasReply = true;
    }
  }

  if (hasLike || hasComment || hasShare) {
    console.log('[FB Tracker] Container analysis', {
      tag: container.tagName,
      classes: container.className,
      hasLike,
      hasComment,
      hasShare,
      hasReply,
      buttonCount: buttons.length
    });
  }

  const interactionCount = [hasLike, hasComment, hasShare].filter(Boolean).length;
  if (interactionCount === 0) {
    return false;
  }

  if (hasShare) {
    return true;
  }

  if (interactionCount >= 2) {
    return true;
  }

  return !hasReply;
}

function findButtonBar(postElement) {
  const shareAnchor = findShareButtonForArticle(postElement);
  if (shareAnchor) {
    let container = shareAnchor.closest('[role="button"]') || shareAnchor.closest('button');
    for (let i = 0; i < 4 && container; i++) {
      if (hasInteractionButtons(container)) {
        console.log('[FB Tracker] Found button bar via share anchor');
        return container;
      }
      container = container.parentElement;
    }
  }

  // Gather all accessible buttons inside the article
  const buttons = Array.from(postElement.querySelectorAll('[role="button"], button'));
  const interactionButtons = buttons.filter((button) => {
    const info = buttonClassification(button);
    return info.isLike || info.isComment || info.isShare;
  });

  const anchorElements = [
    postElement.querySelector('[data-ad-rendering-role="share_button"]'),
    postElement.querySelector('[data-ad-rendering-role="comment_button"]'),
    postElement.querySelector('[data-ad-rendering-role*="gefällt"]')
  ].filter(Boolean);

  const candidates = new Set(interactionButtons);
  anchorElements.forEach((el) => {
    const buttonContainer = el.closest('[role="button"]') || el.closest('button');
    if (buttonContainer) {
      candidates.add(buttonContainer);
    }
  });

  const seen = new Set();

  for (const button of candidates) {
    const info = buttonClassification(button);
    console.log('[FB Tracker] Candidate button', {
      label: normalizeButtonLabel(button),
      hasLike: info.isLike,
      hasComment: info.isComment,
      hasShare: info.isShare,
      hasReply: info.isReply,
      classes: button.className
    });
    let current = button;
    while (current && current !== postElement && current !== document.body) {
      if (seen.has(current)) {
        current = current.parentElement;
        continue;
      }

      seen.add(current);

      const hasBar = hasInteractionButtons(current);
      if (hasBar) {
        console.log('[FB Tracker] Found button bar');
        return current;
      }

      current = current.parentElement;
    }
  }

  let sibling = postElement.nextElementSibling;
  for (let i = 0; i < 6 && sibling; i++) {
    if (!seen.has(sibling) && hasInteractionButtons(sibling)) {
      console.log('[FB Tracker] Found button bar via next sibling');
      return sibling;
    }
    sibling = sibling.nextElementSibling;
  }

  sibling = postElement.previousElementSibling;
  for (let i = 0; i < 3 && sibling; i++) {
    if (!seen.has(sibling) && hasInteractionButtons(sibling)) {
      console.log('[FB Tracker] Found button bar via previous sibling');
      return sibling;
    }
    sibling = sibling.previousElementSibling;
  }

  let parent = postElement.parentElement;
  for (let depth = 0; depth < 4 && parent; depth++) {
    if (!seen.has(parent) && hasInteractionButtons(parent)) {
      console.log('[FB Tracker] Found button bar via parent');
      return parent;
    }

    let next = parent.nextElementSibling;
    for (let i = 0; i < 4 && next; i++) {
      if (!seen.has(next) && hasInteractionButtons(next)) {
        console.log('[FB Tracker] Found button bar via parent sibling');
        return next;
      }
      next = next.nextElementSibling;
    }

    let prev = parent.previousElementSibling;
    for (let i = 0; i < 3 && prev; i++) {
      if (!seen.has(prev) && hasInteractionButtons(prev)) {
        console.log('[FB Tracker] Found button bar via parent previous sibling');
        return prev;
      }
      prev = prev.previousElementSibling;
    }

    parent = parent.parentElement;
  }

  console.log('[FB Tracker] Button bar not found');
  return null;
}

function findPostContainers() {
  const containers = [];
  const seen = new Set();

  const candidateSelectors = [
    'div[role="dialog"] article',
    'div[role="dialog"] div[aria-posinset]',
    '[data-pagelet*="FeedUnit"] article',
    'div[role="main"] article',
    '[data-visualcompletion="ignore-dynamic"] article',
    'div[aria-posinset]',
    'article[role="article"]',
    'article',
    'div[data-pagelet*="Reel"]',
    'div[data-pagelet*="WatchFeed"]',
    'div[data-pagelet*="QPE_PublisherStory"]'
  ];

  if (isOnReelsPage()) {
    candidateSelectors.push('div[role="complementary"]');
  }

  const candidateElements = document.querySelectorAll(candidateSelectors.join(', '));

  candidateElements.forEach((element) => {
    const container = ensurePrimaryPostElement(element);
    if (!container) {
      return;
    }

    if (seen.has(container)) {
      return;
    }

    const buttonBar = findButtonBar(container);
    if (!isMainPost(container, buttonBar)) {
      return;
    }

    const likeButton = findLikeButtonWithin(container);

    seen.add(container);
    if (likeButton || isOnReelsPage()) {
      containers.push({ container, likeButton, buttonBar: buttonBar || null });
    }
  });

  return containers;
}

function findLikeButtonWithin(container) {
  if (!container) {
    return null;
  }

  const selectors = [
    '[data-ad-rendering-role="gefällt"]',
    '[data-ad-rendering-role="gefällt mir_button"]',
    '[data-ad-rendering-role*="gefällt" i]',
    '[aria-label*="gefällt" i]',
    '[aria-label*="like" i]',
    'div[role="button"][aria-pressed]'
  ];

  for (const selector of selectors) {
    const button = container.querySelector(selector);
    if (button) {
      return button;
    }
  }

  return container.querySelector('div[role="button"]');
}

function findLikeButtonWithin(container) {
  if (!container) {
    return null;
  }

  const selectors = [
    '[data-ad-rendering-role="gefällt"]',
    '[data-ad-rendering-role="gefällt mir_button"]',
    '[data-ad-rendering-role*="gefällt" i]',
    '[aria-label*="gefällt" i]',
    '[aria-label*="like" i]',
    '[aria-pressed="true"]',
    'div[role="button"]'
  ];

  for (const selector of selectors) {
    const button = container.querySelector(selector);
    if (button) {
      return button;
    }
  }

  return null;
}

function captureScreenshot(screenshotRect) {
  return new Promise((resolve) => {
    chrome.runtime.sendMessage({ type: 'captureScreenshot', screenshotRect }, (response) => {
      if (chrome.runtime.lastError) {
        console.warn('[FB Tracker] Screenshot capture failed:', chrome.runtime.lastError.message);
        resolve(null);
        return;
      }

      if (!response || response.error) {
        if (response && response.error) {
          console.warn('[FB Tracker] Screenshot capture reported error:', response.error);
        }
        resolve(null);
        return;
      }

      if (!response.imageData) {
        console.warn('[FB Tracker] Screenshot capture returned no data');
        resolve(null);
        return;
      }

      (async () => {
        try {
          let result = response.imageData;
          if (screenshotRect) {
            const cropped = await cropScreenshot(result, screenshotRect);
            if (cropped) {
              result = cropped;
            }
          }

          resolve(result);
        } catch (error) {
          console.warn('[FB Tracker] Screenshot processing failed:', error);
          resolve(response.imageData);
        }
      })();
    });
  });
}

async function uploadScreenshot(postId, imageData) {
  try {
    const response = await backendFetch(`${API_URL}/posts/${postId}/screenshot`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ imageData })
    });

    if (!response.ok) {
      console.warn('[FB Tracker] Screenshot upload failed:', response.status);
    }
  } catch (error) {
    console.error('[FB Tracker] Screenshot upload error:', error);
  }
}

async function captureAndUploadScreenshot(postId, postElement) {
  const imageData = await captureElementScreenshot(postElement);
  if (!imageData) {
    return;
  }

  const optimized = await maybeDownscaleScreenshot(imageData);
  await uploadScreenshot(postId, optimized);
}

async function captureElementScreenshot(element) {
  if (!element) {
    return await captureScreenshot();
  }

  const horizontalMargin = 32;
  const verticalMargin = 96;
  const maxSegments = 12;
  const delayBetweenScrolls = 200;
  const originalScrollX = window.scrollX;
  const originalScrollY = window.scrollY;
  const devicePixelRatio = window.devicePixelRatio || 1;
  const stickyOffset = getStickyHeaderHeight();

  const segments = [];

  const elementRect = element.getBoundingClientRect();
  const elementTop = elementRect.top + window.scrollY;
  const elementBottom = elementRect.bottom + window.scrollY;
  const documentHeight = document.documentElement.scrollHeight;
  const startY = Math.max(0, elementTop - verticalMargin - stickyOffset);
  const endY = Math.min(documentHeight, elementBottom + verticalMargin);
  const baseDocTop = Math.max(0, elementTop - verticalMargin);

  const restoreScrollPosition = () => {
    window.scrollTo({ top: originalScrollY, left: originalScrollX, behavior: 'auto' });
    if (document.documentElement) {
      document.documentElement.scrollTop = originalScrollY;
      document.documentElement.scrollLeft = originalScrollX;
    }
    if (document.body) {
      document.body.scrollTop = originalScrollY;
      document.body.scrollLeft = originalScrollX;
    }
  };

  try {
    let iteration = 0;
    let targetScroll = startY;

    while (iteration < maxSegments) {
      iteration += 1;
      window.scrollTo({ top: targetScroll, left: window.scrollX, behavior: 'auto' });
      await delay(delayBetweenScrolls);

      const rect = element.getBoundingClientRect();
      const viewportHeight = window.innerHeight;

      const captureTop = Math.max(0, rect.top - verticalMargin - stickyOffset);
      const captureBottom = Math.min(viewportHeight, rect.bottom + verticalMargin);
      const captureHeight = captureBottom - captureTop;

      if (captureHeight <= 0) {
        break;
      }

      const captureRect = {
        left: rect.left - horizontalMargin,
        top: captureTop,
        width: rect.width + horizontalMargin * 2,
        height: captureHeight,
        devicePixelRatio
      };

      const segmentData = await captureScreenshot(captureRect);
      if (!segmentData) {
        break;
      }

      const docTop = Math.max(0, window.scrollY + captureTop);
      const docBottom = docTop + captureHeight;

      segments.push({ data: segmentData, docTop, docBottom });

      const reachedBottom = docBottom >= endY - 4;
      if (reachedBottom) {
        break;
      }

      const nextScroll = docBottom - Math.max(0, (viewportHeight - stickyOffset) * 0.5);
      const maxScroll = Math.max(0, endY - viewportHeight);
      targetScroll = Math.min(nextScroll, maxScroll);

      if (targetScroll <= window.scrollY + 1) {
        targetScroll = window.scrollY + Math.max(160, viewportHeight * 0.6);
      }

      if (targetScroll <= window.scrollY + 1 || targetScroll >= endY) {
        break;
      }
    }
  } finally {
    restoreScrollPosition();
    await delay(0);
    restoreScrollPosition();
  }

  if (!segments.length) {
    return await captureScreenshot();
  }

  const stitched = await stitchScreenshotSegments(segments, devicePixelRatio, baseDocTop);
  return stitched;
}

async function stitchScreenshotSegments(segments, devicePixelRatio, baseDocTop) {
  const images = [];
  let maxDocBottom = baseDocTop;

  for (const segment of segments) {
    const img = await loadImage(segment.data);
    if (!img) {
      continue;
    }
    images.push({ img, docTop: segment.docTop, docBottom: segment.docBottom });
    if (segment.docBottom > maxDocBottom) {
      maxDocBottom = segment.docBottom;
    }
  }

  if (!images.length) {
    return null;
  }

  const width = images.reduce((max, item) => Math.max(max, item.img.width), 0);
  const totalHeightPx = Math.max(1, Math.round((maxDocBottom - baseDocTop) * devicePixelRatio));

  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = totalHeightPx;

  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#ffffff';
  ctx.fillRect(0, 0, width, totalHeightPx);

  for (const { img, docTop } of images) {
    const offsetY = Math.round(Math.max(0, docTop - baseDocTop) * devicePixelRatio);
    ctx.drawImage(img, 0, offsetY);
  }

  return canvas.toDataURL('image/jpeg', 0.85);
}

async function cropScreenshot(imageData, rect) {
  if (!rect) {
    return imageData;
  }

  try {
    const image = await loadImage(imageData);
    if (!image) {
      return imageData;
    }

    const ratio = rect.devicePixelRatio || window.devicePixelRatio || 1;

    const rawLeft = (rect.left || 0) * ratio;
    const rawTop = (rect.top || 0) * ratio;
    const rawWidth = (rect.width || image.width) * ratio;
    const rawHeight = (rect.height || image.height) * ratio;

    const rawRight = rawLeft + rawWidth;
    const rawBottom = rawTop + rawHeight;

    const left = Math.max(0, Math.floor(rawLeft));
    const top = Math.max(0, Math.floor(rawTop));
    const right = Math.min(image.width, Math.ceil(rawRight));
    const bottom = Math.min(image.height, Math.ceil(rawBottom));

    const width = Math.max(1, right - left);
    const height = Math.max(1, bottom - top);

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, left, top, width, height, 0, 0, width, height);

    return canvas.toDataURL('image/jpeg', 0.85);
  } catch (error) {
    console.warn('[FB Tracker] Failed to crop screenshot:', error);
    return imageData;
  }
}

async function maybeDownscaleScreenshot(imageData) {
  try {
    const maxWidth = 1600;

    const current = await loadImage(imageData);
    if (!current) {
      return imageData;
    }

    if (current.width <= maxWidth) {
      return imageData;
    }

    const scale = maxWidth / current.width;
    const canvas = document.createElement('canvas');
    canvas.width = Math.round(current.width * scale);
    canvas.height = Math.round(current.height * scale);

    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(current, 0, 0, canvas.width, canvas.height);

    return canvas.toDataURL('image/jpeg', 0.8);
  } catch (error) {
    console.warn('[FB Tracker] Failed to downscale screenshot:', error);
    return imageData;
  }
}

function isLikelyPostImage(img) {
  if (!img) {
    return false;
  }
  const src = img.currentSrc || img.src || '';
  if (!src) {
    return false;
  }
  if (src.startsWith('data:')) {
    return false;
  }
  const lowerSrc = src.toLowerCase();
  if (lowerSrc.includes('emoji') || lowerSrc.includes('static.xx') || lowerSrc.includes('sprite')) {
    return false;
  }
  const width = img.naturalWidth || img.width || 0;
  const height = img.naturalHeight || img.height || 0;
  if (width < 120 || height < 120) {
    return false;
  }
  return true;
}

function waitForImageLoad(img, timeoutMs = 1500) {
  return new Promise((resolve) => {
    if (!img) {
      resolve(false);
      return;
    }
    if (img.complete && img.naturalWidth > 0) {
      resolve(true);
      return;
    }
    let resolved = false;
    const finish = (value) => {
      if (resolved) return;
      resolved = true;
      resolve(value);
    };
    const timer = setTimeout(() => finish(false), timeoutMs);
    img.addEventListener('load', () => {
      clearTimeout(timer);
      finish(true);
    }, { once: true });
    img.addEventListener('error', () => {
      clearTimeout(timer);
      finish(false);
    }, { once: true });
  });
}

function buildDHashFromPixels(imageData) {
  if (!imageData || !imageData.data) {
    return null;
  }
  const { data } = imageData;
  const bits = [];
  for (let y = 0; y < 8; y += 1) {
    for (let x = 0; x < 8; x += 1) {
      const leftIndex = ((y * 9) + x) * 4;
      const rightIndex = ((y * 9) + x + 1) * 4;
      const left = 0.299 * data[leftIndex] + 0.587 * data[leftIndex + 1] + 0.114 * data[leftIndex + 2];
      const right = 0.299 * data[rightIndex] + 0.587 * data[rightIndex + 1] + 0.114 * data[rightIndex + 2];
      bits.push(left > right ? 1 : 0);
    }
  }
  let hex = '';
  for (let i = 0; i < bits.length; i += 4) {
    const value = (bits[i] << 3) | (bits[i + 1] << 2) | (bits[i + 2] << 1) | bits[i + 3];
    hex += value.toString(16);
  }
  return hex.padStart(16, '0');
}

async function computeDHashFromUrl(imageUrl) {
  if (!imageUrl) {
    return null;
  }
  try {
    const response = await fetch(imageUrl);
    if (!response.ok) {
      return null;
    }
    const blob = await response.blob();
    const bitmap = await createImageBitmap(blob);
    const canvas = document.createElement('canvas');
    canvas.width = 9;
    canvas.height = 8;
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      return null;
    }
    ctx.drawImage(bitmap, 0, 0, 9, 8);
    const imageData = ctx.getImageData(0, 0, 9, 8);
    return buildDHashFromPixels(imageData);
  } catch (error) {
    return null;
  }
}

async function getFirstPostImageInfo(postElement) {
  if (!postElement) {
    return { hash: null, url: null };
  }
  const images = Array.from(postElement.querySelectorAll('img')).filter(isLikelyPostImage);
  for (const img of images.slice(0, 5)) {
    const loaded = await waitForImageLoad(img);
    if (!loaded) {
      continue;
    }
    const src = img.currentSrc || img.src;
    const hash = await computeDHashFromUrl(src);
    if (hash) {
      return { hash, url: src };
    }
  }
  return { hash: null, url: null };
}

function getStickyHeaderHeight() {
  try {
    const banner = document.querySelector('[role="banner"], header[role="banner"]');
    if (!banner) {
      return 0;
    }

    const rect = banner.getBoundingClientRect();
    if (!rect || !rect.height) {
      return 0;
    }

    return Math.min(rect.height, 160);
  } catch (error) {
    console.warn('[FB Tracker] Failed to determine sticky header height:', error);
    return 0;
  }
}

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function loadImage(dataUrl) {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => resolve(null);
    img.src = dataUrl;
  });
}

function toDateTimeLocalString(date) {
  const local = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
  return local.toISOString().slice(0, 16);
}

function getNextDayDefaultDeadlineValue() {
  const tomorrow = new Date();
  tomorrow.setHours(0, 0, 0, 0);
  tomorrow.setDate(tomorrow.getDate() + 1);
  return toDateTimeLocalString(tomorrow);
}

function extractDeadlineFromPostText(postElement) {
  if (!postElement) {
    return null;
  }

  // Get all text content from the post
  const textNodes = [];
  const walker = document.createTreeWalker(
    postElement,
    NodeFilter.SHOW_TEXT,
    null,
    false
  );

  let node;
  while (node = walker.nextNode()) {
    if (node.textContent.trim()) {
      textNodes.push(node.textContent.trim());
    }
  }

  const fullText = textNodes.join(' ');
  const normalizedText = fullText
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^\S\r\n]+/g, ' ')
    .replace(/(\d)\s*([.:])\s*(\d)/g, '$1$2$3');
  const normalizedLower = normalizedText.toLowerCase();
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const monthNames = {
    'januar': 1, 'jan': 1,
    'februar': 2, 'feb': 2,
    'märz': 3, 'mär': 3, 'maerz': 3, 'marz': 3,
    'april': 4, 'apr': 4,
    'mai': 5,
    'juni': 6, 'jun': 6,
    'juli': 7, 'jul': 7,
    'august': 8, 'aug': 8,
    'september': 9, 'sep': 9, 'sept': 9,
    'oktober': 10, 'okt': 10,
    'november': 11, 'nov': 11,
    'dezember': 12, 'dez': 12
  };

  // German date patterns
  const patterns = [
    // DD.MM.YYYY or DD.MM.YY (with optional time like ", 23:59Uhr")
    /\b(\d{1,2})\.(\d{1,2})\.(\d{2,4})(?!\d)/g,
    // DD.MM (without year, optional trailing time)
    /\b(\d{1,2})\.(\d{1,2})\.(?!\d)/g
  ];

  const extractTimeAfterIndex = (text, index) => {
    const tail = text.slice(index, index + 80);
    if (/^\s*(?:-|–|—|bis)\s*\d{1,2}\.\d{1,2}\./i.test(tail)) {
      return null;
    }
    const timeMatch = /^\s*(?:\(|\[)?\s*(?:[,;:/|-]|\b)?\s*(?:um|ab|bis|gegen|spaetestens|spatestens|spätestens)?\s*(?:den|dem|am)?\s*(?:ca\.?)?\s*(\d{1,2})(?:[:.](\d{2}))?\s*(?:uhr|h)?\s*(?:\)|\])?/i.exec(tail);
    if (!timeMatch) {
      return null;
    }
    const hour = parseInt(timeMatch[1], 10);
    const minute = typeof timeMatch[2] === 'string' && timeMatch[2].length
      ? parseInt(timeMatch[2], 10)
      : 0;
    if (Number.isNaN(hour) || Number.isNaN(minute)) {
      return null;
    }
    if (hour === 24 && minute === 0) {
      return { hour: 23, minute: 59 };
    }
    if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
      return null;
    }
    return { hour, minute };
  };

  const hasInclusiveKeywordNear = (text, index) => {
    const windowStart = Math.max(0, index - 40);
    const windowText = text.slice(windowStart, index).toLowerCase();
    return /\b(einschlie(?:ß|ss)lich|einschl\.?|inklusive|inkl\.)\b/.test(windowText);
  };

  const hasDeadlineKeywordNear = (text, index) => {
    const windowStart = Math.max(0, index - 50);
    const windowText = text.slice(windowStart, index).toLowerCase();
    return /\b(bis|spaetestens|spatestens|spätestens|teilnahmeschluss|einsendeschluss|anmeldeschluss|anmeldefrist|abgabeschluss|stichtag)\b/.test(windowText);
  };

  const addCandidateDate = (date, hasTime) => {
    if (date > today) {
      foundDates.push({ date, hasTime });
    }
  };

  const weekdayNames = {
    montag: 1,
    dienstag: 2,
    mittwoch: 3,
    donnerstag: 4,
    freitag: 5,
    samstag: 6,
    sonntag: 0
  };

  const getNextWeekdayDate = (weekday, preferNext) => {
    const base = new Date(today);
    let delta = (weekday - base.getDay() + 7) % 7;
    if (delta === 0 && preferNext) {
      delta = 7;
    }
    base.setDate(base.getDate() + delta);
    return base;
  };

  const foundDates = [];
  const rangePatterns = [
    // DD.MM.YYYY - DD.MM.YYYY
    /\b(\d{1,2})\.(\d{1,2})\.(\d{2,4})\s*(?:-|–|—|bis)\s*(\d{1,2})\.(\d{1,2})\.(\d{2,4})\b/i,
    // DD.–DD.MM.YYYY (start day only)
    /\b(\d{1,2})\.\s*(?:-|–|—|bis)\s*(\d{1,2})\.(\d{1,2})\.(\d{2,4})\b/i,
    // vom DD.MM bis DD.MM.YYYY
    /\bvom\s*(\d{1,2})\.(\d{1,2})\.?\s*(?:-|–|—|bis)\s*(\d{1,2})\.(\d{1,2})\.(\d{2,4})\b/i
  ];

  for (const rangePattern of rangePatterns) {
    const rangeMatch = rangePattern.exec(normalizedText);
    if (!rangeMatch) {
      continue;
    }

    const endDay = parseInt(rangeMatch[rangeMatch.length - 3], 10);
    const endMonth = parseInt(rangeMatch[rangeMatch.length - 2], 10);
    let endYear = parseInt(rangeMatch[rangeMatch.length - 1], 10);

    if (endYear < 100) {
      endYear += 2000;
    }

    if (endMonth >= 1 && endMonth <= 12 && endDay >= 1 && endDay <= 31) {
      const endDate = new Date(endYear, endMonth - 1, endDay, 23, 59, 0, 0);
      if (endDate.getMonth() === endMonth - 1 && endDate.getDate() === endDay && endDate > today) {
        return toDateTimeLocalString(endDate);
      }
    }
  }

  // Weekday-only patterns like "bis nächsten Mittwoch" or "Sonntag, 23:59 Uhr"
  const weekdayPattern = /\b(montag|dienstag|mittwoch|donnerstag|freitag|samstag|sonntag)\b/gi;
  let weekdayMatch;
  while ((weekdayMatch = weekdayPattern.exec(normalizedLower)) !== null) {
    const weekdayKey = weekdayMatch[1].toLowerCase();
    const weekday = weekdayNames[weekdayKey];
    if (typeof weekday !== 'number') {
      continue;
    }

    const matchIndex = weekdayMatch.index;
    const windowStart = Math.max(0, matchIndex - 30);
    const windowText = normalizedLower.slice(windowStart, matchIndex);
    const preferNext = /\b(naechst|nachst|kommend)\b/.test(windowText);
    const date = getNextWeekdayDate(weekday, preferNext);
    const timeInfo = extractTimeAfterIndex(normalizedText, weekdayPattern.lastIndex);
    const hasTime = Boolean(timeInfo);

    if (timeInfo) {
      date.setHours(timeInfo.hour, timeInfo.minute, 0, 0);
    } else if (hasInclusiveKeywordNear(normalizedText, matchIndex) || hasDeadlineKeywordNear(normalizedText, matchIndex)) {
      date.setHours(23, 59, 0, 0);
    }

    const recordHasTime = hasTime || hasInclusiveKeywordNear(normalizedText, matchIndex) || hasDeadlineKeywordNear(normalizedText, matchIndex);
    addCandidateDate(date, recordHasTime);
  }

  for (const pattern of patterns) {
    let match;
      while ((match = pattern.exec(normalizedText)) !== null) {
        const day = parseInt(match[1], 10);
        const month = parseInt(match[2], 10);
        let year = match[3] ? parseInt(match[3], 10) : today.getFullYear();
        const matchIndex = match.index;

        // Handle 2-digit years
        if (year < 100) {
          year += 2000;
        }

      // Validate date
      if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
        const date = new Date(year, month - 1, day, 0, 0, 0, 0);

        // Check if date is valid (e.g., not 31.02.)
        if (date.getMonth() === month - 1 && date.getDate() === day) {
          const timeInfo = extractTimeAfterIndex(normalizedText, pattern.lastIndex);
          const hasTime = Boolean(timeInfo);
          if (timeInfo) {
            date.setHours(timeInfo.hour, timeInfo.minute, 0, 0);
          } else if (hasInclusiveKeywordNear(normalizedText, matchIndex) || hasDeadlineKeywordNear(normalizedText, matchIndex)) {
            date.setHours(23, 59, 0, 0);
          }

          const hasInclusiveTime = hasInclusiveKeywordNear(normalizedText, matchIndex);
          const hasDeadlineTime = hasDeadlineKeywordNear(normalizedText, matchIndex);
          const recordHasTime = hasTime || hasInclusiveTime || hasDeadlineTime;
          if (hasInclusiveTime && !hasTime) {
            date.setHours(23, 59, 0, 0);
          }

          addCandidateDate(date, recordHasTime);
        }
      }
    }
  }

  // Pattern for "12. Oktober" or "12 Oktober"
  const monthPattern = /\b(\d{1,2})\.?\s*(januar|jan|februar|feb|märz|mär|maerz|marz|april|apr|mai|juni|jun|juli|jul|august|aug|september|sep|sept|oktober|okt|november|nov|dezember|dez)\s*(\d{2,4})?\b/gi;
  let monthMatch;
  while ((monthMatch = monthPattern.exec(normalizedText)) !== null) {
    const day = parseInt(monthMatch[1], 10);
    const monthStr = monthMatch[2].toLowerCase();
    const month = monthNames[monthStr];
    let year = today.getFullYear();
    if (monthMatch[3]) {
      year = parseInt(monthMatch[3], 10);
      if (year < 100) {
        year += 2000;
      }
    }
    const matchIndex = monthMatch.index;

    if (month && day >= 1 && day <= 31) {
      const date = new Date(year, month - 1, day, 0, 0, 0, 0);

      // Check if date is valid
      if (date.getMonth() === month - 1 && date.getDate() === day) {
        const timeInfo = extractTimeAfterIndex(normalizedText, monthPattern.lastIndex);
        const hasTime = Boolean(timeInfo);
        if (timeInfo) {
          date.setHours(timeInfo.hour, timeInfo.minute, 0, 0);
        } else if (hasInclusiveKeywordNear(normalizedText, matchIndex) || hasDeadlineKeywordNear(normalizedText, matchIndex)) {
          date.setHours(23, 59, 0, 0);
        }

        const hasInclusiveTime = hasInclusiveKeywordNear(normalizedText, matchIndex);
        const hasDeadlineTime = hasDeadlineKeywordNear(normalizedText, matchIndex);
        const recordHasTime = hasTime || hasInclusiveTime || hasDeadlineTime;
        if (hasInclusiveTime && !hasTime) {
          date.setHours(23, 59, 0, 0);
        }

        // If date has passed this year, assume next year
        if (date <= today) {
          date.setFullYear(year + 1);
        }
        addCandidateDate(date, recordHasTime);
      }
    }
  }

  // Prefer dates with explicit time; otherwise fall back to earliest date.
  if (foundDates.length > 0) {
    const withTime = foundDates.filter((entry) => entry.hasTime);
    const candidates = withTime.length ? withTime : foundDates;
    candidates.sort((a, b) => a.date - b.date);
    return toDateTimeLocalString(candidates[0].date);
  }

  return null;
}

function escapeRegex(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function collectKeywordMatches(keywords, text, limit = 20) {
  if (!Array.isArray(keywords) || !keywords.length || !text) {
    return [];
  }
  const found = [];
  for (const keyword of keywords) {
    if (!keyword) continue;
    const pattern = new RegExp(`\\b${escapeRegex(keyword)}\\b`, 'gi');
    const matches = text.match(pattern);
    if (matches && matches.length) {
      found.push(keyword);
      if (found.length >= limit) {
        break;
      }
    }
  }
  return Array.from(new Set(found));
}

function collectRegexMatches(regex, text, limit = 20) {
  if (!regex || !(regex instanceof RegExp) || !text) {
    return [];
  }
  const matches = Array.from(text.matchAll(regex)).map((m) => m[0]);
  if (!matches.length) {
    return [];
  }
  return Array.from(new Set(matches)).slice(0, limit);
}

function filterScorelines(candidates = [], sourceText = '') {
  const filtered = [];
  const lowerSource = typeof sourceText === 'string' ? sourceText.toLowerCase() : '';
  for (const raw of candidates) {
    const value = typeof raw === 'string' ? raw : (raw && raw.value) || '';
    const index = typeof raw === 'string' ? -1 : (raw && typeof raw.index === 'number' ? raw.index : -1);
    const parts = value.split(':').map((part) => part.trim());
    if (parts.length !== 2) {
      continue;
    }
    const [a, b] = parts.map((p) => parseInt(p, 10));
    if (Number.isNaN(a) || Number.isNaN(b)) {
      continue;
    }
    if (a < 0 || b < 0) {
      continue;
    }
    if (a > 15 || b > 15) {
      continue;
    }
    if (index >= 0 && lowerSource) {
      const contextStart = Math.max(0, index - 12);
      const contextEnd = Math.min(lowerSource.length, index + value.length + 8);
      const context = lowerSource.slice(contextStart, contextEnd);
      const before = lowerSource.slice(Math.max(0, index - 6), index);
      const hasTimeIndicatorBefore = /\bum\s*$/.test(before);
      const hasTimeIndicatorAfter = /\buhr/.test(context);
      if (hasTimeIndicatorBefore || hasTimeIndicatorAfter) {
        continue;
      }
    }
    filtered.push(`${a}:${b}`);
  }
  return filtered;
}

function evaluateSportsScore(text, moderationSettings = null) {
  if (!text || typeof text !== 'string') {
    return null;
  }

  const normalizedText = text.toLowerCase();
  const weights = {
    ...SPORTS_SCORING_DEFAULTS.weights,
    ...(moderationSettings && moderationSettings.sports_score_weights ? moderationSettings.sports_score_weights : {})
  };
  const threshold = moderationSettings && typeof moderationSettings.sports_score_threshold === 'number'
    ? moderationSettings.sports_score_threshold
    : SPORTS_SCORING_DEFAULTS.threshold;
  const terms = (() => {
    const base = DEFAULT_SPORT_TERMS;
    const incoming = moderationSettings && moderationSettings.sports_terms ? moderationSettings.sports_terms : null;
    const normalizeList = (list, fallback) => {
      if (!Array.isArray(list)) return fallback;
      const cleaned = list
        .map((entry) => typeof entry === 'string' ? entry.trim().toLowerCase() : '')
        .filter((entry) => entry);
      const unique = Array.from(new Set(cleaned)).slice(0, 200);
      return unique.length ? unique : fallback;
    };
    const src = incoming && typeof incoming === 'object' ? incoming : {};
    return {
      nouns: normalizeList(src.nouns, base.nouns),
      verbs: normalizeList(src.verbs, base.verbs),
      competitions: normalizeList(src.competitions, base.competitions),
      celebrations: normalizeList(src.celebrations, base.celebrations),
      locations: normalizeList(src.locations, base.locations),
      negatives: normalizeList(src.negatives, base.negatives)
    };
  })();

  const matchesCount = (regex) => {
    if (!regex || !(regex instanceof RegExp)) {
      return 0;
    }
    const matches = normalizedText.match(regex);
    return matches ? matches.length : 0;
  };

  const applyWeight = (count, weight, label, matches = []) => {
    if (!count || !weight) {
      return 0;
    }
    const effective = Math.min(count, 5);
    const gain = effective * weight;
    if (matches && matches.length) {
      hitDetails.push(`${label}: ${matches.slice(0, 10).join(', ')}`);
    } else {
      hitDetails.push(`${label} x${effective} (+${gain.toFixed(1)})`);
    }
    score += gain;
    return gain;
  };

  const hitDetails = [];
  let score = 0;

  const scorelineMatchesRaw = Array.from(normalizedText.matchAll(/\b\d{1,2}\s*:\s*\d{1,2}\b/g))
    .map((match) => ({
      value: match[0],
      index: typeof match.index === 'number' ? match.index : -1
    }));
  const scorelineMatches = filterScorelines(scorelineMatchesRaw, normalizedText);
  applyWeight(scorelineMatches.length, weights.scoreline, 'Ergebnis', scorelineMatches);

  const scoreEmojiMatches = collectRegexMatches(/[0-9]️⃣/g, normalizedText)
    .concat(collectRegexMatches(/\+\s*\d\b/g, normalizedText));
  applyWeight(scoreEmojiMatches.length, weights.scoreEmoji, 'Punkte', scoreEmojiMatches);

  const sportEmojiMatches = collectRegexMatches(/[⚽🏐🏀🏈🎾🏉🥅🏒🏑🏓🏸🤾🏏🎽🎳🥊🥋⛳]/g, normalizedText);
  applyWeight(sportEmojiMatches.length, weights.sportEmoji, 'Sport-Emoji', sportEmojiMatches);

  const verbMatches = collectKeywordMatches(terms.verbs, normalizedText);
  applyWeight(verbMatches.length, weights.sportVerb, 'Sport-Verben', verbMatches);

  const nounMatches = collectKeywordMatches(terms.nouns, normalizedText);
  applyWeight(nounMatches.length, weights.sportNoun, 'Sport-Vokabeln', nounMatches);

  const hashtagMatches = collectRegexMatches(/#(?:auswärtssieg|heimsieg|derbysieg|bundesliga|liga|pokal|cup|fc[a-z0-9]+|sv[a-z0-9]+|tsv[a-z0-9]+|sg[a-z0-9]+)/g, normalizedText);
  applyWeight(hashtagMatches.length, weights.hashtag, 'Sport-Hashtags', hashtagMatches);

  const teamMatches = collectRegexMatches(/\b(?:fc|sv|tsv|ssv|bvb|sge|fcb|hsv|vfb|fsv|sg|scl|djk)[\s\-]?[a-zäöüß0-9]+/gi, normalizedText);
  applyWeight(teamMatches.length, weights.teamToken, 'Team-Kürzel', teamMatches);

  const competitionMatches = collectKeywordMatches(terms.competitions, normalizedText);
  applyWeight(competitionMatches.length, weights.competition, 'Liga/Turnier', competitionMatches);

  const celebrationMatches = collectKeywordMatches(terms.celebrations, normalizedText);
  applyWeight(celebrationMatches.length, weights.celebration, 'Ergebnisbezug', celebrationMatches);

  const locationMatches = collectKeywordMatches(terms.locations, normalizedText);
  applyWeight(locationMatches.length, weights.location, 'Spielort', locationMatches);

  const nonSportMatches = collectKeywordMatches(terms.negatives, normalizedText);
  const nonSportHits = nonSportMatches.length;
  if (nonSportHits) {
    const penalty = Math.min(2, nonSportHits) * 1;
    score -= penalty;
    hitDetails.push(`Gegenindizien: ${nonSportMatches.slice(0, 10).join(', ')}`);
  }

  const finalScore = Math.round(score * 10) / 10;
  return {
    score: finalScore,
    threshold,
    wouldHide: finalScore >= threshold,
    hits: hitDetails
  };
}

function buildSportsScoreBadge(scoreInfo) {
  if (!scoreInfo) {
    return null;
  }
  // Show badge only for strictly positive scores; hide zero/negative
  if (typeof scoreInfo.score !== 'number' || scoreInfo.score <= 0) {
    return null;
  }
  const badge = document.createElement('span');
  const wouldHide = !!scoreInfo.wouldHide;
  const bg = wouldHide ? 'rgba(245, 158, 11, 0.18)' : 'rgba(59, 130, 246, 0.12)';
  const border = wouldHide ? 'rgba(245, 158, 11, 0.5)' : 'rgba(59, 130, 246, 0.35)';
  const color = wouldHide ? '#b45309' : '#1d4ed8';
  badge.className = 'fb-tracker-score-badge';
  badge.textContent = `Sport-Score ${scoreInfo.score.toFixed(1)} / ${scoreInfo.threshold}`;
  if (scoreInfo.hits && scoreInfo.hits.length) {
    const lines = scoreInfo.hits.map((hit) => `• ${hit}`).join('\n');
    badge.title = `${lines}\n${wouldHide ? '≥ Schwellwert' : '< Schwellwert'}`;
  } else {
    badge.title = wouldHide ? 'Über Schwellwert' : 'Unter Schwellwert';
  }
  badge.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: ${bg};
    border: 1px solid ${border};
    border-radius: 999px;
    font-weight: 600;
    color: ${color};
    font-size: 12px;
  `;
  return badge;
}

function normalizeFacebookPostUrl(rawValue) {
  if (typeof rawValue !== 'string') {
    return '';
  }

  let value = rawValue.trim();
  if (!value) {
    return '';
  }

  const trackingIndex = value.indexOf('__cft__');
  if (trackingIndex !== -1) {
    value = value.slice(0, trackingIndex);
  }

  value = value.replace(/[?&]$/, '');

  let parsed;
  try {
    parsed = new URL(value);
  } catch (error) {
    try {
      parsed = new URL(value, 'https://www.facebook.com');
    } catch (fallbackError) {
      return '';
    }
  }

  if (!parsed.hostname.toLowerCase().endsWith('facebook.com')) {
    return '';
  }

  const normalizedPathBeforeTrim = parsed.pathname.replace(/\/+$/, '') || '/';
  const lowerPathBeforeTrim = normalizedPathBeforeTrim.toLowerCase();
  const watchId = parsed.searchParams.get('v') || parsed.searchParams.get('video_id');
  if ((lowerPathBeforeTrim === '/watch' || lowerPathBeforeTrim === '/video.php') && watchId) {
    parsed.pathname = `/reel/${watchId}/`;
    parsed.search = '';
  } else {
    const reelMatch = lowerPathBeforeTrim.match(/^\/reel\/([^/]+)$/);
    if (reelMatch) {
      parsed.pathname = `/reel/${reelMatch[1]}/`;
      parsed.search = '';
    }
  }

  const cleanedParams = new URLSearchParams();
  parsed.searchParams.forEach((paramValue, paramKey) => {
    const lowerKey = paramKey.toLowerCase();
    const isSingleUnitParam = lowerKey === 's' && paramValue === 'single_unit';
    if (
      lowerKey.startsWith('__cft__')
      || lowerKey.startsWith('__tn__')
      || lowerKey.startsWith('__eep__')
      || lowerKey.startsWith('mibextid')
      || lowerKey === 'set'
      || lowerKey === 'comment_id'
      || lowerKey === 'hoisted_section_header_type'
      || isSingleUnitParam
    ) {
      return;
    }
    cleanedParams.append(paramKey, paramValue);
  });

  const multiPermalinkId = cleanedParams.get('multi_permalinks');
  if (multiPermalinkId) {
    cleanedParams.delete('multi_permalinks');

    const groupMatch = parsed.pathname.match(/^\/groups\/([A-Za-z0-9\.\-_]+)/);
    if (groupMatch && multiPermalinkId.match(/^[0-9]+$/)) {
      parsed.pathname = `/groups/${groupMatch[1]}/posts/${multiPermalinkId}`;
    } else if (groupMatch) {
      parsed.pathname = `/groups/${groupMatch[1]}/permalink/${multiPermalinkId}`;
    }
  }

  const normalizedPath = parsed.pathname.replace(/\/+$/, '').toLowerCase();
  if (normalizedPath.startsWith('/hashtag/') || normalizedPath.startsWith('/watch/hashtag/')) {
    return '';
  }

  const search = cleanedParams.toString();
  const formatted = `${parsed.origin}${parsed.pathname}${search ? `?${search}` : ''}`;
  return formatted.replace(/[?&]$/, '');
}

async function renderTrackedStatus({
  container,
  postElement,
  postData,
  profileNumber,
  isFeedHome,
  isDialogContext,
  manualHideInfo,
  encodedUrl,
  postNum
}) {
  if (!postData) {
    container.innerHTML = '';
    return { hidden: false };
  }

  const parsedProfile = parseInt(profileNumber, 10);
  const effectiveProfileNumber = Number.isNaN(parsedProfile) ? null : parsedProfile;

  if (postData.id) {
    container.dataset.postId = postData.id;
  }

  const checks = Array.isArray(postData.checks) ? postData.checks : [];
  const checkedCount = postData.checked_count ?? checks.length;
  const targetTotal = postData.target_count || checks.length || 0;
  const statusText = `${checkedCount}/${targetTotal}`;
  const completed = checkedCount >= targetTotal && targetTotal > 0;
  const lastCheck = checks.length
    ? new Date(checks[checks.length - 1].checked_at).toLocaleString('de-DE', { timeZone: 'Europe/Berlin' })
    : null;

  const isExpired = postData.deadline_at ? new Date(postData.deadline_at) < new Date() : false;
  const requiredProfiles = Array.isArray(postData.required_profiles) && postData.required_profiles.length
    ? postData.required_profiles
      .map((value) => {
        const parsed = parseInt(value, 10);
        if (Number.isNaN(parsed)) {
          return null;
        }
        return Math.min(5, Math.max(1, parsed));
      })
      .filter(Boolean)
    : Array.from({ length: Math.max(1, Math.min(5, parseInt(postData.target_count, 10) || 1)) }, (_, index) => index + 1);
  const isCurrentProfileRequired = effectiveProfileNumber !== null && requiredProfiles.includes(effectiveProfileNumber);
  const parsedNextRequired = parseInt(postData.next_required_profile, 10);
  const nextRequiredProfile = Number.isNaN(parsedNextRequired) ? null : parsedNextRequired;
  const canCurrentProfileCheck = isCurrentProfileRequired && nextRequiredProfile === effectiveProfileNumber;
  const isCurrentProfileDone = effectiveProfileNumber !== null
    && checks.some(check => check.profile_number === effectiveProfileNumber);

  if (isFeedHome && isCurrentProfileDone) {
    if (isDialogContext) {
      console.log('[FB Tracker] Post #' + postNum + ' - Would hide on feed (already checked by current profile) but skipping in dialog context');
    } else {
      console.log('[FB Tracker] Post #' + postNum + ' - Hidden on feed (already checked by current profile)');
      hidePostElement(postElement);
      processedPostUrls.set(encodedUrl, {
        element: postElement,
        createdAt: Date.now(),
        hidden: true,
        searchSeenCount: manualHideInfo && typeof manualHideInfo.seen_count === 'number'
          ? manualHideInfo.seen_count
          : null
      });
      return { hidden: true };
    }
  }

  const expiredText = isExpired ? ' ⚠️ ABGELAUFEN' : '';

  let statusHtml = `
    <div style="color: #65676b; white-space: nowrap;">
      <strong>Tracker:</strong> ${statusText}${completed ? ' ✓' : ''}${expiredText}
    </div>
    ${lastCheck ? `<div style="color: #65676b; font-size: 12px; white-space: nowrap;">Letzte: ${lastCheck}</div>` : ''}
  `;

  if (!isExpired && !completed && !isCurrentProfileDone && isCurrentProfileRequired) {
    const checkButtonEnabled = canCurrentProfileCheck;
    const buttonColor = checkButtonEnabled ? '#42b72a' : '#f39c12';
    const cursorStyle = 'pointer';
    const buttonTitle = checkButtonEnabled
      ? 'Beitrag bestätigen'
      : 'Wartet auf vorherige Profile';

    statusHtml += `
      <button class="fb-tracker-check-btn" title="${buttonTitle}" style="
        padding: 4px 12px;
        background-color: ${buttonColor};
        color: white;
        border: none;
        border-radius: 4px;
        font-weight: 600;
        cursor: ${cursorStyle};
        font-size: 13px;
        white-space: nowrap;
        margin-left: 8px;
      ">
        ✓ Bestätigen
      </button>
    `;
  } else if (!isExpired && !completed && !isCurrentProfileDone && effectiveProfileNumber === null) {
    statusHtml += `
      <button class="fb-tracker-check-btn" title="Profilstatus nicht geladen" style="
        padding: 4px 12px;
        background-color: #9ca3af;
        color: white;
        border: none;
        border-radius: 4px;
        font-weight: 600;
        cursor: not-allowed;
        font-size: 13px;
        white-space: nowrap;
        margin-left: 8px;
      " disabled>
        ✓ Bestätigen
      </button>
    `;
  } else if (isCurrentProfileDone) {
    statusHtml += `
      <div style="color: #42b72a; font-size: 12px; white-space: nowrap; margin-left: 8px;">
        ✓ Von dir bestätigt
      </div>
    `;
  }

  container.innerHTML = statusHtml;

  if (postData.id) {
    const actionsContainer = ensureTrackerActionsContainer(container);
    if (actionsContainer) {
      const webAppUrl = (() => {
        try {
          const baseUrl = `${WEBAPP_BASE_URL}/`;
          const url = new URL('', baseUrl);
          url.searchParams.set('tab', 'all');
          url.searchParams.set('postId', String(postData.id));
          if (postData.url) {
            url.searchParams.set('postUrl', postData.url);
          }
          return url.toString();
      } catch (error) {
        console.debug('[FB Tracker] Failed to build WebApp URL, falling back to base:', error);
        return `${WEBAPP_BASE_URL}/?tab=all`;
      }
    })();

      let webAppLink = actionsContainer.querySelector('.fb-tracker-webapp-link');
      if (!webAppLink) {
        webAppLink = document.createElement('a');
        webAppLink.className = 'fb-tracker-webapp-link';
        webAppLink.target = '_blank';
        webAppLink.rel = 'noopener noreferrer';
        webAppLink.setAttribute('aria-label', 'In der Webapp anzeigen');
        webAppLink.title = 'In der Webapp anzeigen';
        webAppLink.textContent = '📋';
        webAppLink.style.cssText = `
          text-decoration: none;
          font-size: 18px;
          line-height: 1;
          padding: 4px 6px;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          border-radius: 6px;
          color: inherit;
          transition: background-color 0.2s ease, transform 0.2s ease;
          cursor: pointer;
        `;
        webAppLink.addEventListener('mouseenter', () => {
          webAppLink.style.backgroundColor = 'rgba(0, 0, 0, 0.08)';
          webAppLink.style.transform = 'translateY(-1px)';
        });
        webAppLink.addEventListener('mouseleave', () => {
          webAppLink.style.backgroundColor = 'transparent';
          webAppLink.style.transform = 'translateY(0)';
        });
        actionsContainer.insertBefore(webAppLink, actionsContainer.firstChild);
      }
      webAppLink.href = webAppUrl;
    }
  }

  await addAICommentButton(container, postElement);

  const checkBtn = container.querySelector('.fb-tracker-check-btn');
  if (checkBtn) {
    checkBtn.addEventListener('click', async () => {
      if (effectiveProfileNumber === null) {
        showToast('Profilstatus nicht geladen. Bitte Tracker neu laden.', 'error');
        return;
      }
      checkBtn.disabled = true;
      checkBtn.textContent = 'Wird bestätigt...';

      const result = await markPostChecked(postData.id, effectiveProfileNumber, { returnError: true });

      if (result && !result.error) {
        await renderTrackedStatus({
          container,
          postElement,
          postData: result,
          profileNumber: effectiveProfileNumber,
          isFeedHome,
          isDialogContext,
          manualHideInfo,
          encodedUrl,
          postNum
        });
      } else {
        checkBtn.disabled = false;
        checkBtn.textContent = '✓ Bestätigen';
        if (result && result.error) {
          showToast(result.error, 'error');
        } else {
          checkBtn.textContent = 'Fehler - Erneut versuchen';
          checkBtn.style.backgroundColor = '#e74c3c';
        }
      }
    });
  }

  console.log('[FB Tracker] Showing status:', statusText);
  return { hidden: false };
}

// Create the tracking UI
async function createTrackerUI(postElement, buttonBar, postNum = '?', options = {}) {
  // Normalize to top-level post container if nested element passed
  postElement = ensurePrimaryPostElement(postElement);

  let existingUI = getTrackerElementForPost(postElement);
  if (!existingUI) {
    existingUI = postElement.querySelector('.fb-tracker-ui');
    if (existingUI && existingUI.isConnected) {
      setTrackerElementForPost(postElement, existingUI);
    }
  }

  if (existingUI && !existingUI.isConnected) {
    clearTrackerElementForPost(postElement, existingUI);
    existingUI = null;
  }

  if (existingUI) {
    console.log('[FB Tracker] Post #' + postNum + ' - UI already exists on normalized element');
    postElement.setAttribute(PROCESSED_ATTR, '1');
    return;
  }

  // Mark immediately to prevent duplicate creation during async operations
  if (postElement.getAttribute(PROCESSED_ATTR) === '1') {
    console.log('[FB Tracker] Post #' + postNum + ' - Already processed:', postElement);
    return;
  }
  postElement.setAttribute(PROCESSED_ATTR, '1');

  const postUrlData = getPostUrl(postElement, postNum);
  if (!postUrlData.url) {
    console.log('[FB Tracker] Post #' + postNum + ' - No URL found:', postElement);
    postElement.removeAttribute(PROCESSED_ATTR);
    clearTrackerElementForPost(postElement);
    return;
  }

  console.log('[FB Tracker] Post #' + postNum + ' - Creating tracker UI for:', postUrlData.url, postElement);

  const encodedUrl = encodeURIComponent(postUrlData.url);
  const scopeRoot = getTrackerScopeRoot(postElement);

  const existingScopedTrackers = getTrackersForUrlInScope(encodedUrl, scopeRoot);
  if (existingScopedTrackers.length > 0) {
    const scopedTracker = dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, existingScopedTrackers[0]);
    if (scopedTracker) {
      console.log('[FB Tracker] Post #' + postNum + ' - Reusing existing tracker UI in same scope');
      setTrackerElementForPost(postElement, scopedTracker);
      postElement.setAttribute(PROCESSED_ATTR, '1');
      return;
    }
  }

  const existingEntry = processedPostUrls.get(encodedUrl);
  if (existingEntry && existingEntry.element && existingEntry.element !== postElement) {
    if (document.body.contains(existingEntry.element)) {
      existingEntry.element.removeAttribute(PROCESSED_ATTR);
      clearTrackerElementForPost(existingEntry.element);
    } else {
      processedPostUrls.delete(encodedUrl);
    }
    getTrackersForUrlInScope(encodedUrl, scopeRoot).forEach((tracker) => tracker.remove());
  }

  const { likeButton: sourceLikeButton = null, isSearchResult = false, isDialogContext = false } = options;
  const currentPath = window.location.pathname || '/';
  const isFeedHome = FEED_HOME_PATHS.includes(currentPath);
  const likedByCurrentUser = isPostLikedByCurrentUser(sourceLikeButton, postElement);

  // Create UI container
  const container = document.createElement('div');
  container.className = 'fb-tracker-ui';
  container.id = 'fb-tracker-ui-post-' + postNum;
  container.setAttribute('data-post-num', postNum);
  container.setAttribute('data-post-url', encodedUrl);
  container.dataset.isFeedHome = isFeedHome ? '1' : '0';
  container.dataset.isDialogContext = isDialogContext ? '1' : '0';
  container.style.cssText = `
    padding: 6px 12px;
    background-color: #f0f2f5;
    border-top: 1px solid #e4e6eb;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    row-gap: 6px;
    width: 100%;
    box-sizing: border-box;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 13px;
  `;

  // Check current status (check all URL candidates to avoid duplicates)
  const profileNumber = await getProfileNumber();
  const postData = await checkPostStatus(postUrlData.url, postUrlData.allCandidates);
  const isTracked = !!postData;

  let searchTrackingInfo = null;
  if (isSearchResult) {
    const cacheKey = encodedUrl;
    const alreadyRecorded = sessionSearchRecordedUrls.has(cacheKey);
    const latestInfo = await recordSearchResultPost(postUrlData.url, postUrlData.allCandidates, {
      skipIncrement: alreadyRecorded
    });

    if (!alreadyRecorded && latestInfo) {
      sessionSearchRecordedUrls.add(cacheKey);
    }

    if (latestInfo) {
      sessionSearchInfoCache.set(cacheKey, latestInfo);
      searchTrackingInfo = latestInfo;
    } else if (sessionSearchInfoCache.has(cacheKey)) {
      searchTrackingInfo = sessionSearchInfoCache.get(cacheKey);
    } else {
      searchTrackingInfo = latestInfo;
    }
  }

  let manualHideInfo = null;
  if (!isSearchResult && isFeedHome) {
    try {
      manualHideInfo = await recordSearchResultPost(postUrlData.url, postUrlData.allCandidates, {
        skipIncrement: true
      });
    } catch (error) {
      console.debug('[FB Tracker] Manual hide lookup failed:', error);
    }
  }

  if (isSearchResult && (isTracked || likedByCurrentUser)) {
    if (isDialogContext) {
      console.log('[FB Tracker] Post #' + postNum + ' - Would hide on search results (tracked or liked) but skipping in dialog context');
    } else {
      console.log('[FB Tracker] Post #' + postNum + ' - Hidden on search results (tracked or liked)');
      hidePostElement(postElement);
      processedPostUrls.set(encodedUrl, {
        element: postElement,
        createdAt: Date.now(),
        hidden: true,
        searchSeenCount: searchTrackingInfo && typeof searchTrackingInfo.seen_count === 'number'
          ? searchTrackingInfo.seen_count
          : null
      });
      return;
    }
  }

  if (searchTrackingInfo && searchTrackingInfo.should_hide && !isTracked && !likedByCurrentUser) {
    if (isDialogContext) {
      console.log('[FB Tracker] Post #' + postNum + ' - Would hide on search results after repeated sightings but skipping in dialog context:', searchTrackingInfo);
    } else {
      console.log('[FB Tracker] Post #' + postNum + ' - Hidden on search results after repeated sightings:', searchTrackingInfo);
      hidePostElement(postElement);
      processedPostUrls.set(encodedUrl, {
        element: postElement,
        createdAt: Date.now(),
        hidden: true,
        searchSeenCount: typeof searchTrackingInfo.seen_count === 'number' ? searchTrackingInfo.seen_count : null
      });
      return;
    }
  }

  if (!isSearchResult && manualHideInfo && (manualHideInfo.manually_hidden || manualHideInfo.should_hide)) {
    if (isDialogContext) {
      console.log('[FB Tracker] Post #' + postNum + ' - Would hide on feed (manually hidden) but skipping in dialog context:', manualHideInfo);
    } else {
      console.log('[FB Tracker] Post #' + postNum + ' - Hidden on feed (manually hidden):', manualHideInfo);
      hidePostElement(postElement);
      processedPostUrls.set(encodedUrl, {
        element: postElement,
        createdAt: Date.now(),
        hidden: true,
        searchSeenCount: typeof manualHideInfo.seen_count === 'number' ? manualHideInfo.seen_count : null
      });
      return;
    }
  }

  if (postData) {
    const renderResult = await renderTrackedStatus({
      container,
      postElement,
      postData,
      profileNumber,
      isFeedHome,
      isDialogContext,
      manualHideInfo,
      encodedUrl,
      postNum
    });

    if (renderResult && renderResult.hidden) {
      return;
    }
  } else {
    // Post not tracked - show add UI
    const selectId = `tracker-select-${Date.now()}`;
    const deadlineId = `tracker-deadline-${Date.now()}`;
    container.innerHTML = `
      <label for="${selectId}" style="color: #65676b; font-weight: 500; font-size: 13px; white-space: nowrap;">
        Ziel:
      </label>
      <select id="${selectId}" style="
        padding: 4px 8px;
        border: 1px solid #ccd0d5;
        border-radius: 4px;
        background: white;
        font-size: 13px;
        cursor: pointer;
      ">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
        <option value="5">5</option>
      </select>
      <label for="${deadlineId}" style="color: #65676b; font-weight: 500; font-size: 13px; white-space: nowrap;">
        Deadline:
      </label>
      <input id="${deadlineId}" type="datetime-local" style="
        padding: 4px 8px;
        border: 1px solid #ccd0d5;
        border-radius: 4px;
        font-size: 13px;
        max-width: 160px;
      " />
      <div class="fb-tracker-similarity" style="
        display: none;
        align-items: center;
        gap: 8px;
        padding: 4px 8px;
        border: 1px solid #f0c36d;
        background: #fff6d5;
        border-radius: 6px;
        font-size: 12px;
        color: #6b5b00;
        flex-basis: 100%;
      ">
        <span class="fb-tracker-similarity__text"></span>
        <button class="fb-tracker-merge-btn" type="button" style="
          border: 1px solid #caa848;
          background: white;
          color: #7a5d00;
          padding: 4px 8px;
          border-radius: 6px;
          cursor: pointer;
          font-size: 12px;
          font-weight: 600;
        ">Mergen</button>
        <a class="fb-tracker-similarity-link" href="#" target="_blank" rel="noopener" style="
          color: #7a5d00;
          text-decoration: none;
          font-weight: 600;
          display: none;
        ">Öffnen</a>
      </div>
      <button class="fb-tracker-add-btn" style="
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border: none;
        padding: 6px 12px;
        border-radius: 6px;
        cursor: pointer;
        font-size: 13px;
        font-weight: 600;
        white-space: nowrap;
      ">
        Hinzufügen
      </button>
    `;

    // Add click handler for the button
    const addButton = container.querySelector('.fb-tracker-add-btn');
    const selectElement = container.querySelector(`#${selectId}`);
    const deadlineInput = container.querySelector(`#${deadlineId}`);
    selectElement.value = '2';
    const similarityBox = container.querySelector('.fb-tracker-similarity');
    const similarityText = container.querySelector('.fb-tracker-similarity__text');
    const mergeButton = container.querySelector('.fb-tracker-merge-btn');
    const similarityLink = container.querySelector('.fb-tracker-similarity-link');
    const similarityPayloadPromise = buildSimilarityPayload(postElement);
    let similarityPayload = null;
    let similarityMatch = null;
    const mainLinkUrl = postUrlData.mainUrl;

    const resolveSimilarityPayload = async () => {
      if (!similarityPayload) {
        similarityPayload = await similarityPayloadPromise;
      }
      return similarityPayload;
    };

    if (similarityBox && similarityText && mergeButton) {
      (async () => {
        const payload = await resolveSimilarityPayload();
        const similarity = await findSimilarPost({
          url: postUrlData.url,
          postText: payload.postText,
          firstImageHash: payload.firstImageHash
        });
        if (!similarity || !similarity.match) {
          return;
        }
        similarityMatch = similarity.match;
        similarityText.textContent = formatSimilarityLabel(similarity);
        similarityBox.style.display = 'flex';
        if (!addButton.disabled) {
          addButton.textContent = 'Neu speichern';
        }
        if (similarityLink) {
          similarityLink.style.display = 'inline';
          similarityLink.textContent = 'Öffnen';
          if (similarityMatch.url) {
            similarityLink.href = similarityMatch.url;
            similarityLink.dataset.ready = '1';
          } else {
            similarityLink.href = '#';
            similarityLink.dataset.ready = '0';
            similarityLink.dataset.postId = similarityMatch.id;
          }
        }
      })();

      mergeButton.addEventListener('click', async () => {
        if (!similarityMatch) {
          return;
        }
        mergeButton.disabled = true;
        const previousLabel = mergeButton.textContent;
        mergeButton.textContent = 'Mergen...';

        const payload = await resolveSimilarityPayload();
        const urlCandidates = [postUrlData.url, ...postUrlData.allCandidates];
        const uniqueUrls = Array.from(new Set(urlCandidates.filter(Boolean)));
        const attached = await attachUrlToExistingPost(similarityMatch.id, uniqueUrls, payload);

        if (attached) {
          const updatedPost = await fetchPostByUrl(similarityMatch.url);
          if (updatedPost) {
            await renderTrackedStatus({
              container,
              postElement,
              postData: updatedPost,
              profileNumber,
              isFeedHome,
              isDialogContext,
              manualHideInfo,
              encodedUrl,
              postNum
            });
            return;
          }
        }

        mergeButton.disabled = false;
        mergeButton.textContent = previousLabel;
      });
    }

    if (similarityLink && !similarityLink.dataset.bound) {
      similarityLink.dataset.bound = '1';
      similarityLink.addEventListener('click', async (event) => {
        if (similarityLink.dataset.ready === '1') {
          return;
        }
        event.preventDefault();
        const postId = similarityLink.dataset.postId;
        if (!postId) {
          return;
        }
        const resolved = await fetchPostById(postId);
        if (resolved && resolved.url) {
          similarityLink.href = resolved.url;
          similarityLink.dataset.ready = '1';
          window.open(resolved.url, '_blank', 'noopener');
        }
      });
    }

    if (mainLinkUrl) {
      const mainLinkButton = document.createElement('button');
      mainLinkButton.className = 'fb-tracker-mainlink-btn';
      mainLinkButton.type = 'button';
      mainLinkButton.title = 'Main-Link öffnen';
      mainLinkButton.setAttribute('aria-label', 'Main-Link öffnen');
      mainLinkButton.style.cssText = `
        width: 28px;
        height: 28px;
        padding: 0;
        border-radius: 6px;
        border: 1px solid #ccd0d5;
        background-color: #ffffff;
        background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2365766b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M10 13a5 5 0 0 1 0-7l2-2a5 5 0 0 1 7 7l-1 1'/><path d='M14 11a5 5 0 0 1 0 7l-2 2a5 5 0 0 1-7-7l1-1'/></svg>");
        background-repeat: no-repeat;
        background-position: center;
        background-size: 16px 16px;
        cursor: pointer;
      `;
      addButton.insertAdjacentElement('afterend', mainLinkButton);
      mainLinkButton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        window.open(mainLinkUrl, '_blank', 'noopener');
      });
    }

    if (deadlineInput) {
      // Try to extract deadline from post text first
      const extractedDeadline = extractDeadlineFromPostText(postElement);
      deadlineInput.value = extractedDeadline || getNextDayDefaultDeadlineValue();
    }

    addButton.addEventListener('click', async () => {
      const targetCount = parseInt(selectElement.value, 10);
      console.log('[FB Tracker] Add button clicked, target:', targetCount);

      if (!profileNumber) {
        showToast('Profilstatus nicht geladen. Bitte Tracker neu laden.', 'error');
        addButton.disabled = false;
        addButton.textContent = 'Erneut versuchen';
        return;
      }

      addButton.disabled = true;
      addButton.textContent = 'Wird hinzugefügt...';

      postElement.scrollIntoView({ behavior: 'auto', block: 'start', inline: 'nearest' });
      await delay(220);

      const deadlineValue = deadlineInput ? deadlineInput.value : '';
      const payload = await resolveSimilarityPayload();

      const result = await addPostToTracking(postUrlData.url, targetCount, profileNumber, {
        postElement,
        deadline: deadlineValue,
        candidates: postUrlData.allCandidates,
        postText: payload.postText,
        firstImageHash: payload.firstImageHash,
        firstImageUrl: payload.firstImageUrl
      });

      if (result) {
        const renderOutcome = await renderTrackedStatus({
          container,
          postElement,
          postData: result,
          profileNumber,
          isFeedHome,
          isDialogContext,
          manualHideInfo,
          encodedUrl,
          postNum
        });

        if (renderOutcome && renderOutcome.hidden) {
          return;
        }

        return;
      } else {
        // Error
        addButton.disabled = false;
        addButton.textContent = 'Fehler - Erneut versuchen';
        addButton.style.backgroundColor = '#e74c3c';
        if (deadlineInput) {
          deadlineInput.value = getNextDayDefaultDeadlineValue();
        }
      }
    });

    console.log('[FB Tracker] Post #' + postNum + ' - UI created for new post');

  // Add AI button for new posts
  await addAICommentButton(container, postElement);
  }

  let sportsScoreInfo = null;
  try {
    const moderationSettings = await fetchModerationSettings();
    if (moderationSettings && moderationSettings.sports_scoring_enabled !== false) {
      const postTextForScore = extractPostText(postElement);
      if (postTextForScore) {
        sportsScoreInfo = evaluateSportsScore(postTextForScore, moderationSettings);
      }

      if (
        moderationSettings.sports_auto_hide_enabled
        && sportsScoreInfo
        && sportsScoreInfo.wouldHide
        && !isTracked
        && !likedByCurrentUser
      ) {
        if (isDialogContext) {
          console.log('[FB Tracker] Post #' + postNum + ' - Would auto-hide by sports score but skipping in dialog context');
        } else {
          console.log('[FB Tracker] Post #' + postNum + ' - Auto-hidden by sports score', sportsScoreInfo);
          try {
            await recordSearchResultPost(postUrlData.url, postUrlData.allCandidates, { forceHide: true, sportsAutoHide: true });
          } catch (error) {
            console.debug('[FB Tracker] Auto-hide scoring could not persist hide state:', error);
          }
          hidePostElement(postElement);
          processedPostUrls.set(encodedUrl, {
            element: postElement,
            createdAt: Date.now(),
            hidden: true,
            searchSeenCount: searchTrackingInfo && typeof searchTrackingInfo.seen_count === 'number'
              ? searchTrackingInfo.seen_count
              : null
          });
          return;
        }
      }
    }
  } catch (error) {
    console.debug('[FB Tracker] Sport-Scoring nicht verfügbar:', error);
  }

  if (isSearchResult) {
    const info = document.createElement('button');
    info.type = 'button';
    info.className = 'fb-tracker-search-info';
    info.title = 'Beitrag künftig in den Suchergebnissen ausblenden';
    info.setAttribute('aria-label', 'Beitrag künftig in den Suchergebnissen ausblenden');
    const countText = searchTrackingInfo && typeof searchTrackingInfo.seen_count === 'number'
      ? searchTrackingInfo.seen_count
      : (isTracked ? 'gespeichert' : 'n/v');

    const iconSpan = document.createElement('span');
    iconSpan.setAttribute('aria-hidden', 'true');
    iconSpan.style.fontSize = '15px';
    const countSpan = document.createElement('span');
    countSpan.textContent = countText;

    info.appendChild(iconSpan);
    info.appendChild(countSpan);

    info.style.cssText = `
      color: #1d2129;
      font-weight: 600;
      border-radius: 999px;
      padding: 4px 12px;
      background: rgba(255, 255, 255, 0.98);
      border: 1px solid rgba(29, 33, 41, 0.18);
      display: inline-flex;
      align-items: center;
      gap: 6px;
      box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
      transition: transform 150ms ease, box-shadow 150ms ease, opacity 120ms ease;
      cursor: pointer;
      outline: none;
    `;

    const OPEN_EYES_ICON = '😳';
    const CLOSED_EYES_ICON = '😌';

    const setIconOpen = () => {
      iconSpan.textContent = OPEN_EYES_ICON;
    };

    const setIconClosed = () => {
      iconSpan.textContent = CLOSED_EYES_ICON;
    };

    setIconOpen();

    const resetHover = () => {
      setIconOpen();
      info.style.transform = 'scale(1)';
      info.style.boxShadow = '0 1px 2px rgba(0, 0, 0, 0.08)';
    };

    info.addEventListener('mouseenter', () => {
      info.style.transform = 'translateY(-1px) scale(1.05)';
      info.style.boxShadow = '0 6px 14px rgba(0, 0, 0, 0.16)';
      setIconClosed();
    });
    info.addEventListener('mouseleave', () => {
      resetHover();
    });
    info.addEventListener('focus', () => {
      info.style.transform = 'translateY(-1px) scale(1.05)';
      info.style.boxShadow = '0 6px 14px rgba(24, 119, 242, 0.35)';
      setIconClosed();
    });
    info.addEventListener('blur', () => {
      resetHover();
    });
    info.addEventListener('mousedown', () => {
      info.style.transform = 'translateY(0) scale(0.96)';
      info.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.18)';
    });
    info.addEventListener('mouseup', () => {
      info.style.transform = 'translateY(-1px) scale(1.03)';
      info.style.boxShadow = '0 6px 14px rgba(0, 0, 0, 0.16)';
    });

    const handleManualHide = async (event) => {
      event.preventDefault();
      event.stopPropagation();

      if (postElement.getAttribute('data-fb-tracker-hidden') === '1') {
        return;
      }

      info.disabled = true;
      info.style.cursor = 'progress';
      info.style.opacity = '0.75';

      let hideResult = null;

      try {
        hideResult = await recordSearchResultPost(postUrlData.url, postUrlData.allCandidates, {
          forceHide: true
        });
      } catch (error) {
        console.error('[FB Tracker] Failed to hide search result manually:', error);
      }

      if (!hideResult) {
        info.disabled = false;
        info.style.cursor = 'pointer';
        info.style.opacity = '1';
        resetHover();
        return;
      }

      if (isSearchResult) {
        const cacheKeyForHide = encodedUrl;
        sessionSearchRecordedUrls.add(cacheKeyForHide);
        sessionSearchInfoCache.set(cacheKeyForHide, hideResult);
      }

      setIconClosed();

      if (isDialogContext) {
        console.log('[FB Tracker] Post #' + postNum + ' - Manual hide skipped in dialog context');
      } else {
        hidePostElement(postElement);

        const seenCountValue = typeof hideResult.seen_count === 'number' ? hideResult.seen_count : null;
        processedPostUrls.set(encodedUrl, {
          element: postElement,
          createdAt: Date.now(),
          hidden: true,
          searchSeenCount: seenCountValue
        });
      }
    };

    info.addEventListener('click', handleManualHide);
    resetHover();

    container.insertBefore(info, container.firstChild);
    const sportsScoreBadge = buildSportsScoreBadge(sportsScoreInfo);
    if (sportsScoreBadge) {
      container.insertBefore(sportsScoreBadge, info.nextSibling);
    }
  } else {
    const sportsScoreBadge = buildSportsScoreBadge(sportsScoreInfo);
    if (sportsScoreBadge) {
      container.insertBefore(sportsScoreBadge, container.firstChild);
    }
  }

  // Insert UI - try multiple strategies to find stable insertion point
  let inserted = false;

  const tryInsertBeforeReelsCommentComposer = () => {
    const textboxCandidates = postElement ? postElement.querySelectorAll('div[role="textbox"]') : [];
    const composerElement = Array.from(textboxCandidates).find((element) => {
      const ariaLabel = (element.getAttribute('aria-label') || '').toLowerCase();
      const ariaPlaceholder = (element.getAttribute('aria-placeholder') || '').toLowerCase();
      const combined = `${ariaLabel} ${ariaPlaceholder}`;
      return combined.includes('komment') || combined.includes('comment');
    });

    if (!composerElement) {
      return false;
    }

    const anchorRoot = composerElement.closest('form[role="presentation"]')
      || composerElement.closest('form')
      || composerElement.parentElement;

    if (!anchorRoot || !anchorRoot.parentElement) {
      return false;
    }

    anchorRoot.parentElement.insertBefore(container, anchorRoot);
    console.log('[FB Tracker] Post #' + postNum + ' - UI inserted before comment composer (Reels complementary). ID: #' + container.id);
    return true;
  };

  if (!inserted && isOnReelsPage() && postElement && postElement.matches('div[role="complementary"]')) {
    inserted = tryInsertBeforeReelsCommentComposer();
  }

  const resolvedButtonBar = resolveActionButtonBar(postElement, buttonBar);
  const inPostActionAnchor = getDirectActionAnchorInPost(postElement, resolvedButtonBar);
  const commentSortAnchor = findAdjacentCommentSortAnchor(postElement, inPostActionAnchor);
  const preferredAnchor = commentSortAnchor || inPostActionAnchor;

  // Strategy 1: Insert below actions; if present, place below the sort row
  if (!inserted && preferredAnchor && preferredAnchor.parentElement) {
    preferredAnchor.parentElement.insertBefore(container, preferredAnchor.nextSibling);
    if (commentSortAnchor) {
      console.log('[FB Tracker] Post #' + postNum + ' - UI inserted below comment sort row. ID: #' + container.id);
    } else {
      console.log('[FB Tracker] Post #' + postNum + ' - UI inserted directly below actions. ID: #' + container.id);
    }
    inserted = true;
  }
  // Strategy 2: Insert directly after the detected button bar if still inside the post container
  if (!inserted && resolvedButtonBar && resolvedButtonBar.parentElement && postElement.contains(resolvedButtonBar.parentElement)) {
    resolvedButtonBar.parentElement.insertBefore(container, resolvedButtonBar.nextSibling);
    console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar (in-post fallback). ID: #' + container.id);
    inserted = true;
  }
  // Strategy 3: Legacy fallback for old variants where the interaction bar is outside the post container
  if (!inserted && buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
    const grandParent = buttonBar.parentElement.parentElement;
    grandParent.insertBefore(container, buttonBar.parentElement.nextSibling);
    console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after external button bar parent (legacy). ID: #' + container.id);
    inserted = true;
  }
  // Strategy 4: Append to post element
  if (!inserted) {
    postElement.appendChild(container);
    console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id);
    inserted = true;
  }

  if (inserted) {
    const dedupedTracker = dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, container);
    if (dedupedTracker && dedupedTracker !== container) {
      clearTrackerElementForPost(postElement, container);
    }

    processedPostUrls.set(encodedUrl, {
      element: postElement,
      createdAt: Date.now(),
      searchSeenCount: searchTrackingInfo && typeof searchTrackingInfo.seen_count === 'number'
        ? searchTrackingInfo.seen_count
        : null,
      hidden: false
    });
    setTrackerElementForPost(postElement, dedupedTracker || container);
  }

  // Monitor if the UI gets removed and re-insert it
  if (inserted) {
    const observer = new MutationObserver((mutations) => {
      if (!document.getElementById(container.id)) {
        console.log('[FB Tracker] Post #' + postNum + ' - UI was removed, re-inserting...');
        observer.disconnect();
        // Try to re-insert
        const currentResolvedButtonBar = resolveActionButtonBar(postElement, buttonBar);
        const currentInPostActionAnchor = getDirectActionAnchorInPost(postElement, currentResolvedButtonBar);
        const currentCommentSortAnchor = findAdjacentCommentSortAnchor(postElement, currentInPostActionAnchor);
        const currentPreferredAnchor = currentCommentSortAnchor || currentInPostActionAnchor;
        if (currentPreferredAnchor && currentPreferredAnchor.parentElement) {
          currentPreferredAnchor.parentElement.insertBefore(container, currentPreferredAnchor.nextSibling);
        } else if (currentResolvedButtonBar && currentResolvedButtonBar.parentElement && postElement.contains(currentResolvedButtonBar.parentElement)) {
          currentResolvedButtonBar.parentElement.insertBefore(container, currentResolvedButtonBar.nextSibling);
        } else if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
          buttonBar.parentElement.parentElement.insertBefore(container, buttonBar.parentElement.nextSibling);
        } else if (postElement) {
          postElement.appendChild(container);
        }
        if (container.isConnected) {
          dedupeTrackersForUrlInScope(encodedUrl, scopeRoot, container);
          setTrackerElementForPost(postElement, container);
        }
      }
    });

    // Observe the parent for changes
    const observeTarget = container.parentElement || postElement;
    observer.observe(observeTarget, { childList: true, subtree: false });

    // Stop observing after 5 seconds
    setTimeout(() => observer.disconnect(), 5000);
  }
}

// Check if article is a main post (not a comment)
function isMainPost(article, buttonBar) {
  if (!article || article === document.body) {
    return false;
  }

  if (isOnReelsPage()) {
    if (article.matches('div[role="complementary"]') || article.closest('div[role="complementary"]')) {
      return true;
    }
  }

  const roleDescription = (article.getAttribute('aria-roledescription') || '').toLowerCase();
  if (roleDescription && (roleDescription.includes('kommentar') || roleDescription.includes('comment'))) {
    return false;
  }

  if (article.matches('[data-testid*="comment" i], [data-scope="comment"]')) {
    return false;
  }

  if (article.closest('[data-testid*="comment" i]')) {
    return false;
  }

  if (buttonBar) {
    const shareIndicator = buttonBar.querySelector('[data-ad-rendering-role="share_button"]');
    if (shareIndicator) {
      return true;
    }
  }

  // Comments are usually nested inside other articles or comment sections
  // Check if this article is inside another article (likely a comment)
  let parent = article.parentElement;
  while (parent && parent !== document.body) {
    if (parent.getAttribute('role') === 'article') {
      const parentRoleDescription = (parent.getAttribute('aria-roledescription') || '').toLowerCase();
      if (parentRoleDescription.includes('kommentar') || parentRoleDescription.includes('comment')) {
        return false;
      }
      // This article is inside another article - it's a comment
      return false;
    }
    parent = parent.parentElement;
  }

  // Additional check: Main posts usually have a link with /posts/ or /permalink/
  const postLinks = article.querySelectorAll('a[href*="/posts/"], a[href*="/permalink/"], a[href*="/photo"], a[href*="/videos/"], a[href*="/reel/"]');
  if (postLinks.length === 0) {
    if (article.querySelector('[data-testid*="comment" i]')) {
      return false;
    }
    if (article.querySelector('[data-ad-rendering-role="share_button"]')) {
      return true;
    }
    const nextSibling = article.nextElementSibling;
    if (nextSibling && nextSibling.querySelector('[data-ad-rendering-role="share_button"]')) {
      const nextButtons = nextSibling.querySelectorAll('[role="button"]');
      if (nextButtons.length > 30) {
        return true;
      }
    }
    // No post-type links found - likely a comment
    return false;
  }

  return true;
}

// Global post counter for unique IDs
let globalPostCounter = 0;

// Find all Facebook posts on the page
function findPosts() {
  if (maybeRedirectPageReelsToMain()) {
    return;
  }

  console.log('[FB Tracker] Scanning for posts...');

  const postContainers = findPostContainers();
  const seenFeedContainers = new Set();
  const seenDialogContainers = new Set();

  console.log('[FB Tracker] Found', postContainers.length, 'candidate containers');

  let processed = 0;
  const pathname = window.location.pathname || '';
  const isSearchResultsPage = typeof pathname === 'string' && pathname.startsWith(SEARCH_RESULTS_PATH_PREFIX);

  for (const { container: originalContainer, likeButton, buttonBar: precomputedButtonBar } of postContainers) {
    let container = ensurePrimaryPostElement(originalContainer);
    const dialogRoot = container.closest(DIALOG_ROOT_SELECTOR);
    const isInDialog = !!dialogRoot;
    const seenSet = isInDialog ? seenDialogContainers : seenFeedContainers;

    if (seenSet.has(container)) {
      continue;
    }

    const isPending = container.getAttribute(PENDING_ATTR) === '1';
    if (isPending) {
      seenSet.add(container);
      continue;
    }

    let existingTracker = getTrackerElementForPost(container);
    if (!existingTracker) {
      existingTracker = container.querySelector('.fb-tracker-ui');
      if (existingTracker && existingTracker.isConnected) {
        setTrackerElementForPost(container, existingTracker);
      }
    }

    if (existingTracker && !existingTracker.isConnected) {
      clearTrackerElementForPost(container, existingTracker);
      existingTracker = null;
    }

    const alreadyProcessed = container.getAttribute(PROCESSED_ATTR) === '1';
    const trackerDialogRoot = existingTracker ? existingTracker.closest(DIALOG_ROOT_SELECTOR) : null;
    const trackerInSameDialog = Boolean(
      existingTracker
      && existingTracker.isConnected
      && (
        trackerDialogRoot === dialogRoot
        || (dialogRoot && dialogRoot.contains(existingTracker))
      )
    );

    if (isInDialog) {
      if (trackerInSameDialog) {
        seenSet.add(container);
        continue;
      }

      if (existingTracker && existingTracker.isConnected) {
        existingTracker.remove();
        clearTrackerElementForPost(container, existingTracker);
      }

      if (alreadyProcessed) {
        container.removeAttribute(PROCESSED_ATTR);
      }
    } else {
      if (alreadyProcessed || (existingTracker && existingTracker.isConnected)) {
        seenSet.add(container);
        continue;
      }
    }

    const buttonBar = precomputedButtonBar || findButtonBar(container);
    if (!buttonBar) {
      console.log('[FB Tracker] Proceeding without button bar, will fallback to post container insertion');
    }

    if (!isMainPost(container, buttonBar)) {
      console.log('[FB Tracker] Skipping non-main post container');
      continue;
    }

    seenSet.add(container);

    processed++;
    globalPostCounter++;
    const postNum = globalPostCounter;
    console.log('[FB Tracker] Post #' + postNum + ' - Adding tracker:', container);

    container.setAttribute(PENDING_ATTR, '1');
    createTrackerUI(container, buttonBar, postNum, {
      likeButton,
      isSearchResult: isSearchResultsPage,
      isDialogContext: isInDialog
    }).catch((error) => {
      console.error('[FB Tracker] Post #' + postNum + ' - Failed to create UI:', error, container);
    }).finally(() => {
      container.removeAttribute(PENDING_ATTR);
    });
  }

  console.log('[FB Tracker] Total processed posts this scan:', processed, '| Global count:', globalPostCounter);
}

// Initialize
console.log('[FB Tracker] Initializing...');
maybeRedirectPageReelsToMain();

// Run multiple times to catch loading posts
setTimeout(findPosts, 2000);
setTimeout(findPosts, 4000);
setTimeout(findPosts, 6000);

// Debounced scan function
let scanTimeout = null;
function scheduleScan() {
  if (scanTimeout) {
    clearTimeout(scanTimeout);
  }
  scanTimeout = setTimeout(() => {
    console.log('[FB Tracker] Scheduled scan triggered');
    findPosts();
  }, 1000);
}

// Watch for new posts being added to the page
const observer = new MutationObserver((mutations) => {
  scheduleScan();
});

// Start observing
observer.observe(document.body, {
  childList: true,
  subtree: true
});

// Trigger scan on scroll (for infinite scroll)
let scrollTimeout = null;
window.addEventListener('scroll', () => {
  if (scrollTimeout) {
    clearTimeout(scrollTimeout);
  }
  scrollTimeout = setTimeout(() => {
    console.log('[FB Tracker] Scroll detected, scanning...');
    findPosts();
  }, 1000);
});

// Use IntersectionObserver to detect when posts become visible
const visibilityObserver = new IntersectionObserver((entries) => {
  let needsScan = false;

  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const postContainer = entry.target;

      // Check if already processed
      if (postContainer.getAttribute(PROCESSED_ATTR) !== '1') {
        console.log('[FB Tracker] Post became visible and not yet processed:', postContainer);
        needsScan = true;
      }
    }
  });

  if (needsScan) {
    // Delay scan slightly to let Facebook finish loading the post content
    setTimeout(() => {
      console.log('[FB Tracker] Scanning newly visible posts...');
      findPosts();
    }, 500);
  }
}, {
  root: null,
  rootMargin: '50px',
  threshold: 0.1
});

// Watch for new posts and observe them
const postObserver = new MutationObserver((mutations) => {
  // Find all posts and observe them for visibility
  const postSelector = isOnReelsPage()
    ? 'div[aria-posinset], div[role="complementary"]'
    : 'div[aria-posinset]';
  const posts = document.querySelectorAll(postSelector);
  posts.forEach(post => {
    if (!post.dataset.trackerObserved) {
      post.dataset.trackerObserved = 'true';
      visibilityObserver.observe(post);
    }
  });

  scheduleScan();
});

// Start observing
postObserver.observe(document.body, {
  childList: true,
  subtree: true
});

// Initial observation of existing posts
const initialPostSelector = isOnReelsPage()
  ? 'div[aria-posinset], div[role="complementary"]'
  : 'div[aria-posinset]';
const initialPosts = document.querySelectorAll(initialPostSelector);
initialPosts.forEach(post => {
  post.dataset.trackerObserved = 'true';
  visibilityObserver.observe(post);
});

console.log('[FB Tracker] Observer with IntersectionObserver started');

// Store the element where context menu was opened
let contextMenuTarget = null;
document.addEventListener('contextmenu', (event) => {
  contextMenuTarget = event.target;
  console.log('[FB Tracker] Context menu opened on:', contextMenuTarget);
}, true);

// Floating AI button on text selection
let selectionAIContainer = null;
let selectionAIButton = null;
let selectionAINoteButton = null;
let selectionAIRaf = null;
let selectionAIHideTimeout = null;
let selectionAIEnabledCached = null;
let selectionAIContextElement = null;

const clearSelectionAIHideTimeout = () => {
  if (selectionAIHideTimeout) {
    clearTimeout(selectionAIHideTimeout);
    selectionAIHideTimeout = null;
  }
};

const hideSelectionAIButton = () => {
  clearSelectionAIHideTimeout();
  if (selectionAIContainer) {
    selectionAIContainer.style.display = 'none';
  }
  selectionAIContextElement = null;
  if (selectionAIButton) {
    selectionAIButton.dataset.selectionText = '';
  }
  if (selectionAINoteButton) {
    selectionAINoteButton.dataset.selectionText = '';
  }
};

const ensureSelectionAIButton = () => {
  if (selectionAIContainer && selectionAIContainer.isConnected && selectionAIButton && selectionAINoteButton) {
    return selectionAIContainer;
  }

  const container = document.createElement('div');
  container.style.cssText = `
    position: fixed;
    z-index: 2147483647;
    display: none;
    align-items: center;
    gap: 8px;
    pointer-events: auto;
  `;

  const noteButton = document.createElement('button');
  noteButton.type = 'button';
  noteButton.textContent = '➕ Zusatzinfo';
  noteButton.title = 'Aktuelle Auswahl als Zusatzinfo speichern';
  noteButton.style.cssText = `
    padding: 7px 10px;
    border-radius: 10px;
    border: 1px solid #d1d5db;
    background: #fff;
    color: #111827;
    font-weight: 700;
    font-size: 12px;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
    cursor: pointer;
    transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
    display: inline-flex;
    align-items: center;
    gap: 6px;
  `;
  noteButton.addEventListener('mouseenter', () => {
    noteButton.style.transform = 'translateY(-1px)';
    noteButton.style.boxShadow = '0 8px 18px rgba(0, 0, 0, 0.18)';
  });
  noteButton.addEventListener('mouseleave', () => {
    noteButton.style.transform = 'translateY(0)';
    noteButton.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.12)';
  });
  noteButton.addEventListener('click', (event) => {
    event.preventDefault();
    event.stopPropagation();
    const selectedText = noteButton.dataset.selectionText || '';
    if (!selectedText.trim()) {
      showToast('Keine Textauswahl gefunden', 'error');
      return;
    }
    const selection = window.getSelection();
    const anchorNode = selection ? (selection.anchorNode || selection.focusNode) : null;
    if (anchorNode && isSelectionInsideEditable(anchorNode)) {
      showToast('Textauswahl aus Eingabefeldern kann nicht genutzt werden', 'error');
      return;
    }

    const anchorElement = anchorNode && anchorNode.nodeType === Node.TEXT_NODE
      ? anchorNode.parentElement
      : anchorNode;
    const postContext = selectionAIContextElement
      || (anchorElement ? ensurePrimaryPostElement(anchorElement) : null);
    if (!postContext) {
      showToast('Keinen zugehörigen Beitrag gefunden', 'error');
      return;
    }
    const normalized = normalizeSelectionText(selectedText);
    if (!normalized) {
      showToast('Keine Textauswahl gefunden', 'error');
      return;
    }
    postAdditionalNotes.set(postContext, normalized);
    showToast('Auswahl als Zusatzinfo gesetzt', 'success');
  });

  const button = document.createElement('button');
  button.type = 'button';
  button.textContent = '✨ AI';
  button.title = 'Auswahl mit AI beantworten';
  button.style.cssText = `
    padding: 8px 12px;
    padding: 8px 12px;
    border-radius: 999px;
    border: none;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: #fff;
    font-weight: 700;
    font-size: 13px;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
    cursor: pointer;
    align-items: center;
    gap: 6px;
    transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
  `;

  button.addEventListener('mouseenter', () => {
    button.style.transform = 'translateY(-1px) scale(1.02)';
    button.style.boxShadow = '0 10px 22px rgba(0, 0, 0, 0.26)';
  });
  button.addEventListener('mouseleave', () => {
    button.style.transform = 'translateY(0)';
    button.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.22)';
  });

  button.addEventListener('click', async (event) => {
    event.preventDefault();
    event.stopPropagation();
    const selectedText = button.dataset.selectionText || '';
    hideSelectionAIButton();
    if (!selectedText.trim()) {
      return;
    }
    const originalLabel = button.textContent;
    button.textContent = '⏳ AI läuft...';
    try {
      await handleSelectionAIRequest(selectedText, () => {});
    } finally {
      button.textContent = originalLabel;
    }
  });

  container.appendChild(noteButton);
  container.appendChild(button);
  document.body.appendChild(container);
  selectionAIContainer = container;
  selectionAIButton = button;
  selectionAINoteButton = noteButton;
  selectionAIButton = button;
  return container;
};

const isSelectionInsideEditable = (node) => {
  if (!node) {
    return false;
  }
  if (node.nodeType === Node.ELEMENT_NODE) {
    const el = node;
    if (el.closest('input, textarea, [contenteditable="true"]')) {
      return true;
    }
  }
  if (node.parentElement && node.parentElement.closest('input, textarea, [contenteditable="true"]')) {
    return true;
  }
  return false;
};

const positionSelectionAIButton = (rect) => {
  if (!selectionAIContainer || !rect) {
    return;
  }

  const viewportPadding = 8;
  const containerWidth = selectionAIContainer.offsetWidth || 160;
  let left = rect.right + 8;
  let top = rect.top - (selectionAIContainer.offsetHeight || 40) - 8;

  if (left + containerWidth + viewportPadding > window.innerWidth) {
    left = Math.max(viewportPadding, rect.right - containerWidth - 8);
  }

  if (top < viewportPadding) {
    top = rect.bottom + 8;
  }

  selectionAIContainer.style.left = `${Math.max(viewportPadding, left)}px`;
  selectionAIContainer.style.top = `${Math.max(viewportPadding, top)}px`;
};

const updateSelectionAIButton = async () => {
  clearSelectionAIHideTimeout();

  if (selectionAIEnabledCached === null) {
    try {
      selectionAIEnabledCached = await isAIEnabled();
    } catch (error) {
      console.warn('[FB Tracker] AI enable check failed for selection button:', error);
      selectionAIEnabledCached = false;
    }
  }

  if (!selectionAIEnabledCached) {
    hideSelectionAIButton();
    return;
  }

  const selection = window.getSelection();
  if (!selection || selection.isCollapsed) {
    hideSelectionAIButton();
    return;
  }

  const selectionText = (selection.toString() || '').trim();
  if (!selectionText || selectionText.length > MAX_SELECTION_LENGTH) {
    hideSelectionAIButton();
    return;
  }

  const anchorNode = selection.anchorNode || selection.focusNode;
  if (isSelectionInsideEditable(anchorNode)) {
    hideSelectionAIButton();
    return;
  }

  if (!selection.rangeCount) {
    hideSelectionAIButton();
    return;
  }

  const range = selection.getRangeAt(0);
  const rect = range.getBoundingClientRect();
  if (!rect || (rect.width === 0 && rect.height === 0)) {
    hideSelectionAIButton();
    return;
  }

  const contextElement = (() => {
    const containerNode = range.commonAncestorContainer || anchorNode;
    if (!containerNode) {
      return null;
    }
    const element = containerNode.nodeType === Node.TEXT_NODE
      ? containerNode.parentElement
      : containerNode;
    return element ? ensurePrimaryPostElement(element) : null;
  })();
  selectionAIContextElement = contextElement;

  const container = ensureSelectionAIButton();
  if (!selectionAIButton || !selectionAINoteButton) {
    hideSelectionAIButton();
    return;
  }
  selectionAIButton.dataset.selectionText = selectionText;
  selectionAINoteButton.dataset.selectionText = selectionText;
  container.style.display = 'inline-flex';
  positionSelectionAIButton(rect);

  selectionAIHideTimeout = setTimeout(() => {
    hideSelectionAIButton();
  }, 8000);
};

const scheduleSelectionAIUpdate = () => {
  if (selectionAIRaf) {
    return;
  }
  selectionAIRaf = requestAnimationFrame(() => {
    selectionAIRaf = null;
    updateSelectionAIButton();
  });
};

const initSelectionAIFloatingButton = () => {
  document.addEventListener('selectionchange', scheduleSelectionAIUpdate, true);
  document.addEventListener('mouseup', scheduleSelectionAIUpdate, true);
  document.addEventListener('keyup', scheduleSelectionAIUpdate, true);
  window.addEventListener('scroll', hideSelectionAIButton, true);
  window.addEventListener('blur', hideSelectionAIButton, true);
};

initSelectionAIFloatingButton();

// Listen for manual reparse command
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message && message.type === 'generateSelectionAI') {
    handleSelectionAIRequest(message.selectionText || '', sendResponse);
    return true;
  }

  if (message && message.type === 'reparsePost') {
    console.log('[FB Tracker] Manual reparse triggered');

    // Use the stored context menu target, fallback to elementFromPoint
    let clickedElement = contextMenuTarget;
    if (!clickedElement && message.x !== undefined && message.y !== undefined) {
      clickedElement = document.elementFromPoint(message.x, message.y);
    }

    if (!clickedElement) {
      console.log('[FB Tracker] No element found');
      sendResponse({ success: false });
      return true;
    }

    console.log('[FB Tracker] Searching for post container starting from:', clickedElement);

    // Find the post container (aria-posinset)
    let postContainer = clickedElement.closest('div[aria-posinset]');
    if (!postContainer && isOnReelsPage()) {
      postContainer = clickedElement.closest('div[role="complementary"]');
    }

    if (!postContainer) {
      console.log('[FB Tracker] No post container found for clicked element:', clickedElement);
      sendResponse({ success: false });
      return true;
    }

    console.log('[FB Tracker] Found post container:', postContainer);

    const normalizedContainer = ensurePrimaryPostElement(postContainer);
    if (normalizedContainer && normalizedContainer !== postContainer) {
      console.log('[FB Tracker] Normalized post container to:', normalizedContainer);
      postContainer = normalizedContainer;
    }

    // Remove processed attribute and existing UI
    postContainer.removeAttribute(PROCESSED_ATTR);
    const existingUI = postContainer.querySelector('.fb-tracker-ui');
    if (existingUI) {
      existingUI.remove();
      clearTrackerElementForPost(postContainer, existingUI);
      console.log('[FB Tracker] Removed existing UI');
    }

    // Find button bar and create UI
    let buttonBar = findButtonBar(postContainer);
    if (!buttonBar) {
      let fallback = postContainer.parentElement;
      while (!buttonBar && fallback && fallback !== document.body) {
        buttonBar = findButtonBar(fallback);
        fallback = fallback.parentElement;
      }
    }

    if (!buttonBar) {
      console.log('[FB Tracker] No button bar found for this post, proceeding with fallback');
    }

    globalPostCounter++;
    const postNum = globalPostCounter;
    console.log('[FB Tracker] Reparsing post as #' + postNum);

    createTrackerUI(postContainer, buttonBar, postNum).then(() => {
      sendResponse({ success: true });
    }).catch((error) => {
      console.error('[FB Tracker] Failed to reparse:', error);
      sendResponse({ success: false });
    });

    return true;
  }
});

// ============================================================================
// AI COMMENT GENERATION
// ============================================================================

/**
 * Show a toast notification
 */
function showToast(message, type = 'info') {
  const toast = document.createElement('div');
  toast.style.cssText = `
    position: fixed;
    bottom: 24px;
    right: 24px;
    background: ${type === 'error' ? '#e74c3c' : type === 'success' ? '#42b72a' : '#1877f2'};
    color: white;
    padding: 12px 20px;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 14px;
    z-index: 999999;
    max-width: 350px;
    animation: slideIn 0.3s ease-out;
  `;
  toast.textContent = message;

  // Add animation keyframes
  if (!document.getElementById('fb-tracker-toast-styles')) {
    const style = document.createElement('style');
    style.id = 'fb-tracker-toast-styles';
    style.textContent = `
      @keyframes slideIn {
        from {
          transform: translateX(400px);
          opacity: 0;
        }
        to {
          transform: translateX(0);
          opacity: 1;
        }
      }
      @keyframes slideOut {
        from {
          transform: translateX(0);
          opacity: 1;
        }
        to {
          transform: translateX(400px);
          opacity: 0;
        }
      }
    `;
    document.head.appendChild(style);
  }

  document.body.appendChild(toast);

  setTimeout(() => {
    toast.style.animation = 'slideOut 0.3s ease-out';
    setTimeout(() => toast.remove(), 300);
  }, 3000);
}

async function copyTextToClipboard(text) {
  if (typeof text !== 'string') {
    return false;
  }

  if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch (error) {
      console.warn('[FB Tracker] navigator.clipboard.writeText failed:', error);
    }
  }

  try {
    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed';
    textarea.style.top = '-999px';
    textarea.style.left = '-999px';
    document.body.appendChild(textarea);
    textarea.focus();
    textarea.select();
    const success = document.execCommand('copy');
    document.body.removeChild(textarea);
    return success;
  } catch (error) {
    console.warn('[FB Tracker] execCommand copy fallback failed:', error);
    return false;
  }
}

/**
 * Extract post text from a Facebook post element
 */
function normalizeSelectionText(text) {
  if (!text) {
    return '';
  }
  const trimmed = text.trim();
  if (!trimmed) {
    return '';
  }
  return trimmed.length > MAX_SELECTION_LENGTH
    ? trimmed.substring(0, MAX_SELECTION_LENGTH)
    : trimmed;
}

function ensurePrimaryPostElement(element) {
  if (!element) {
    return element;
  }

  const selectors = [
    'div[role="dialog"] article',
    'div[role="dialog"] div[aria-posinset]',
    '[data-pagelet*="FeedUnit"] article',
    'div[role="main"] article',
    '[data-visualcompletion="ignore-dynamic"] article',
    'div[aria-posinset]',
    'article[role="article"]',
    'article'
  ];

  if (isOnReelsPage()) {
    selectors.unshift('div[role="complementary"]');
  }

  let current = element;
  while (current && current !== document.body && current !== document.documentElement) {
    for (const selector of selectors) {
      if (current.matches && current.matches(selector)) {
        return current;
      }
    }
    current = current.parentElement;
  }

  return element;
}

function cacheSelectionForPost(postElement) {
  try {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) {
      return;
    }

    const text = normalizeSelectionText(selection.toString());
    if (text) {
      postSelectionCache.set(postElement, {
        text,
        timestamp: Date.now()
      });
      lastGlobalSelection = { text, timestamp: Date.now() };
    }
  } catch (error) {
    console.warn('[FB Tracker] Failed to cache selection text:', error);
  }
}

function cacheCurrentSelection() {
  try {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) {
      return;
    }

    const text = normalizeSelectionText(selection.toString());
    if (text) {
      lastGlobalSelection = {
        text,
        timestamp: Date.now()
      };
    }
  } catch (error) {
    console.debug('[FB Tracker] Unable to cache current selection:', error);
  }
}

function getSelectedTextFromPost(postElement) {
  try {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) {
      throw new Error('No active selection');
    }

    const text = normalizeSelectionText(selection.toString());
    if (text) {
      postSelectionCache.set(postElement, { text, timestamp: Date.now() });
      lastGlobalSelection = { text, timestamp: Date.now() };
      return text;
    }
    throw new Error('Empty selection');
  } catch (error) {
    if (error && error.message) {
      console.debug('[FB Tracker] Selection fallback:', error.message);
    }
    const cached = postSelectionCache.get(postElement);
    if (cached && Date.now() - cached.timestamp < LAST_SELECTION_MAX_AGE) {
      return cached.text;
    }
    if (lastGlobalSelection.text && Date.now() - lastGlobalSelection.timestamp < LAST_SELECTION_MAX_AGE) {
      return lastGlobalSelection.text;
    }
    return '';
  }
}

function extractPostText(postElement) {
  if (!postElement) {
    return '';
  }

  const logPostText = (...args) => {
    try {
      console.log(POST_TEXT_LOG_TAG, ...args);
    } catch (error) {
      // ignore logging failure
    }
  };

  const hasEmojiChars = (text) => /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u2600-\u27BF]/.test(text);

  const injectEmojiLabels = (root) => {
    if (!root || typeof root.querySelectorAll !== 'function') {
      return;
    }
    const emojiNodes = root.querySelectorAll('img[alt], [role="img"][aria-label]');
    emojiNodes.forEach((node) => {
      const label = node.getAttribute('alt') || node.getAttribute('aria-label');
      if (!label || !hasEmojiChars(label)) {
        return;
      }
      const textNode = node.ownerDocument.createTextNode(label);
      node.replaceWith(textNode);
    });
  };

  const getTextWithEmojis = (element) => {
    if (!element) {
      return '';
    }
    const clone = element.cloneNode(true);
    injectEmojiLabels(clone);
    return clone.innerText || clone.textContent || '';
  };

  const SKIP_TEXT_CONTAINERS_SELECTOR = [
    'div[role="textbox"]',
    '[contenteditable="true"]',
    '[data-lexical-editor="true"]',
    'form[role="presentation"]',
    'form[method]',
    '.fb-tracker-ui',
    '.fb-tracker-ai-wrapper',
    '[aria-label*="komment"]',
    '[aria-label*="comment"]',
    '[aria-roledescription*="komment"]',
    '[aria-roledescription*="comment"]'
  ].join(', ');

  const KEYWORD_HINTS = ['meta', 'facebook', 'instagram'];

  const isInsideSkippedRegion = (element) => {
    if (!element || typeof element.closest !== 'function') {
      return false;
    }
    return Boolean(element.closest(SKIP_TEXT_CONTAINERS_SELECTOR));
  };

  const scoreCandidate = (text) => {
    const base = text.length;
    const lower = text.toLowerCase();
    let bonus = 0;
    for (const keyword of KEYWORD_HINTS) {
      if (lower.includes(keyword)) {
        bonus += 200;
      }
    }
    return base + bonus;
  };

  const makeSnippet = (text) => {
    if (!text) {
      return '';
    }
    const trimmed = text.trim();
    return trimmed.length > 140 ? `${trimmed.substring(0, 137)}…` : trimmed;
  };

  const contentSelectors = [
    '[data-ad-preview="message"]',
    '[data-ad-comet-preview="message"]',
    'div[data-ad-comet-preview] > div > div > span',
    '.x193iq5w.xeuugli', // Common Facebook text class
    'span[dir="auto"]',
    'div[dir="auto"]'
  ];

  const uiTextPattern = /(Gefällt mir|Kommentieren|Teilen|Like|Comment|Share)/gi;
  const timePattern = /\d+\s*(Std\.|Min\.|Tag|hour|minute|day)/gi;
  const sponsoredPattern = /(Gesponsert|Sponsored)/gi;

  const cleanCandidate = (text) => {
    if (!text) {
      return '';
    }

    const cleaned = text
      .replace(uiTextPattern, ' ')
      .replace(timePattern, ' ')
      .replace(sponsoredPattern, ' ')
      .replace(/\s+/g, ' ')
      .trim();

    if (!cleaned) {
      logPostText('Discard empty candidate after cleaning');
      return '';
    }

    // Ignore very short snippets that are likely button labels
    if (cleaned.length < 5 && cleaned.split(/\s+/).length < 2) {
      logPostText('Discard very short candidate', makeSnippet(text));
      return '';
    }

    return cleaned;
  };

  const candidates = [];
  const seen = new Set();

  const tryAddCandidate = (rawText, element = null, context = {}) => {
    const candidate = cleanCandidate(rawText);
    if (!candidate) {
      if (rawText) {
        logPostText('Candidate rejected during cleaning', makeSnippet(rawText), context);
      }
      return;
    }
    if (seen.has(candidate)) {
      logPostText('Candidate skipped as duplicate', makeSnippet(candidate), context);
      return;
    }
    if (element && isInsideSkippedRegion(element)) {
      logPostText('Candidate inside skipped region', makeSnippet(candidate), context);
      return;
    }
    seen.add(candidate);
    candidates.push({
      text: candidate,
      score: scoreCandidate(candidate)
    });
    logPostText('Candidate accepted', {
      score: scoreCandidate(candidate),
      snippet: makeSnippet(candidate),
      context
    });
  };

  logPostText('Begin extraction');

  for (const selector of contentSelectors) {
    const elements = postElement.querySelectorAll(selector);
    for (const element of elements) {
      if (isInsideSkippedRegion(element)) {
        logPostText('Selector result skipped (inside disallowed region)', selector, makeSnippet(element.innerText || element.textContent || ''));
        continue;
      }
      tryAddCandidate(getTextWithEmojis(element), element, { selector });
    }
  }

  let textContent = '';

  if (candidates.length) {
    const best = candidates.reduce((top, current) => (
      current.score > top.score ? current : top
    ), candidates[0]);
    textContent = best.text;
    logPostText('Best candidate selected', {
      score: best.score,
      snippet: makeSnippet(best.text)
    });
  }

  if (!textContent) {
    let fallbackText = '';
    try {
      const clone = postElement.cloneNode(true);
      const elementsToRemove = clone.querySelectorAll(SKIP_TEXT_CONTAINERS_SELECTOR);
      elementsToRemove.forEach((node) => node.remove());
      injectEmojiLabels(clone);
      const cloneText = clone.innerText || clone.textContent || '';
      fallbackText = cleanCandidate(cloneText);
      logPostText('Fallback extracted from clone', makeSnippet(fallbackText || cloneText));
    } catch (error) {
      const allText = getTextWithEmojis(postElement);
      fallbackText = cleanCandidate(allText);
      logPostText('Fallback extracted from original element', makeSnippet(fallbackText || allText));
    }
    textContent = fallbackText;
  }

  if (!textContent) {
    logPostText('No usable text found');
    return '';
  }

  logPostText('Final post text', makeSnippet(textContent));

  return textContent.substring(0, 2000); // Limit length
}

/**
 * Find and click the comment button to open comment field
 */
function findAndClickCommentButton(postElement) {
  if (!postElement) {
    return false;
  }

  // Look for comment button with various selectors
  const commentButtonSelectors = [
    '[data-ad-rendering-role="comment_button"]',
    '[aria-label*="Kommentieren"]',
    '[aria-label*="Comment"]'
  ];

  for (const selector of commentButtonSelectors) {
    const button = postElement.querySelector(selector);
    if (button) {
      console.log('[FB Tracker] Found comment button, clicking it');
      button.click();
      return true;
    }
  }

  // Try in parent elements
  let parent = postElement;
  for (let i = 0; i < 3; i++) {
    parent = parent.parentElement;
    if (!parent) break;

    for (const selector of commentButtonSelectors) {
      const button = parent.querySelector(selector);
      if (button) {
        console.log('[FB Tracker] Found comment button in parent, clicking it');
        button.click();
        return true;
      }
    }
  }

  return false;
}

function containsPostContent(element) {
  if (!element || element.nodeType !== Node.ELEMENT_NODE) {
    return false;
  }
  if (element.matches && element.matches('article, [role="article"]')) {
    return true;
  }
  if (element.querySelector && element.querySelector('article, [role="article"]')) {
    return true;
  }
  if (element.matches && element.matches('[data-fb-tracker-processed="1"]')) {
    return true;
  }
  return false;
}

function isTimestampArtifactNode(node) {
  if (!node || node.nodeType !== Node.ELEMENT_NODE) {
    return false;
  }
  if (!node.classList) {
    return false;
  }
  if (!node.classList.contains('__fb-light-mode') && !node.classList.contains('__fb-dark-mode')) {
    return false;
  }
  if (node.querySelector && node.querySelector('article, [role="article"]')) {
    return false;
  }
  const text = node.textContent ? node.textContent.trim() : '';
  if (!text) {
    return true;
  }
  if (text.length > 80) {
    return false;
  }
  const lowered = text.toLowerCase();
  const hasDate = /\b\d{1,2}\.\s*(?:jan|feb|mär|mae|apr|mai|jun|jul|aug|sep|okt|nov|dez)/i.test(lowered);
  const hasTime = /\b\d{1,2}[:.]\d{2}\b/.test(lowered);
  const hasMonthWord = /\b(?:januar|februar|märz|maerz|april|mai|juni|juli|august|september|oktober|november|dezember)\b/.test(lowered);
  if (hasDate || hasTime || hasMonthWord) {
    return true;
  }
  return false;
}

function removeNodeAndEmptyAncestors(node) {
  if (!node || node.nodeType !== Node.ELEMENT_NODE) {
    return;
  }
  const parents = [];
  let currentParent = node.parentElement;
  node.remove();

  while (currentParent && currentParent !== document.body && currentParent !== document.documentElement) {
    parents.push(currentParent);
    currentParent = currentParent.parentElement;
  }

  parents.forEach((parentNode) => {
    if (parentNode.childElementCount === 0) {
      parentNode.remove();
    }
  });
}

function cleanupDanglingSearchArtifacts(context) {
  if (!isOnSearchResultsPage()) {
    return;
  }
  const scope = (context && context.nodeType === Node.ELEMENT_NODE)
    ? context
    : document;
  const candidates = scope.querySelectorAll('div.__fb-light-mode, div.__fb-dark-mode');
  candidates.forEach((node) => {
    if (!isTimestampArtifactNode(node)) {
      return;
    }
    const parent = node.parentElement;
    if (parent) {
      const hasContentSibling = Array.from(parent.children).some((child) => {
        if (child === node) {
          return false;
        }
        return containsPostContent(child);
      });
      if (hasContentSibling) {
        return;
      }
    }
    removeNodeAndEmptyAncestors(node);
  });
}

/**
 * Find comment input field on current page
 */
function findCommentInput(postElement, options = {}) {
  const {
    preferredRoot = null,
    includeParents = true
  } = options;

  if (!postElement && !preferredRoot) {
    return null;
  }

  const selectors = [
    'div[contenteditable="true"][role="textbox"]',
    'div[aria-label*="Kommentar"][contenteditable="true"]',
    'div[aria-label*="comment"][contenteditable="true"]',
    'div[aria-label*="Write a comment"][contenteditable="true"]'
  ];

  const searchInRoot = (root) => {
    if (!root) {
      return null;
    }
    for (const selector of selectors) {
      const input = root.querySelector(selector);
      if (input && isElementVisible(input)) {
        return input;
      }
    }
    return null;
  };

  const roots = [];

  if (postElement) {
    roots.push(postElement);
  }

  if (preferredRoot && preferredRoot.isConnected && !roots.includes(preferredRoot)) {
    roots.push(preferredRoot);
  }

  for (const root of roots) {
    const input = searchInRoot(root);
    if (input) {
      return input;
    }
  }

  if (includeParents && postElement) {
    let parent = postElement.parentElement;
    for (let i = 0; i < 3 && parent; i++) {
      if (preferredRoot && !preferredRoot.contains(parent)) {
        parent = parent.parentElement;
        continue;
      }
      const input = searchInRoot(parent);
      if (input) {
        return input;
      }
      parent = parent.parentElement;
    }
  }

  return null;
}

function isElementVisible(element) {
  if (!element || !element.isConnected) {
    return false;
  }

  if (typeof element.offsetParent !== 'undefined' && element.offsetParent !== null) {
    return true;
  }

  const rects = element.getClientRects();
  return rects && rects.length > 0;
}

function isCancellationError(error) {
  if (!error) {
    return false;
  }

  if (error.name === 'AICancelled' || error.name === 'AbortError') {
    return true;
  }

  if (typeof error.message === 'string') {
    const normalized = error.message.toLowerCase();
    if (normalized === 'ai_cancelled' || normalized === 'abgebrochen') {
      return true;
    }
  }

  return false;
}

async function waitForCommentInput(postElement, options = {}) {
  const {
    encodedPostUrl = null,
    timeout = 6000,
    interval = 200,
    context = null,
    preferredRoot: rawPreferredRoot = null
  } = options;

  const deadline = Date.now() + Math.max(timeout, 0);
  let attempts = 0;
  const preferredRoot = rawPreferredRoot && rawPreferredRoot.isConnected
    ? rawPreferredRoot
    : null;

  const findByEncodedUrl = () => {
    if (context && context.cancelled) {
      return null;
    }

    if (!encodedPostUrl) {
      return null;
    }

    const trackers = document.querySelectorAll(`.fb-tracker-ui[data-post-url="${encodedPostUrl}"]`);
    for (const tracker of trackers) {
      if (!tracker.isConnected) {
        continue;
      }

      if (preferredRoot && !preferredRoot.contains(tracker)) {
        continue;
      }

      const trackerContainer = tracker.closest('div[aria-posinset], article[role="article"], article, div[role="complementary"]');
      if (trackerContainer) {
        const input = findCommentInput(trackerContainer, { preferredRoot });
        if (isElementVisible(input)) {
          return input;
        }
      }

      const dialogRoot = preferredRoot || tracker.closest(DIALOG_ROOT_SELECTOR);
      if (dialogRoot) {
        const dialogInput = dialogRoot.querySelector('div[contenteditable="true"][role="textbox"]');
        if (isElementVisible(dialogInput)) {
          return dialogInput;
        }
      }
    }

    return null;
  };

  while (Date.now() <= deadline) {
    if (context && context.cancelled) {
      return null;
    }

    attempts++;

    let input = findCommentInput(postElement, { preferredRoot });
    if (isElementVisible(input)) {
      if (attempts > 1) {
        console.log('[FB Tracker] Comment input located after', attempts, 'attempts (post context)');
      }
      return input;
    }

    input = findByEncodedUrl();
    if (input) {
      if (attempts > 1) {
        console.log('[FB Tracker] Comment input located after', attempts, 'attempts (encoded URL context)');
      }
      return input;
    }

    const dialogRootFromPost = preferredRoot
      || (postElement ? postElement.closest(DIALOG_ROOT_SELECTOR) : null);
    if (dialogRootFromPost) {
      const dialogInput = dialogRootFromPost.querySelector('div[contenteditable="true"][role="textbox"]');
      if (isElementVisible(dialogInput)) {
        if (attempts > 1) {
          console.log('[FB Tracker] Comment input located after', attempts, 'attempts (dialog context)');
        }
        return dialogInput;
      }
    }

    if (!preferredRoot) {
      const fallbackDialog = document.querySelector(DIALOG_ROOT_SELECTOR);
      if (fallbackDialog && fallbackDialog !== dialogRootFromPost) {
        const dialogInput = fallbackDialog.querySelector('div[contenteditable="true"][role="textbox"]');
        if (isElementVisible(dialogInput)) {
          if (attempts > 1) {
            console.log('[FB Tracker] Comment input located after', attempts, 'attempts (fallback dialog)');
          }
          return dialogInput;
        }
      }
    }

    const globalInputs = Array.from(document.querySelectorAll('div[contenteditable="true"][role="textbox"]')).filter(isElementVisible);
    if (preferredRoot) {
      const scopedInputs = globalInputs.filter(input => preferredRoot.contains(input));
      if (scopedInputs.length > 0) {
        const lastInput = scopedInputs[scopedInputs.length - 1];
        if (lastInput) {
          if (attempts > 1) {
            console.log('[FB Tracker] Comment input located after', attempts, 'attempts (preferred root fallback)');
          }
          return lastInput;
        }
      }
    }
    if (globalInputs.length > 0) {
      const lastInput = globalInputs[globalInputs.length - 1];
      if (lastInput) {
        if (attempts > 1) {
          console.log('[FB Tracker] Comment input located after', attempts, 'attempts (global fallback)');
        }
        return lastInput;
      }
    }

    await delay(interval);
  }

  console.log('[FB Tracker] Comment input wait timed out after', timeout, 'ms');
  return null;
}

/**
 * Set text in comment input field
 */
async function setCommentText(inputElement, text, options = {}) {
  const { context = null } = options;

  const ensureNotCancelled = () => {
    if (context && context.cancelled) {
      const cancelError = new Error('AI_CANCELLED');
      cancelError.name = 'AICancelled';
      throw cancelError;
    }
  };

  if (!inputElement || !text) {
    return false;
  }

  try {
    ensureNotCancelled();
    console.log('[FB Tracker] Setting comment text:', text.substring(0, 50) + '...');
    console.log('[FB Tracker] Input element:', inputElement);

    // Focus and click to ensure field is active
    inputElement.focus();
    inputElement.click();

    // Small delay to ensure field is ready
    await delay(50);
    ensureNotCancelled();

    // Clear existing content
    inputElement.textContent = '';

    // Method 1: Try execCommand first (best for Facebook)
    const range = document.createRange();
    const selection = window.getSelection();
    range.selectNodeContents(inputElement);
    selection.removeAllRanges();
    selection.addRange(range);

    const execSuccess = document.execCommand('insertText', false, text);
    console.log('[FB Tracker] execCommand result:', execSuccess);

    // Wait a bit and check if it worked
    await delay(100);
    ensureNotCancelled();

    let currentContent = inputElement.textContent || inputElement.innerText || '';
    console.log('[FB Tracker] Content after execCommand:', currentContent);

    // If execCommand didn't work, use direct method
    if (!currentContent || currentContent.trim().length === 0) {
      console.log('[FB Tracker] execCommand failed, using direct method');
      inputElement.textContent = text;
      currentContent = text;
    }

    // Trigger input events
    inputElement.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true }));
    inputElement.dispatchEvent(new Event('change', { bubbles: true }));

    // Final verification
    ensureNotCancelled();
    const finalContent = inputElement.textContent || inputElement.innerText || '';
    console.log('[FB Tracker] Final content:', finalContent.substring(0, 50));

    return finalContent.length > 0;
  } catch (error) {
    console.error('[FB Tracker] Failed to set comment text:', error);
    return false;
  }
}

function sanitizeAIComment(comment) {
  if (!comment || typeof comment !== 'string') {
    return '';
  }

  const tempDiv = document.createElement('div');
  tempDiv.innerHTML = comment;
  const sanitized = tempDiv.textContent || tempDiv.innerText || '';

  return sanitized.trim();
}

function nowPerformanceMs() {
  if (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') {
    return performance.now();
  }
  return Date.now();
}

function roundDurationMs(value) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric) || numeric < 0) {
    return null;
  }
  return Math.round(numeric * 1000) / 1000;
}

function buildAITraceId(prefix = 'trace') {
  const safePrefix = String(prefix || 'trace').trim().replace(/[^a-z0-9_-]/gi, '').toLowerCase() || 'trace';
  if (typeof crypto !== 'undefined' && crypto && typeof crypto.randomUUID === 'function') {
    return `${safePrefix}-${crypto.randomUUID()}`;
  }
  const randomPart = Math.random().toString(36).slice(2, 10);
  return `${safePrefix}-${Date.now().toString(36)}-${randomPart}`;
}

async function reportAIDebugFrontendTrace(payload = {}) {
  try {
    const body = {
      traceId: payload.traceId || null,
      flowId: payload.flowId || null,
      source: payload.source || 'extension-ai-button',
      status: payload.status || 'frontend_reported',
      requestMeta: payload.requestMeta || null,
      frontendTimings: payload.frontendTimings || null,
      frontendSteps: Array.isArray(payload.frontendSteps) ? payload.frontendSteps : null,
      frontendError: payload.frontendError || null,
      totalDurationMs: payload.totalDurationMs
    };

    await backendFetch(`${API_URL}/ai/debug-traces/frontend`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body)
    });
  } catch (error) {
    console.warn('[FB Tracker] Failed to submit AI frontend trace:', error);
  }
}

/**
 * Generate AI comment for a post
 */
async function generateAIComment(postText, profileNumber, options = {}) {
  const {
    signal = null,
    preferredCredentialId = null,
    maxAttempts = 3,
    flowId = null,
    source = 'extension-ai-button',
    returnMeta = false
  } = options;
  const normalizedFlowId = typeof flowId === 'string' && flowId.trim()
    ? flowId.trim()
    : buildAITraceId('flow');
  const basePayload = {
    postText,
    profileNumber,
    flowId: normalizedFlowId,
    traceSource: source
  };

  if (typeof preferredCredentialId === 'number' && !Number.isNaN(preferredCredentialId)) {
    basePayload.preferredCredentialId = preferredCredentialId;
  }

  const requestAttempts = [];
  let lastError = null;
  let lastTraceId = null;
  const attempts = Math.max(1, maxAttempts);

  for (let attempt = 1; attempt <= attempts; attempt += 1) {
    const requestTraceId = `${normalizedFlowId}-r${attempt}`;
    const startedAt = new Date().toISOString();
    const attemptStartedMs = nowPerformanceMs();
    const payload = {
      ...basePayload,
      traceId: requestTraceId,
      requestAttempt: attempt
    };

    try {
      const response = await backendFetch(`${API_URL}/ai/generate-comment`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-ai-trace-id': requestTraceId,
          'x-ai-flow-id': normalizedFlowId,
          'x-ai-trace-source': source
        },
        body: JSON.stringify(payload),
        signal
      });
      const durationMs = roundDurationMs(nowPerformanceMs() - attemptStartedMs);
      const responseTraceId = response.headers.get('x-ai-trace-id') || requestTraceId;
      const responseFlowId = response.headers.get('x-ai-flow-id') || normalizedFlowId;
      lastTraceId = responseTraceId || lastTraceId;

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        const message = errorData.error || 'Failed to generate comment';
        requestAttempts.push({
          attempt,
          traceId: responseTraceId,
          flowId: responseFlowId,
          status: 'http_error',
          startedAt,
          durationMs,
          httpStatus: response.status,
          error: message
        });
        const error = new Error(message);
        error.aiTrace = {
          traceId: responseTraceId,
          flowId: responseFlowId,
          requestAttempts: requestAttempts.slice()
        };
        throw error;
      }

      const data = await response.json();
      const sanitizedComment = sanitizeAIComment(data.comment);
      const effectiveTraceId = data.traceId || responseTraceId;
      const effectiveFlowId = data.flowId || responseFlowId;
      lastTraceId = effectiveTraceId || lastTraceId;

      requestAttempts.push({
        attempt,
        traceId: effectiveTraceId,
        flowId: effectiveFlowId,
        status: sanitizedComment ? 'success' : 'empty',
        startedAt,
        durationMs,
        httpStatus: response.status,
        backendTimings: data.timings && data.timings.backend ? data.timings.backend : null
      });

      if (sanitizedComment) {
        const result = {
          comment: sanitizedComment,
          traceId: effectiveTraceId,
          flowId: effectiveFlowId,
          requestAttempts,
          backendTimings: data.timings && data.timings.backend ? data.timings.backend : null
        };
        return returnMeta ? result : sanitizedComment;
      }

      lastError = new Error('AI response empty');
    } catch (error) {
      const durationMs = roundDurationMs(nowPerformanceMs() - attemptStartedMs);
      const cancelled = error && (error.name === 'AbortError' || isCancellationError(error));

      if (!error || !error.aiTrace) {
        requestAttempts.push({
          attempt,
          traceId: requestTraceId,
          flowId: normalizedFlowId,
          status: cancelled ? 'cancelled' : 'request_error',
          startedAt,
          durationMs,
          error: error && error.message ? String(error.message) : 'Unbekannter Fehler'
        });
      } else if (error.aiTrace && error.aiTrace.traceId) {
        lastTraceId = error.aiTrace.traceId;
      }

      lastError = error;
      if (cancelled) {
        break;
      }
    }

    if (attempt < attempts) {
      console.warn(`[FB Tracker] AI comment generation attempt ${attempt} failed, retrying...`, lastError);
      await delay(200);
    }
  }

  if (lastError) {
    if (!lastError.aiTrace) {
      lastError.aiTrace = {
        traceId: lastTraceId,
        flowId: normalizedFlowId,
        requestAttempts: requestAttempts.slice()
      };
    }
    if (lastError.name === 'AbortError' || isCancellationError(lastError)) {
      throw lastError;
    }
  }

  console.error('[FB Tracker] AI comment generation failed after retries:', lastError);
  const finalError = new Error('AI-Antwort konnte nicht erzeugt werden. Bitte später erneut versuchen.');
  finalError.aiTrace = {
    traceId: lastTraceId,
    flowId: normalizedFlowId,
    requestAttempts: requestAttempts.slice()
  };
  throw finalError;
}

async function handleSelectionAIRequest(selectionText, sendResponse) {
  try {
    const normalizedSelection = normalizeSelectionText(selectionText);
    if (!normalizedSelection) {
      showToast('Keine gültige Auswahl gefunden', 'error');
      sendResponse({ success: false, error: 'Keine gültige Auswahl gefunden' });
      return;
    }

    showToast('AI verarbeitet Auswahl...', 'info');

    const profileNumber = await getProfileNumber();
    if (!profileNumber) {
      showToast('Profilstatus nicht geladen. Bitte Tracker neu laden.', 'error');
      sendResponse({ error: 'profile-missing' });
      return;
    }
    const comment = await generateAIComment(normalizedSelection, profileNumber, {
      source: 'selection-ai'
    });

    if (!comment) {
      throw new Error('Keine Antwort vom AI-Dienst erhalten');
    }

    const copied = await copyTextToClipboard(comment);
    if (!copied) {
      throw new Error('Antwort konnte nicht in die Zwischenablage kopiert werden');
    }

    showToast('AI-Antwort in die Zwischenablage kopiert', 'success');
    sendResponse({ success: true, comment });
  } catch (error) {
    console.error('[FB Tracker] Selection AI error:', error);
    showToast(`❌ ${error.message || 'Fehler bei AI-Anfrage'}`, 'error');
    sendResponse({ success: false, error: error.message || 'Unbekannter Fehler' });
  }
}

/**
 * Check if AI is enabled
 */
async function isAIEnabled() {
  try {
    const response = await backendFetch(`${API_URL}/ai-settings`);
    if (!response.ok) {
      return false;
    }
    const settings = await response.json();
    return settings && settings.enabled === 1;
  } catch (error) {
    console.warn('[FB Tracker] Failed to check AI settings:', error);
    return false;
  }
}

/**
 * Add AI comment button to tracker UI
 */
async function addAICommentButton(container, postElement) {
  const aiEnabled = await isAIEnabled();
  if (!aiEnabled) {
    return;
  }

  const actionsContainer = ensureTrackerActionsContainer(container);
  if (!actionsContainer) {
    return;
  }

  const encodedPostUrl = container && container.getAttribute('data-post-url')
    ? container.getAttribute('data-post-url')
    : null;

  const wrapper = document.createElement('div');
  wrapper.className = 'fb-tracker-ai-wrapper';
  wrapper.style.cssText = `
    position: relative;
    display: inline-flex;
    align-items: stretch;
    border-radius: 6px;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  `;

  const button = document.createElement('button');
  button.type = 'button';
  button.className = 'fb-tracker-btn fb-tracker-btn-ai';
  button.textContent = '✨ AI';
  button.title = 'Generiere automatisch einen passenden Kommentar';
  button.style.cssText = `
    background: transparent;
    color: white;
    border: none;
    padding: 6px 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    flex: 1 1 auto;
    border-radius: 0;
    transition: background-color 0.2s ease;
  `;

  const dropdownButton = document.createElement('button');
  dropdownButton.type = 'button';
  dropdownButton.className = 'fb-tracker-btn fb-tracker-btn-ai-dropdown';
  dropdownButton.textContent = '▾';
  dropdownButton.title = 'AI auswählen';
  dropdownButton.setAttribute('aria-label', 'AI auswählen');
  dropdownButton.setAttribute('aria-haspopup', 'menu');
  dropdownButton.setAttribute('aria-expanded', 'false');
  dropdownButton.style.cssText = `
    background: transparent;
    color: white;
    border: none;
    width: 34px;
    font-size: 14px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-left: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 0;
    transition: background-color 0.2s ease;
  `;

  const dropdown = document.createElement('div');
  dropdown.className = 'fb-tracker-ai-dropdown';
  dropdown.style.cssText = `
    display: none;
    min-width: 220px;
    background: #ffffff;
    border-radius: 8px;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
    z-index: 2147483647;
    padding: 6px 0;
  `;

  wrapper.appendChild(button);
  wrapper.appendChild(dropdownButton);
  wrapper.appendChild(dropdown);
  actionsContainer.appendChild(wrapper);

  const baseWrapperShadow = '0 1px 2px rgba(0, 0, 0, 0.12)';
  const setHoverState = (active) => {
    if (active) {
      wrapper.style.transform = 'translateY(-2px)';
      wrapper.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)';
      button.style.backgroundColor = 'rgba(255, 255, 255, 0.08)';
      dropdownButton.style.backgroundColor = 'rgba(255, 255, 255, 0.08)';
    } else {
      wrapper.style.transform = 'translateY(0)';
      wrapper.style.boxShadow = baseWrapperShadow;
      button.style.backgroundColor = 'transparent';
      dropdownButton.style.backgroundColor = 'transparent';
    }
  };
  setHoverState(false);

  const baseButtonText = button.textContent;

  const resolvePostContexts = () => {
    const contextCandidate = container.closest('div[aria-posinset], article[role="article"], article, div[role="complementary"]');
    const normalizedContext = contextCandidate ? ensurePrimaryPostElement(contextCandidate) : null;
    const fallbackContext = postElement ? ensurePrimaryPostElement(postElement) : null;
    const postContext = normalizedContext || fallbackContext || contextCandidate || postElement || container;
    return { postContext, contextCandidate, fallbackContext, normalizedContext };
  };

  const resolvePostContext = () => resolvePostContexts().postContext;

  const getAdditionalNote = () => {
    const context = resolvePostContext();
    return context ? (postAdditionalNotes.get(context) || '') : '';
  };

  let notePreviewElement = null;
  let noteClearButton = null;

  const truncateNoteForPreview = (note) => {
    if (!note) {
      return '';
    }
    return note.length > 120 ? `${note.slice(0, 117)}…` : note;
  };

  const updateNoteIndicator = () => {
    const note = getAdditionalNote();
    const hasNote = note.trim().length > 0;
    button.dataset.aiOriginalText = hasNote ? `${baseButtonText} ✎` : baseButtonText;
    if ((button.dataset.aiState || 'idle') === 'idle' && !button._aiContext) {
      button.textContent = button.dataset.aiOriginalText;
    }
    button.title = hasNote
      ? 'Generiere automatisch einen passenden Kommentar (mit Zusatzinfo)'
      : 'Generiere automatisch einen passenden Kommentar';
  };

  const updateNotePreview = () => {
    updateNoteIndicator();
    if (notePreviewElement) {
      const note = getAdditionalNote();
      notePreviewElement.textContent = note
        ? `Aktuelle Zusatzinfo: ${truncateNoteForPreview(note)}`
        : 'Keine Zusatzinfo gesetzt';
      if (noteClearButton) {
        const hasNote = note.trim().length > 0;
        noteClearButton.disabled = !hasNote;
        noteClearButton.style.opacity = hasNote ? '1' : '0.6';
        noteClearButton.style.cursor = hasNote ? 'pointer' : 'default';
      }
    }
  };

  const setAdditionalNote = (value) => {
    const context = resolvePostContext();
    if (!context) {
      return;
    }
    const trimmed = (value || '').trim();
    if (trimmed) {
      postAdditionalNotes.set(context, trimmed);
    } else {
      postAdditionalNotes.delete(context);
    }
    updateNotePreview();
  };

  const maybeActivateHover = () => {
    if ((button.dataset.aiState || 'idle') === 'idle') {
      setHoverState(true);
    }
  };

  const maybeDeactivateHover = () => {
    if (wrapper.classList.contains('fb-tracker-ai-wrapper--open')) {
      return;
    }
    if (button.matches(':hover') || dropdownButton.matches(':hover')) {
      return;
    }
    setHoverState(false);
  };

  button.addEventListener('mouseenter', maybeActivateHover);
  button.addEventListener('mouseleave', maybeDeactivateHover);

  dropdownButton.addEventListener('mouseenter', maybeActivateHover);
  dropdownButton.addEventListener('mouseleave', maybeDeactivateHover);

  button.addEventListener('pointerdown', () => {
    const context = resolvePostContext();
    const target = context || postElement || container;
    cacheSelectionForPost(target);
  });

  button.dataset.aiState = 'idle';
  button.dataset.aiOriginalText = button.textContent;

  let dropdownOpen = false;
  let dropdownPortalParent = null;

  const resolveDropdownPortalParent = () => {
    if (dropdownPortalParent && dropdownPortalParent.isConnected) {
      return dropdownPortalParent;
    }
    const candidate = document.body || document.documentElement;
    dropdownPortalParent = candidate;
    return dropdownPortalParent;
  };

  const mountDropdownInPortal = () => {
    const portalParent = resolveDropdownPortalParent();
    if (!portalParent) {
      return;
    }
    if (dropdown.parentElement !== portalParent) {
      portalParent.appendChild(dropdown);
    }
  };

  const restoreDropdownToWrapper = () => {
    if (dropdown.parentElement !== wrapper) {
      wrapper.appendChild(dropdown);
    }
  };

  const closeDropdown = () => {
    if (!dropdownOpen) {
      return;
    }
    dropdown.style.display = 'none';
    dropdownOpen = false;
    dropdownButton.setAttribute('aria-expanded', 'false');
    dropdownButton.textContent = '▾';
    wrapper.classList.remove('fb-tracker-ai-wrapper--open');
    document.removeEventListener('click', handleOutsideClick, true);
    document.removeEventListener('keydown', handleKeydown, true);
    if (button.matches(':hover') || dropdownButton.matches(':hover')) {
      setHoverState(true);
    } else {
      setHoverState(false);
    }
    dropdown.style.position = '';
    dropdown.style.top = '';
    dropdown.style.left = '';
    dropdown.style.maxHeight = '';
    dropdown.style.overflowY = '';
    restoreDropdownToWrapper();
    window.removeEventListener('scroll', repositionDropdown, true);
    window.removeEventListener('resize', repositionDropdown);
  };

  const getDecodedPostUrl = () => {
    const raw = encodedPostUrl || (container && container.getAttribute('data-post-url'));
    if (!raw) {
      return null;
    }
    try {
      return decodeURIComponent(raw);
    } catch (error) {
      console.warn('[FB Tracker] Konnte Post-URL nicht dekodieren:', error);
      return null;
    }
  };

  const confirmParticipationAfterAI = async (profileNumber) => {
    try {
      if (!container) {
        return;
      }

      const effectiveProfile = profileNumber || await getProfileNumber();
      if (!effectiveProfile) {
        showToast('Profilstatus nicht geladen. Bitte Tracker neu laden.', 'error');
        return;
      }
      const decodedUrl = getDecodedPostUrl();
      const isFeedHomeFlag = container.dataset.isFeedHome === '1';
      const isDialogFlag = container.dataset.isDialogContext === '1';
      const postNumValue = container.getAttribute('data-post-num') || '?';
      const encodedUrlValue = container.getAttribute('data-post-url') || '';

      let latestData = null;
      let postId = container.dataset.postId || '';

      if (postId) {
        latestData = await markPostChecked(postId, effectiveProfile, { ignoreOrder: true });
        if (!latestData && decodedUrl) {
          const refreshed = await checkPostStatus(decodedUrl);
          if (refreshed && refreshed.id) {
            container.dataset.postId = refreshed.id;
            latestData = await markPostChecked(refreshed.id, effectiveProfile, { ignoreOrder: true }) || refreshed;
          }
        }
      } else if (decodedUrl) {
        const refreshed = await checkPostStatus(decodedUrl);
        if (refreshed && refreshed.id) {
          container.dataset.postId = refreshed.id;
          latestData = await markPostChecked(refreshed.id, effectiveProfile, { ignoreOrder: true }) || refreshed;
        }
      }

      if (!latestData && decodedUrl) {
        const fallbackStatus = await checkPostStatus(decodedUrl);
        if (fallbackStatus) {
          latestData = fallbackStatus;
        }
      }

      if (latestData) {
        await renderTrackedStatus({
          container,
          postElement,
          postData: latestData,
          profileNumber: effectiveProfile,
          isFeedHome: isFeedHomeFlag,
          isDialogContext: isDialogFlag,
          manualHideInfo: null,
          encodedUrl: encodedUrlValue,
          postNum: postNumValue
        });
      }
    } catch (error) {
      console.error('[FB Tracker] Auto-confirm nach AI fehlgeschlagen:', error);
    }
  };

  const handleOutsideClick = (event) => {
    if (!wrapper.contains(event.target) && !dropdown.contains(event.target)) {
      closeDropdown();
    }
  };

  const handleKeydown = (event) => {
    if (event.key === 'Escape') {
      closeDropdown();
    }
  };

  const renderDropdownItems = async () => {
    dropdown.innerHTML = '';
    const loading = document.createElement('div');
    loading.textContent = 'Lade AI-Auswahl...';
    loading.style.cssText = 'padding: 8px 14px; font-size: 13px; color: #555;';
    dropdown.appendChild(loading);

  const appendNoteUI = () => {
    noteClearButton = null;
    const noteSection = document.createElement('div');
      noteSection.style.cssText = 'padding: 10px 14px; display: flex; flex-direction: column; gap: 6px;';

      notePreviewElement = document.createElement('div');
      notePreviewElement.style.cssText = 'font-size: 12px; color: #65676b; white-space: normal;';
      noteSection.appendChild(notePreviewElement);

      const buttonsRow = document.createElement('div');
      buttonsRow.style.cssText = 'display: flex; gap: 8px; flex-wrap: wrap;';

      const selectionButton = document.createElement('button');
      selectionButton.type = 'button';
      selectionButton.textContent = 'Auswahl als Zusatzinfo';
      selectionButton.style.cssText = 'padding: 6px 10px; border-radius: 6px; border: 1px solid #ccd0d5; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #1d2129;';
      selectionButton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();

        const selection = window.getSelection();
        const anchorNode = selection ? (selection.anchorNode || selection.focusNode) : null;
        if (anchorNode && isSelectionInsideEditable(anchorNode)) {
          showToast('Textauswahl aus Eingabefeldern kann nicht genutzt werden', 'error');
          return;
        }

        const context = resolvePostContext();
        let selectedText = context ? getSelectedTextFromPost(context) : '';
        if (!selectedText && selection) {
          selectedText = normalizeSelectionText(selection.toString());
        }
        if (!selectedText && lastGlobalSelection.text && Date.now() - lastGlobalSelection.timestamp < LAST_SELECTION_MAX_AGE) {
          selectedText = normalizeSelectionText(lastGlobalSelection.text);
        }

        if (!selectedText) {
          showToast('Keine Textauswahl gefunden', 'error');
          return;
        }

        setAdditionalNote(selectedText);
        showToast('Auswahl als Zusatzinfo gesetzt', 'success');
      });
      buttonsRow.appendChild(selectionButton);

      const editButton = document.createElement('button');
      editButton.type = 'button';
      editButton.textContent = 'Zusatzinfo bearbeiten';
      editButton.style.cssText = 'padding: 6px 10px; border-radius: 6px; border: 1px solid #ccd0d5; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #1d2129;';
      editButton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        const existingNote = getAdditionalNote();
        const input = window.prompt('Zusatzinfo für den AI-Prompt eingeben:', existingNote);
        if (input === null) {
          return;
        }
        const trimmed = (input || '').trim();
        setAdditionalNote(trimmed);
        if (trimmed) {
          showToast('Zusatzinfo gespeichert', 'success');
        } else {
          showToast('Zusatzinfo entfernt', 'success');
        }
      });
      buttonsRow.appendChild(editButton);

      const clearButton = document.createElement('button');
      clearButton.type = 'button';
      clearButton.textContent = 'Zurücksetzen';
      clearButton.style.cssText = 'padding: 6px 10px; border-radius: 6px; border: 1px solid #ccd0d5; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #a11900;';
      clearButton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        if (!getAdditionalNote()) {
          return;
        }
        setAdditionalNote('');
        showToast('Zusatzinfo entfernt', 'success');
      });
      buttonsRow.appendChild(clearButton);
      noteClearButton = clearButton;

      noteSection.appendChild(buttonsRow);
      dropdown.appendChild(noteSection);

      updateNotePreview();
    };

    try {
      const credentials = await fetchActiveAICredentials();
      dropdown.innerHTML = '';
      appendNoteUI();

      if (!credentials || credentials.length === 0) {
        const empty = document.createElement('div');
        empty.textContent = 'Keine aktiven AI-Anbieter gefunden';
        empty.style.cssText = 'padding: 8px 14px; font-size: 13px; color: #888;';
        dropdown.appendChild(empty);
      } else {
        const divider = document.createElement('div');
        divider.style.cssText = 'border-top: 1px solid #e4e6eb; margin: 6px 0;';
        dropdown.appendChild(divider);

        credentials.forEach((credential) => {
          const option = document.createElement('button');
          option.type = 'button';
          option.className = 'fb-tracker-ai-option';
          option.style.cssText = `
            width: 100%;
            padding: 8px 14px;
            background: transparent;
            border: none;
            text-align: left;
            cursor: pointer;
            font-size: 13px;
            display: flex;
            flex-direction: column;
            gap: 2px;
          `;

          option.addEventListener('mouseenter', () => {
            option.style.background = '#f0f2f5';
          });

          option.addEventListener('mouseleave', () => {
            option.style.background = 'transparent';
          });

          const label = document.createElement('span');
          label.textContent = formatAICredentialLabel(credential);
          label.style.cssText = 'font-weight: 600; color: #1d2129;';

          const metaParts = [];
          if (credential.provider) {
            metaParts.push(`Provider: ${credential.provider}`);
          }
          if (credential.model) {
            metaParts.push(`Modell: ${credential.model}`);
          }

          if (metaParts.length > 0) {
            const meta = document.createElement('span');
            meta.textContent = metaParts.join(' · ');
            meta.style.cssText = 'font-size: 12px; color: #65676b;';
            option.appendChild(label);
            option.appendChild(meta);
          } else {
            option.appendChild(label);
          }

          option.addEventListener('click', () => {
            closeDropdown();
            if ((button.dataset.aiState || 'idle') === 'idle') {
              cacheSelectionForPost(postElement);
              startAIFlow(credential.id);
            }
          });

          dropdown.appendChild(option);
        });
      }
    } catch (error) {
      dropdown.innerHTML = '';
      appendNoteUI();
      const errorItem = document.createElement('div');
      errorItem.textContent = error.message || 'AI-Anbieter konnten nicht geladen werden';
      errorItem.style.cssText = 'padding: 8px 14px; font-size: 13px; color: #c0392b; white-space: normal;';
      dropdown.appendChild(errorItem);
    }
    };

  const positionDropdown = () => {
    if (!dropdownOpen) {
      return;
    }

    mountDropdownInPortal();
    dropdown.style.position = 'fixed';
    dropdown.style.maxHeight = `${Math.max(200, Math.floor(window.innerHeight * 0.6))}px`;
    dropdown.style.overflowY = 'auto';

    const rect = wrapper.getBoundingClientRect();
    const dropdownRect = dropdown.getBoundingClientRect();
    const margin = 8;

    let top = rect.top - dropdownRect.height - margin;
    if (top < margin) {
      top = rect.bottom + margin;
    }

    const viewportPadding = 8;
    let left = rect.right - dropdownRect.width;
    if (left < viewportPadding) {
      left = viewportPadding;
    }
    const maxLeft = window.innerWidth - dropdownRect.width - viewportPadding;
    if (left > maxLeft) {
      left = Math.max(viewportPadding, maxLeft);
    }

    const maxTop = window.innerHeight - dropdownRect.height - margin;
    if (top > maxTop) {
      top = Math.max(viewportPadding, maxTop);
    }

    dropdown.style.top = `${top}px`;
    dropdown.style.left = `${left}px`;
  };

  const repositionDropdown = () => {
    if (dropdownOpen) {
      positionDropdown();
    }
  };

  const toggleDropdown = async () => {
    if ((button.dataset.aiState || 'idle') !== 'idle') {
      return;
    }

    if (dropdownOpen) {
      closeDropdown();
      return;
    }

    dropdownOpen = true;
    wrapper.classList.add('fb-tracker-ai-wrapper--open');
    dropdownButton.textContent = '▴';
    setHoverState(true);
    mountDropdownInPortal();
    dropdown.style.display = 'block';
    dropdownButton.setAttribute('aria-expanded', 'true');
    document.addEventListener('click', handleOutsideClick, true);
    document.addEventListener('keydown', handleKeydown, true);

    await renderDropdownItems();
    positionDropdown();
    window.addEventListener('scroll', repositionDropdown, true);
    window.addEventListener('resize', repositionDropdown);
  };

  dropdownButton.addEventListener('click', (event) => {
    event.preventDefault();
    event.stopPropagation();
    toggleDropdown();
  });

  const startAIFlow = async (preferredCredentialId = null) => {
    closeDropdown();

    const originalText = button.dataset.aiOriginalText || '✨ AI';
    const currentState = button.dataset.aiState || 'idle';

    if (currentState === 'processing') {
      const runningContext = button._aiContext;
      if (runningContext && !runningContext.cancelled) {
        runningContext.cancel();
        button.dataset.aiState = 'cancelling';
        button.textContent = '✋ Abbruch...';
        button.setAttribute('aria-busy', 'true');
        button.style.cursor = 'progress';
        dropdownButton.style.cursor = 'progress';
        button.classList.remove('fb-tracker-btn-ai--processing');
        button.classList.add('fb-tracker-btn-ai--cancelling');
      }
      return;
    }

    if (currentState === 'cancelling') {
      return;
    }

    const aiContext = {
      cancelled: false,
      abortController: new AbortController(),
      cancel() {
        if (!this.cancelled) {
          this.cancelled = true;
          this.abortController.abort();
        }
      }
    };

    const flowTrace = {
      source: 'extension-ai-button',
      flowId: buildAITraceId('flow'),
      traceId: null,
      status: 'processing',
      startedAt: new Date().toISOString(),
      finishedAt: null,
      frontendTimings: {},
      frontendSteps: [],
      frontendError: null,
      totalDurationMs: null,
      requestMeta: {
        preferredCredentialId: typeof preferredCredentialId === 'number' && !Number.isNaN(preferredCredentialId)
          ? preferredCredentialId
          : null
      },
      backend: null
    };
    const flowStartMs = nowPerformanceMs();
    const phaseStartTimes = {};

    const addStep = (step, payload = {}) => {
      flowTrace.frontendSteps.push({
        step,
        at: new Date().toISOString(),
        ...(payload && typeof payload === 'object' ? payload : {})
      });
    };

    const beginPhase = (name) => {
      phaseStartTimes[name] = nowPerformanceMs();
    };

    const endPhase = (name, payload = null) => {
      if (!Object.prototype.hasOwnProperty.call(phaseStartTimes, name)) {
        return null;
      }
      const durationMs = roundDurationMs(nowPerformanceMs() - phaseStartTimes[name]);
      flowTrace.frontendTimings[name] = durationMs;
      if (payload && typeof payload === 'object') {
        addStep(name, payload);
      }
      return durationMs;
    };

    const mergeTraceInfo = (tracePayload) => {
      if (!tracePayload || typeof tracePayload !== 'object') {
        return;
      }
      if (tracePayload.traceId) {
        flowTrace.traceId = tracePayload.traceId;
      }
      if (tracePayload.flowId) {
        flowTrace.flowId = tracePayload.flowId;
      }
      const backend = {};
      if (Array.isArray(tracePayload.requestAttempts)) {
        backend.requestAttempts = tracePayload.requestAttempts;
      }
      if (tracePayload.backendTimings && typeof tracePayload.backendTimings === 'object') {
        backend.timings = tracePayload.backendTimings;
      }
      if (Object.keys(backend).length) {
        flowTrace.backend = backend;
      }
    };

    const throwIfCancelled = () => {
      if (aiContext.cancelled) {
        const cancelError = new Error('AI_CANCELLED');
        cancelError.name = 'AICancelled';
        throw cancelError;
      }
    };

    const updateProcessingText = (text) => {
      if (button.dataset.aiState === 'processing' && !aiContext.cancelled) {
        button.textContent = text;
      }
    };

    const restoreIdle = (text, revertDelay = 0) => {
      button.dataset.aiState = 'idle';
      button._aiContext = null;
      button.removeAttribute('aria-busy');
      button.classList.remove('fb-tracker-btn-ai--processing', 'fb-tracker-btn-ai--cancelling');
      button.style.cursor = 'pointer';
      dropdownButton.disabled = false;
      dropdownButton.style.opacity = '1';
      dropdownButton.style.cursor = 'pointer';
      dropdownButton.textContent = '▾';
      dropdownButton.setAttribute('aria-busy', 'false');
      button.textContent = text;

      if (revertDelay > 0) {
        setTimeout(() => {
          if ((button.dataset.aiState || 'idle') === 'idle' && !button._aiContext) {
            button.textContent = originalText;
          }
        }, revertDelay);
      }

      if (wrapper.classList.contains('fb-tracker-ai-wrapper--open')) {
        setHoverState(true);
      } else if (!(button.matches(':hover') || dropdownButton.matches(':hover'))) {
        setHoverState(false);
      }
    };

    button._aiContext = aiContext;
    button.dataset.aiState = 'processing';
    button.setAttribute('aria-busy', 'true');
    button.classList.add('fb-tracker-btn-ai--processing');
    button.classList.remove('fb-tracker-btn-ai--cancelling');
    button.style.cursor = 'progress';
    setHoverState(true);
    dropdownButton.disabled = true;
    dropdownButton.style.opacity = '0.5';
    dropdownButton.style.cursor = 'not-allowed';
    dropdownButton.setAttribute('aria-busy', 'true');
    button.textContent = '⏳ Generiere...';

    try {
      const contexts = resolvePostContexts();
      const { postContext, contextCandidate, fallbackContext } = contexts;

      const selectionKeys = [];
      if (postContext) {
        selectionKeys.push(postContext);
      }
      if (postElement && postElement !== postContext) {
        selectionKeys.push(postElement);
      }
      if (contextCandidate && contextCandidate !== postContext && contextCandidate !== postElement) {
        selectionKeys.push(contextCandidate);
      }
      if (fallbackContext
        && fallbackContext !== postContext
        && fallbackContext !== postElement
        && fallbackContext !== contextCandidate) {
        selectionKeys.push(fallbackContext);
      }

      const resolveRecentSelection = () => {
        for (const key of selectionKeys) {
          if (!key) {
            continue;
          }
          const entry = postSelectionCache.get(key);
          if (entry && Date.now() - entry.timestamp < LAST_SELECTION_MAX_AGE) {
            return entry;
          }
        }
        return null;
      };

      beginPhase('extractPostTextMs');
      let postText = '';
      const cachedSelection = resolveRecentSelection();
      if (cachedSelection) {
        console.log('[FB Tracker] Using cached selection text');
        postText = cachedSelection.text;
      }

      throwIfCancelled();

      if (!postText) {
        const selectionSource = postContext || postElement;
        if (selectionSource) {
          postText = getSelectedTextFromPost(selectionSource);
          if (postText) {
            console.log('[FB Tracker] Using active selection text');
          }
        }
      }

      if (!postText) {
        const latestCached = resolveRecentSelection();
        if (latestCached) {
          console.log('[FB Tracker] Using latest cached selection after check');
          postText = latestCached.text;
        }
      }

      if (!postText) {
        postText = extractPostText(postContext);
        if (postText) {
          console.log('[FB Tracker] Fallback to DOM extraction');
        }
      }

      if (!postText) {
        throw new Error('Konnte Post-Text nicht extrahieren');
      }

      selectionKeys.forEach((key) => {
        if (key) {
          postSelectionCache.delete(key);
        }
      });

      const additionalNote = postContext ? (postAdditionalNotes.get(postContext) || '') : '';
      if (additionalNote) {
        postText = `${postText}\n\nZusatzinfo:\n${additionalNote}`;
      }

      flowTrace.requestMeta.postTextLength = postText.length;
      endPhase('extractPostTextMs', { postTextLength: postText.length });

      throwIfCancelled();

      console.log('[FB Tracker] Generating AI comment for:', postText.substring(0, 100));

      beginPhase('profileLookupMs');
      const profileNumber = await getProfileNumber();
      endPhase('profileLookupMs', { profileNumber: profileNumber || null });
      if (!profileNumber) {
        flowTrace.status = 'profile_missing';
        flowTrace.frontendError = 'Profilstatus nicht geladen';
        showToast('Profilstatus nicht geladen. Bitte Tracker neu laden.', 'error');
        restoreIdle(originalText);
        return;
      }

      throwIfCancelled();

      beginPhase('aiRequestMs');
      const aiResult = await generateAIComment(postText, profileNumber, {
        signal: aiContext.abortController.signal,
        preferredCredentialId,
        flowId: flowTrace.flowId,
        source: flowTrace.source,
        returnMeta: true
      });
      endPhase('aiRequestMs', {
        traceId: aiResult.traceId || null,
        requestAttempts: Array.isArray(aiResult.requestAttempts) ? aiResult.requestAttempts.length : 0
      });
      mergeTraceInfo(aiResult);

      throwIfCancelled();

      const comment = aiResult.comment;
      console.log('[FB Tracker] Generated comment:', comment);

      const dialogRoot = container.closest(DIALOG_ROOT_SELECTOR)
        || (postContext && postContext.closest(DIALOG_ROOT_SELECTOR));

      let commentInput = findCommentInput(postContext, { preferredRoot: dialogRoot });
      let waitedForInput = false;
      let waitStartedMs = null;

      if (!commentInput) {
        console.log('[FB Tracker] Comment input not found, trying to click comment button');
        let buttonClicked = findAndClickCommentButton(postContext);

        if (!buttonClicked && dialogRoot) {
          const dialogCommentButton = dialogRoot.querySelector('[data-ad-rendering-role="comment_button"], [aria-label*="Kommentieren"], [aria-label*="Comment"]');
          if (dialogCommentButton && isElementVisible(dialogCommentButton)) {
            dialogCommentButton.click();
            buttonClicked = true;
          }
        }

        updateProcessingText(buttonClicked ? '⏳ Öffne Kommentare...' : '⏳ Suche Kommentarfeld...');

        waitedForInput = true;
        waitStartedMs = nowPerformanceMs();
        commentInput = await waitForCommentInput(postContext, {
          encodedPostUrl,
          timeout: buttonClicked ? 8000 : 5000,
          interval: 250,
          context: aiContext,
          preferredRoot: dialogRoot
        });
      }

      if (!commentInput && !waitedForInput) {
        updateProcessingText('⏳ Suche Kommentarfeld...');
        waitedForInput = true;
        waitStartedMs = nowPerformanceMs();
        commentInput = await waitForCommentInput(postContext, {
          encodedPostUrl,
          timeout: 4000,
          interval: 200,
          context: aiContext,
          preferredRoot: dialogRoot
        });
      }

      if (waitStartedMs !== null) {
        flowTrace.frontendTimings.waitForCommentInputMs = roundDurationMs(nowPerformanceMs() - waitStartedMs);
        addStep('waitForCommentInputMs', {
          durationMs: flowTrace.frontendTimings.waitForCommentInputMs,
          found: Boolean(commentInput)
        });
      }

      flowTrace.requestMeta.waitedForInput = waitedForInput;
      flowTrace.requestMeta.commentInputFound = Boolean(commentInput);

      throwIfCancelled();

      if (!commentInput) {
        beginPhase('clipboardWriteMs');
        await navigator.clipboard.writeText(comment);
        endPhase('clipboardWriteMs', { reason: 'input_missing' });
        throwIfCancelled();
        showToast('📋 Kommentarfeld nicht gefunden - In Zwischenablage kopiert', 'info');
        restoreIdle('📋 Kopiert', 2000);
        flowTrace.status = 'clipboard_fallback';
        beginPhase('confirmParticipationMs');
        await confirmParticipationAfterAI(profileNumber);
        endPhase('confirmParticipationMs');
        return;
      }

      if (waitedForInput) {
        updateProcessingText('⏳ Füge Kommentar ein...');
      }

      beginPhase('setCommentTextMs');
      const success = await setCommentText(commentInput, comment, { context: aiContext });
      endPhase('setCommentTextMs', { success: Boolean(success) });

      throwIfCancelled();

      if (success) {
        showToast('✓ Kommentar wurde eingefügt', 'success');
        restoreIdle('✓ Eingefügt', 2000);
        flowTrace.status = 'success';
        beginPhase('confirmParticipationMs');
        await confirmParticipationAfterAI(profileNumber);
        endPhase('confirmParticipationMs');
      } else {
        beginPhase('clipboardWriteMs');
        await navigator.clipboard.writeText(comment);
        endPhase('clipboardWriteMs', { reason: 'set_comment_failed' });
        throwIfCancelled();
        showToast('📋 Einfügen fehlgeschlagen - In Zwischenablage kopiert', 'info');
        restoreIdle('📋 Kopiert', 2000);
        flowTrace.status = 'clipboard_fallback';
        beginPhase('confirmParticipationMs');
        await confirmParticipationAfterAI(profileNumber);
        endPhase('confirmParticipationMs');
      }
    } catch (error) {
      if (error && error.aiTrace) {
        mergeTraceInfo(error.aiTrace);
      }

      const cancelled = aiContext.cancelled || isCancellationError(error);
      if (cancelled) {
        console.log('[FB Tracker] AI comment operation cancelled');
        flowTrace.status = 'cancelled';
        flowTrace.frontendError = 'AI_CANCELLED';
        restoreIdle('✋ Abgebrochen', 1500);
        showToast('⏹️ Vorgang abgebrochen', 'info');
        return;
      }

      console.error('[FB Tracker] AI comment error:', error);
      flowTrace.status = 'error';
      flowTrace.frontendError = error && error.message ? String(error.message) : 'Unbekannter Fehler';
      showToast(`❌ ${error.message}`, 'error');
      restoreIdle(originalText);
    } finally {
      flowTrace.finishedAt = new Date().toISOString();
      flowTrace.totalDurationMs = roundDurationMs(nowPerformanceMs() - flowStartMs);
      flowTrace.frontendTimings.totalMs = flowTrace.totalDurationMs;
      if (!flowTrace.status || flowTrace.status === 'processing') {
        flowTrace.status = 'finished';
      }
      addStep('flowComplete', {
        status: flowTrace.status,
        totalDurationMs: flowTrace.totalDurationMs
      });

      void reportAIDebugFrontendTrace({
        traceId: flowTrace.traceId,
        flowId: flowTrace.flowId,
        source: flowTrace.source,
        status: flowTrace.status,
        requestMeta: {
          ...flowTrace.requestMeta,
          startedAt: flowTrace.startedAt,
          finishedAt: flowTrace.finishedAt,
          backend: flowTrace.backend
        },
        frontendTimings: flowTrace.frontendTimings,
        frontendSteps: flowTrace.frontendSteps,
        frontendError: flowTrace.frontendError,
        totalDurationMs: flowTrace.totalDurationMs
      });
    }
  };

  button.addEventListener('click', (event) => {
    event.preventDefault();
    event.stopPropagation();
    startAIFlow();
  });

  container.appendChild(wrapper);
}

// Expose function globally so it can be called from createTrackerUI
window.addAICommentButton = addAICommentButton;
