/**
 * API-Routes für HTTV/click-TT HTTP-Seiten mit Logging.
 * Ermöglicht das Testen verschiedener URLs und das Auslesen der Logs.
 */

import express from 'express';
import crypto from 'crypto';
import clickTtHttpPageService from '../services/clickTtHttpPageService.js';
import HttpPageFetchLog from '../models/HttpPageFetchLog.js';
import { authenticate } from '../middleware/authMiddleware.js';

const router = express.Router();

// Cookie-Store pro Session (sid) – Cookies vom Ursprungsserver werden hier gehalten und weitergeleitet
const cookieStore = new Map();

function extractCookies(setCookieHeaders) {
  const cookies = {};
  if (setCookieHeaders) {
    (Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders]).forEach(cookie => {
      const [nameValue] = String(cookie).split(';');
      const eq = nameValue.indexOf('=');
      if (eq > 0) {
        const name = nameValue.substring(0, eq).trim();
        const value = nameValue.substring(eq + 1).trim();
        if (name) cookies[name] = value;
      }
    });
  }
  return cookies;
}

function formatCookies(cookies) {
  return Object.entries(cookies)
    .map(([name, value]) => `${name}=${value}`)
    .join('; ');
}

function getOrCreateSid(req) {
  return req.query.sid || crypto.randomBytes(16).toString('hex');
}

function serializeFormBody(req) {
  if (typeof req.rawBody === 'string' && req.rawBody.length > 0) {
    return req.rawBody;
  }

  if (Buffer.isBuffer(req.body) && req.body.length > 0) {
    return req.body.toString('utf8');
  }

  if (typeof req.body === 'string') {
    return req.body;
  }

  if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
    return new URLSearchParams(req.body).toString();
  }

  return null;
}

function maskSensitiveValue(key, value) {
  if (!key) return value;
  return /(pass|password|passwd|pwd|token|secret)/i.test(String(key)) ? '[redacted]' : value;
}

function summarizeFormBody(body) {
  if (!body || typeof body !== 'string') {
    return { fieldCount: 0, fields: [] };
  }

  try {
    const params = new URLSearchParams(body);
    const fields = [];

    for (const [key, value] of params.entries()) {
      fields.push({
        key,
        value: maskSensitiveValue(key, value),
      });
    }

    return {
      fieldCount: fields.length,
      fields,
      parseError: null,
    };
  } catch {
    return {
      fieldCount: 0,
      fields: [],
      parseError: 'body is not URL-encoded',
    };
  }
}

function summarizeInlineConfirmElements(html) {
  if (!html || typeof html !== 'string') return [];
  const matches = [];
  const elementRegex = /<(a|button|input|form)\b([^>]*)>/gi;
  let match;
  while ((match = elementRegex.exec(html)) !== null && matches.length < 10) {
    const tag = match[1];
    const attrs = match[2] || '';
    const onclickMatch = attrs.match(/\bonclick\s*=\s*(["'])([\s\S]*?)\1/i);
    const onclick = onclickMatch?.[2] || null;
    if (!onclick || !/confirm\s*\(/i.test(onclick)) {
      continue;
    }
    const valueMatch = attrs.match(/\bvalue\s*=\s*(["'])([\s\S]*?)\1/i);
    const hrefMatch = attrs.match(/\bhref\s*=\s*(["'])([\s\S]*?)\1/i);
    const nameMatch = attrs.match(/\bname\s*=\s*(["'])([\s\S]*?)\1/i);
    matches.push({
      tag,
      name: nameMatch?.[2] || null,
      value: valueMatch?.[2] || null,
      href: hrefMatch?.[2] || null,
      onclick
    });
  }
  return matches;
}

/** Domains, deren Links durch den Proxy umgeleitet werden (für Folge-Logs) */
const PROXY_DOMAINS = ['click-tt.de', 'httv.de', 'liga.nu'];

function isAllowedProxyUrl(url) {
  if (!url || typeof url !== 'string') return false;
  try {
    const hostname = new URL(url).hostname.toLowerCase();
    return PROXY_DOMAINS.some(domain => hostname === domain || hostname.endsWith(`.${domain}`));
  } catch {
    return false;
  }
}

/**
 * Prüft, ob eine URL durch unseren Proxy umgeleitet werden soll
 */
function shouldProxyUrl(href) {
  if (!href || typeof href !== 'string') return false;
  const h = href.trim();
  if (h.startsWith('#') || h.startsWith('javascript:')) return false;
  return isAllowedProxyUrl(h);
}

/**
 * Baut die Proxy-URL inkl. Session-ID für Cookie-Weitergabe
 */
function buildProxyUrl(proxyBase, targetUrl, sid) {
  const sep = proxyBase.includes('?') ? '&' : '?';
  return `${proxyBase}${sep}url=${encodeURIComponent(targetUrl)}${sid ? `&sid=${sid}` : ''}`;
}

function applyOutsideScriptTags(html, transform) {
  if (!html) return html;
  const scripts = [];
  const placeholderHtml = html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, (match) => {
    const token = `__CLICKTT_SCRIPT_${scripts.length}__`;
    scripts.push(match);
    return token;
  });

  let transformed = transform(placeholderHtml);
  scripts.forEach((script, index) => {
    transformed = transformed.replace(`__CLICKTT_SCRIPT_${index}__`, script);
  });
  return transformed;
}

function normalizeInlineScriptStrings(html) {
  if (!html) return html;

  return html.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (match, attrs, content) => {
    if (/\bsrc\s*=/i.test(attrs || '')) return match;

    let out = '';
    let inSingle = false;
    let inDouble = false;
    let inTemplate = false;
    let escaped = false;

    for (let i = 0; i < content.length; i += 1) {
      const ch = content[i];

      if (escaped) {
        out += ch;
        escaped = false;
        continue;
      }

      if (ch === '\\') {
        out += ch;
        escaped = true;
        continue;
      }

      if (!inDouble && !inTemplate && ch === '\'') {
        inSingle = !inSingle;
        out += ch;
        continue;
      }

      if (!inSingle && !inTemplate && ch === '"') {
        inDouble = !inDouble;
        out += ch;
        continue;
      }

      if (!inSingle && !inDouble && ch === '`') {
        inTemplate = !inTemplate;
        out += ch;
        continue;
      }

      if ((inSingle || inDouble) && (ch === '\n' || ch === '\r')) {
        if (ch === '\r' && content[i + 1] === '\n') {
          i += 1;
        }
        out += '\\n';
        continue;
      }

      out += ch;
    }

    return `<script${attrs}>${out}</script>`;
  });
}

/**
 * Schreibt Links im HTML um, sodass Klicks im iframe über unseren Proxy laufen (Folge-Logs).
 */
function rewriteLinksInHtml(html, proxyBaseUrl, pageBaseUrl, sid) {
  if (!html || !proxyBaseUrl || !pageBaseUrl) return html;
  return applyOutsideScriptTags(html, (safeHtml) => {
    try {
      const base = new URL(pageBaseUrl);
      return safeHtml.replace(
        /<a\s+([^>]*?)href\s*=\s*["']([^"']+)["']([^>]*)>/gi,
        (match, before, href, after) => {
          let absoluteUrl = href;
          if (href.startsWith('/') || !href.startsWith('http')) {
            absoluteUrl = new URL(href, base.origin + base.pathname).href;
          }
          if (!shouldProxyUrl(absoluteUrl)) return match;
          const proxyUrl = buildProxyUrl(proxyBaseUrl, absoluteUrl, sid);
          return `<a ${before}href="${proxyUrl}"${after}>`;
        }
      );
    } catch {
      return safeHtml;
    }
  });
}

/**
 * Schreibt meta-refresh-URLs um, damit Redirects nach Login über unseren Proxy laufen (Session bleibt erhalten).
 */
function rewriteMetaRefreshInHtml(html, proxyBaseUrl, pageBaseUrl, sid) {
  if (!html || !proxyBaseUrl || !pageBaseUrl || !sid) return html;
  return applyOutsideScriptTags(html, (safeHtml) => {
    try {
      const base = new URL(pageBaseUrl);
      return safeHtml.replace(
        /<meta\s[^>]*(?:http-equiv\s*=\s*["']refresh["'][^>]*content\s*=\s*["']([^"']+)["']|content\s*=\s*["']([^"']+)["'][^>]*http-equiv\s*=\s*["']refresh["'])[^>]*>/gi,
        (match, content1, content2) => {
          const content = content1 || content2;
          if (!content) return match;
          const urlMatch = content.match(/^\s*\d+\s*;\s*url\s*=\s*(.+)$/i);
          if (!urlMatch) return match;
          let targetUrl = urlMatch[1].trim();
          if ((targetUrl.startsWith("'") && targetUrl.endsWith("'")) || (targetUrl.startsWith('"') && targetUrl.endsWith('"'))) {
            targetUrl = targetUrl.slice(1, -1);
          }
          if (targetUrl.startsWith('/') || !targetUrl.startsWith('http')) {
            targetUrl = new URL(targetUrl, base.origin + base.pathname).href;
          }
          if (!shouldProxyUrl(targetUrl)) return match;
          const proxyUrl = buildProxyUrl(proxyBaseUrl, targetUrl, sid);
          const newContent = content.replace(/url\s*=\s*.+$/i, `url=${proxyUrl}`);
          return match.replace(content, newContent);
        }
      );
    } catch {
      return safeHtml;
    }
  });
}

/**
 * Schreibt Formular-Actions um, sodass Submissions über unseren Proxy laufen (Login etc. wird geloggt).
 */
function rewriteFormActionsInHtml(html, proxyBaseUrl, pageBaseUrl, sid) {
  if (!html || !proxyBaseUrl || !pageBaseUrl) return html;
  return applyOutsideScriptTags(html, (safeHtml) => {
    try {
      const base = new URL(pageBaseUrl);
      return safeHtml.replace(
        /<form\s+([^>]*?)action\s*=\s*["']([^"']*)["']([^>]*)>/gi,
        (match, before, action, after) => {
          const actionTrim = action.trim();
          if (!actionTrim) return match; // action="" = same URL
          let absoluteUrl = actionTrim;
          if (actionTrim.startsWith('/') || !actionTrim.startsWith('http')) {
            absoluteUrl = new URL(actionTrim, base.origin + base.pathname).href;
          }
          if (!shouldProxyUrl(absoluteUrl)) return match;
          const proxyUrl = buildProxyUrl(proxyBaseUrl, absoluteUrl, sid);
          return `<form ${before}action="${proxyUrl}"${after}>`;
        }
      );
    } catch {
      return safeHtml;
    }
  });
}

function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
  if (!html || !proxyBaseUrl || !pageBaseUrl) return html;

  const scriptContent = [
    '(function(){',
    `var PROXY_BASE_URL=${JSON.stringify(proxyBaseUrl)};`,
    `var PAGE_BASE_URL=${JSON.stringify(pageBaseUrl)};`,
    `var SID=${JSON.stringify(sid || '')};`,
    'function normalizeUrl(value, base){',
    'if(!value)return null;',
    "var trimmed=String(value).trim();",
    "if(!trimmed||trimmed.indexOf('#')===0||trimmed.indexOf('javascript:')===0)return null;",
    'try{return new URL(trimmed, base||PAGE_BASE_URL).href;}catch(e){return null;}',
    '}',
    'function shouldProxyUrl(value){',
    'try{var hostname=new URL(value).hostname;return /(^|\\\\.)click-tt\\\\.de$|(^|\\\\.)httv\\\\.de$|(^|\\\\.)liga\\\\.nu$/i.test(hostname);}catch(e){return false;}',
    '}',
    'function buildProxyUrl(targetUrl){',
    "var separator=PROXY_BASE_URL.indexOf('?')>=0?'&':'?';",
    "return PROXY_BASE_URL+separator+'url='+encodeURIComponent(targetUrl)+(SID?'&sid='+encodeURIComponent(SID):'');",
    '}',
    'function navigateViaProxy(targetUrl){',
    'if(!targetUrl||!shouldProxyUrl(targetUrl))return false;',
    'window.location.assign(buildProxyUrl(targetUrl));',
    'return true;',
    '}',
    'function shouldAllowInlineConfirm(element){',
    "var onclickValue=element&&element.getAttribute?element.getAttribute('onclick'):null;",
    'if(!onclickValue)return true;',
    "var match=onclickValue.match(/confirm\\((['\"])([\\s\\S]*?)\\1\\)/i);",
    "try{console.log('[ClickTT Proxy] inline confirm inspect',{onclick:onclickValue,matched:!!match});}catch(e){}",
    'if(!match)return true;',
    'var confirmed=window.confirm(match[2]);',
    "try{console.log('[ClickTT Proxy] inline confirm',{message:match[2],confirmed:confirmed,onclick:onclickValue});}catch(e){}",
    'return confirmed;',
    '}',
    'function logInlineConfirmElements(){',
    'try{',
    "var elements=[].slice.call(document.querySelectorAll('[onclick*=\"confirm(\"]')).slice(0,10).map(function(el){",
    'return {',
    'tag:el.tagName,',
    "name:el.getAttribute('name'),",
    "value:el.getAttribute('value'),",
    "href:el.getAttribute('href'),",
    "onclick:el.getAttribute('onclick'),",
    "text:(el.textContent||'').trim().slice(0,120)",
    '};',
    '});',
    "console.log('[ClickTT Proxy] inline confirm elements json',JSON.stringify(elements));",
    '}catch(e){}',
    '}',
    'function bindInlineConfirmElements(){',
    'try{',
    "var elements=[].slice.call(document.querySelectorAll('[onclick*=\"confirm(\"]'));",
    'elements.forEach(function(el,index){',
    "if(el.__clickttConfirmBound)return;",
    'el.__clickttConfirmBound=true;',
    "el.addEventListener('click',function(event){",
    "try{console.log('[ClickTT Proxy] direct confirm element click',{index:index,tag:el.tagName,name:el.getAttribute('name'),value:el.getAttribute('value'),href:el.getAttribute('href'),onclick:el.getAttribute('onclick'),text:(el.textContent||'').trim().slice(0,120)});}catch(e){}",
    'var allowed=shouldAllowInlineConfirm(el);',
    'if(!allowed){',
    'event.preventDefault();',
    'event.stopPropagation();',
    'return false;',
    '}',
    "var href=el.getAttribute&&el.getAttribute('href');",
    'var targetUrl=normalizeUrl(href,PAGE_BASE_URL);',
    'if(targetUrl&&shouldProxyUrl(targetUrl)){',
    'event.preventDefault();',
    'event.stopPropagation();',
    'navigateViaProxy(targetUrl);',
    'return false;',
    '}',
    'return true;',
    '},true);',
    '});',
    '}catch(e){}',
    '}',
    'function getSubmitTarget(form, submitter){',
    "var action=submitter&&submitter.getAttribute?submitter.getAttribute('formaction'):null;",
    'if(action)return normalizeUrl(action,PAGE_BASE_URL);',
    "return normalizeUrl((form&&form.getAttribute?form.getAttribute('action'):null)||PAGE_BASE_URL,PAGE_BASE_URL);",
    '}',
    'var lastSubmitter=null;',
    'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",function(){logInlineConfirmElements();bindInlineConfirmElements();},{once:true});}else{logInlineConfirmElements();bindInlineConfirmElements();}',
    "document.addEventListener('click',function(event){",
    "var anchor=event.target&&event.target.closest?event.target.closest('a[href]'):null;",
    "var submitControl=event.target&&event.target.closest?event.target.closest('button, input[type=\"submit\"], input[type=\"image\"]'):null;",
    'if(submitControl){',
    'lastSubmitter=submitControl;',
    "try{console.log('[ClickTT Proxy] submit control click',{name:submitControl.name||null,value:submitControl.value||null,type:submitControl.type||submitControl.tagName,formAction:submitControl.getAttribute?submitControl.getAttribute('formaction'):null,onclick:submitControl.getAttribute?submitControl.getAttribute('onclick'):null,text:(submitControl.textContent||'').trim().slice(0,120)});}catch(e){}",
    'if(!shouldAllowInlineConfirm(submitControl)){',
    'event.preventDefault();',
    'event.stopPropagation();',
    'return;',
    '}',
    '}',
    'if(!anchor||event.defaultPrevented)return;',
    'if(!shouldAllowInlineConfirm(anchor)){',
    'event.preventDefault();',
    'return;',
    '}',
    "var targetUrl=normalizeUrl(anchor.getAttribute('href'),PAGE_BASE_URL);",
    'if(!targetUrl||!shouldProxyUrl(targetUrl))return;',
    "try{console.log('[ClickTT Proxy] anchor click',{href:anchor.getAttribute('href'),targetUrl:targetUrl,text:(anchor.textContent||'').trim().slice(0,120)});}catch(e){}",
    'event.preventDefault();',
    'navigateViaProxy(targetUrl);',
    '},false);',
    "document.addEventListener('submit',function(event){",
    'var form=event.target;',
    "if(!form||!form.tagName||form.tagName.toLowerCase()!=='form')return;",
    'var submitter=event.submitter||lastSubmitter||null;',
    'if(submitter&&!shouldAllowInlineConfirm(submitter)){',
    'event.preventDefault();',
    'event.stopPropagation();',
    'return;',
    '}',
    'if(!shouldAllowInlineConfirm(form)){',
    'event.preventDefault();',
    'event.stopPropagation();',
    'return;',
    '}',
    'var targetUrl=getSubmitTarget(form,submitter);',
    'if(!targetUrl||!shouldProxyUrl(targetUrl))return;',
    'if(submitter&&submitter.form===form&&submitter.name){',
    "var hasField=!!form.querySelector('[name=\"'+CSS.escape(submitter.name)+'\"]');",
    'if(!hasField){',
    "var hidden=document.createElement('input');",
    "hidden.type='hidden';",
    'hidden.name=submitter.name;',
    "hidden.value=submitter.value||'';",
    "hidden.setAttribute('data-clicktt-submit-proxy','1');",
    'form.appendChild(hidden);',
    '}',
    '}',
    'try{',
    'var formDataEntries=[];',
    'if(window.FormData){',
    'var formData=new FormData(form);',
    'formData.forEach(function(value,key){',
    "var safeValue=/(pass|password|passwd|pwd|token|secret)/i.test(String(key))?'[redacted]':String(value);",
    'formDataEntries.push({key:key,value:safeValue});',
    '});',
    '}',
    "console.log('[ClickTT Proxy] form submit',{action:form.getAttribute('action'),targetUrl:targetUrl,submitterName:submitter&&submitter.name?submitter.name:null,submitterValue:submitter&&submitter.value?submitter.value:null,fieldCount:formDataEntries.length,fields:formDataEntries.slice(0,40)});",
    '}catch(e){}',
    "form.setAttribute('action',buildProxyUrl(targetUrl));",
    '},true);',
    'if(window.fetch){',
    'var nativeFetch=window.fetch.bind(window);',
    'window.fetch=function patchedFetch(input, init){',
    'var rawUrl=typeof input==="string"?input:(input&&input.url?input.url:null);',
    'var targetUrl=normalizeUrl(rawUrl,PAGE_BASE_URL);',
    'if(targetUrl&&shouldProxyUrl(targetUrl)){',
    'if(typeof input==="string"){input=buildProxyUrl(targetUrl);}else if(input&&input.url){input=buildProxyUrl(targetUrl);}',
    '}',
    'return nativeFetch(input, init);',
    '};',
    '}',
    'if(window.XMLHttpRequest&&window.XMLHttpRequest.prototype&&window.XMLHttpRequest.prototype.open){',
    'var nativeOpen=window.XMLHttpRequest.prototype.open;',
    'window.XMLHttpRequest.prototype.open=function patchedOpen(method, url){',
    'var targetUrl=normalizeUrl(url,PAGE_BASE_URL);',
    'if(targetUrl&&shouldProxyUrl(targetUrl)){arguments[1]=buildProxyUrl(targetUrl);}',
    'return nativeOpen.apply(this, arguments);',
    '};',
    '}',
    'if(window.HTMLFormElement&&window.HTMLFormElement.prototype&&window.HTMLFormElement.prototype.submit){',
    'var nativeSubmit=window.HTMLFormElement.prototype.submit;',
    'window.HTMLFormElement.prototype.submit=function patchedSubmit(){',
    'var submitter=lastSubmitter&&lastSubmitter.form===this?lastSubmitter:null;',
    'var targetUrl=getSubmitTarget(this,submitter);',
    'if(submitter&&submitter.name){',
    "var hasField=!!this.querySelector('[name=\"'+CSS.escape(submitter.name)+'\"]');",
    'if(!hasField){',
    "var hidden=document.createElement('input');",
    "hidden.type='hidden';",
    'hidden.name=submitter.name;',
    "hidden.value=submitter.value||'';",
    "hidden.setAttribute('data-clicktt-submit-proxy','1');",
    'this.appendChild(hidden);',
    '}',
    '}',
    'try{console.log("[ClickTT Proxy] direct form.submit()",{action:this.getAttribute("action"),targetUrl:targetUrl,submitterName:submitter&&submitter.name?submitter.name:null,submitterValue:submitter&&submitter.value?submitter.value:null});}catch(e){}',
    "if(targetUrl&&shouldProxyUrl(targetUrl)){this.setAttribute('action',buildProxyUrl(targetUrl));}",
    'return nativeSubmit.apply(this,arguments);',
    '};',
    '}',
    '})();',
  ].join('');
  const script = `<script>${scriptContent}<\/script>`;

  if (/<head[^>]*>/i.test(html)) {
    return html.replace(/<head([^>]*)>/i, `<head$1>${script}`);
  }

  if (/<body[^>]*>/i.test(html)) {
    return html.replace(/<body([^>]*)>/i, `<body$1>${script}`);
  }

  if (/<\/body>/i.test(html)) {
    return html.replace(/<\/body>/i, `${script}</body>`);
  }

  return html + script;
}

/**
 * GET /api/clicktt/proxy
 * Proxy für iframe-Einbettung – liefert HTML direkt (ohne Auth, für iframe src).
 * Links zu click-tt.de/httv.de werden umgeschrieben, damit Folge-Klicks geloggt werden.
 * Query: type (leaguePage|clubInfo|regionMeetings), association, championship, clubId
 * ODER: url (vollständige URL, nur click-tt.de/httv.de)
 */
router.get('/proxy', async (req, res, next) => {
  try {
    const sid = getOrCreateSid(req);
    const cookies = cookieStore.get(sid) || {};

    const { type, association, championship, clubId, url } = req.query;
    let targetUrl = null;
    let fetchType = 'proxy';

    if (url) {
      if (!isAllowedProxyUrl(url)) {
        return res.status(400).send('<html><body><h1>Fehler</h1><p>Nur URLs von click-tt.de, httv.de oder liga.nu sind erlaubt.</p></body></html>');
      }
      targetUrl = url;
      fetchType = 'arbitrary';
    } else if (type === 'leaguePage') {
      targetUrl = clickTtHttpPageService.buildLeaguePageUrl({
        association: association || 'HeTTV',
        championship: championship || 'HTTV 25/26',
      });
      fetchType = 'leaguePage';
    } else if (type === 'clubInfo' && clubId) {
      targetUrl = clickTtHttpPageService.buildClubInfoDisplayUrl({
        association: association || 'HeTTV',
        clubId,
      });
      fetchType = 'clubInfoDisplay';
    } else if (type === 'regionMeetings') {
      targetUrl = clickTtHttpPageService.buildRegionMeetingFilterUrl({
        association: association || 'HeTTV',
        championship: championship || 'HTTV 25/26',
      });
      fetchType = 'regionMeetingFilter';
    } else {
      return res.status(400).send('<html><body><h1>Fehler</h1><p>Parameter type (leaguePage|clubInfo|regionMeetings) oder url erforderlich. Bei clubInfo ist clubId nötig.</p></body></html>');
    }

    const result = await clickTtHttpPageService.fetchWithLogging({
      url: targetUrl,
      fetchType,
      association: association || null,
      championship: championship || null,
      clubIdParam: clubId || null,
      userId: null,
      headers: Object.keys(cookies).length > 0 ? { Cookie: formatCookies(cookies) } : {},
    });
    const effectivePageUrl = result.responseUrl || targetUrl;

    const responseHeaders = result.headers;
    if (responseHeaders) {
      const raw = responseHeaders.raw?.();
      const setCookies = raw?.['set-cookie'];
      if (setCookies) {
        const newCookies = extractCookies(setCookies);
        if (Object.keys(newCookies).length > 0) {
          cookieStore.set(sid, { ...cookies, ...newCookies });
        }
      }
    }

    let html = result.body;
    // CSP/X-Frame-Options entfernen für iframe-Einbettung
    html = (html || '')
      .replace(/<meta[^>]*http-equiv=["']content-security-policy["'][^>]*>/gi, '')
      .replace(/<meta[^>]*http-equiv=["']x-frame-options["'][^>]*>/gi, '')
      .replace(/<meta[^>]*http-equiv=["']x-content-type-options["'][^>]*>/gi, '');

    // Base-Tag: Relative URLs (z.B. Formular-Actions beim Login) müssen zur Original-Domain auflösen
    const pageOrigin = (() => { try { return new URL(effectivePageUrl).origin + '/'; } catch { return null; } })();
    if (pageOrigin) {
      html = html.replace(/<base[^>]*>/gi, '');
      html = html.replace(/<head([^>]*)>/i, `<head$1><base href="${pageOrigin}">`);
    }

    // Links umschreiben: Klicks im iframe laufen über unseren Proxy → Folge-Logs
    // URL aus .env (BACKEND_BASE_URL oder BASE_URL), Fallback: Request-Host
    const baseUrl = process.env.BACKEND_BASE_URL || process.env.BASE_URL
      || `${req.protocol || 'http'}://${req.get('host') || 'localhost:' + (process.env.PORT || 3005)}`;
    const proxyBase = baseUrl.replace(/\/$/, '') + '/api/clicktt/proxy';
    html = rewriteLinksInHtml(html, proxyBase, effectivePageUrl, sid);
    html = rewriteFormActionsInHtml(html, proxyBase, effectivePageUrl, sid);
    html = rewriteMetaRefreshInHtml(html, proxyBase, effectivePageUrl, sid);
    html = normalizeInlineScriptStrings(html);
    html = injectProxyNavigationScript(html, proxyBase, effectivePageUrl, sid);

    res.set({
      'Content-Type': 'text/html; charset=utf-8',
      'Access-Control-Allow-Origin': '*',
      'X-Frame-Options': 'ALLOWALL',
      'Content-Security-Policy': 'frame-ancestors *;',
      'Cache-Control': 'no-cache, no-store, must-revalidate',
    });
    res.send(html);
  } catch (error) {
    console.error('ClickTT Proxy Fehler:', error);
    res.status(500).send(
      `<html><body><h1>Fehler beim Laden</h1><p>${String(error.message)}</p></body></html>`
    );
  }
});

/**
 * POST /api/clicktt/proxy
 * Formular-Weiterleitung (Login etc.) – POST wird an Ziel-URL weitergeleitet und geloggt.
 * Query: url (Ziel-URL, erforderlich)
 */
router.post('/proxy', async (req, res, next) => {
  try {
    const sid = getOrCreateSid(req);
    const cookies = cookieStore.get(sid) || {};

    const targetUrl = req.query.url;
    if (!targetUrl || !isAllowedProxyUrl(targetUrl)) {
      return res.status(400).send('<html><body><h1>Fehler</h1><p>Parameter url (click-tt.de, httv.de oder liga.nu) erforderlich.</p></body></html>');
    }

    const contentType = req.get('content-type') || 'application/x-www-form-urlencoded';
    const body = serializeFormBody(req);
    const bodySummary = summarizeFormBody(body);

    console.log('[ClickTT Proxy POST]', JSON.stringify({
      sid,
      targetUrl,
      contentType,
      fieldCount: bodySummary.fieldCount,
      fields: bodySummary.fields,
      parseError: bodySummary.parseError,
    }));

    const result = await clickTtHttpPageService.fetchWithLogging({
      url: targetUrl,
      fetchType: 'formPost',
      method: 'POST',
      body: body || undefined,
      headers: {
        'Content-Type': contentType,
        ...(Object.keys(cookies).length > 0 && { Cookie: formatCookies(cookies) }),
      },
      userId: null,
    });
    const effectivePageUrl = result.responseUrl || targetUrl;

    const responseHeaders = result.headers;
    if (responseHeaders) {
      const raw = responseHeaders.raw?.();
      const setCookies = raw?.['set-cookie'] ?? responseHeaders.get?.('set-cookie') ?? responseHeaders['set-cookie'];
      if (setCookies) {
        const newCookies = extractCookies(setCookies);
        if (Object.keys(newCookies).length > 0) {
          cookieStore.set(sid, { ...cookies, ...newCookies });
        }
        (Array.isArray(setCookies) ? setCookies : [setCookies]).forEach(c => res.append('Set-Cookie', c));
      }
      let location = responseHeaders.get?.('location') ?? responseHeaders['location'];
      if (location && (result.status >= 301 && result.status <= 308)) {
        if (location.startsWith('/')) {
          try {
            const base = new URL(targetUrl);
            location = base.origin + location;
          } catch { /* ignore */ }
        }
        const baseUrl = process.env.BACKEND_BASE_URL || process.env.BASE_URL
          || `${req.protocol || 'http'}://${req.get('host') || 'localhost:' + (process.env.PORT || 3005)}`;
        const proxyBase = baseUrl.replace(/\/$/, '') + '/api/clicktt/proxy';
        if (shouldProxyUrl(location)) {
          res.redirect(302, buildProxyUrl(proxyBase, location, sid));
          return;
        }
        res.redirect(result.status, location);
        return;
      }
    }

    let responseBody = result.body;
    const responseContentType = result.contentType || '';
    if (responseBody && responseContentType.includes('text/html')) {
      const baseUrl = process.env.BACKEND_BASE_URL || process.env.BASE_URL
        || `${req.protocol || 'http'}://${req.get('host') || 'localhost:' + (process.env.PORT || 3005)}`;
      const proxyBase = baseUrl.replace(/\/$/, '') + '/api/clicktt/proxy';
      responseBody = responseBody
        .replace(/<meta[^>]*http-equiv=["']content-security-policy["'][^>]*>/gi, '')
        .replace(/<meta[^>]*http-equiv=["']x-frame-options["'][^>]*>/gi, '');
      const pageOrigin = (() => { try { return new URL(effectivePageUrl).origin + '/'; } catch { return null; } })();
      if (pageOrigin) {
        responseBody = responseBody.replace(/<base[^>]*>/gi, '');
        responseBody = responseBody.replace(/<head([^>]*)>/i, `<head$1><base href="${pageOrigin}">`);
      }
      responseBody = rewriteLinksInHtml(responseBody, proxyBase, effectivePageUrl, sid);
      responseBody = rewriteFormActionsInHtml(responseBody, proxyBase, effectivePageUrl, sid);
      responseBody = rewriteMetaRefreshInHtml(responseBody, proxyBase, effectivePageUrl, sid);
      responseBody = normalizeInlineScriptStrings(responseBody);
      const inlineConfirmSummary = summarizeInlineConfirmElements(responseBody);
      if (inlineConfirmSummary.length > 0) {
        console.log('[ClickTT Proxy HTML inline confirm]', JSON.stringify({
          effectivePageUrl,
          inlineConfirmSummary
        }));
      }
      responseBody = injectProxyNavigationScript(responseBody, proxyBase, effectivePageUrl, sid);
    }

    res.set({
      'Content-Type': result.contentType || 'text/html; charset=utf-8',
      'Access-Control-Allow-Origin': '*',
      'X-Frame-Options': 'ALLOWALL',
      'Content-Security-Policy': 'frame-ancestors *;',
      'Cache-Control': 'no-cache, no-store, must-revalidate',
    });
    res.status(result.status).send(responseBody);
  } catch (error) {
    console.error('ClickTT Proxy POST Fehler:', error);
    res.status(500).send(
      `<html><body><h1>Fehler beim Senden</h1><p>${String(error.message)}</p></body></html>`
    );
  }
});

/**
 * GET /api/clicktt/league-page
 * Ruft die Ligenübersicht ab (leaguePage)
 * Query: association, championship
 */
router.get('/league-page', authenticate, async (req, res, next) => {
  try {
    const association = req.query.association || 'HeTTV';
    const championship = req.query.championship || 'HTTV 25/26';
    const userId = req.user?.id ?? null;

    const result = await clickTtHttpPageService.fetchLeaguePage({
      association,
      championship,
      userId,
    });

    res.json({
      success: result.success,
      status: result.status,
      contentType: result.contentType,
      executionTimeMs: result.executionTimeMs,
      bodyLength: result.body?.length ?? 0,
      body: result.body,
    });
  } catch (error) {
    next(error);
  }
});

/**
 * GET /api/clicktt/club-info
 * Ruft die Vereinsinfo ab (clubInfoDisplay)
 * Query: association, clubId
 */
router.get('/club-info', authenticate, async (req, res, next) => {
  try {
    const association = req.query.association || 'HeTTV';
    const clubId = req.query.clubId;
    const userId = req.user?.id ?? null;

    if (!clubId) {
      return res.status(400).json({ error: 'clubId ist erforderlich' });
    }

    const result = await clickTtHttpPageService.fetchClubInfoDisplay({
      association,
      clubId,
      userId,
    });

    res.json({
      success: result.success,
      status: result.status,
      contentType: result.contentType,
      executionTimeMs: result.executionTimeMs,
      bodyLength: result.body?.length ?? 0,
      body: result.body,
    });
  } catch (error) {
    next(error);
  }
});

/**
 * GET /api/clicktt/region-meetings
 * Ruft den Regionsspielplan ab (regionMeetingFilter)
 */
router.get('/region-meetings', authenticate, async (req, res, next) => {
  try {
    const association = req.query.association || 'HeTTV';
    const championship = req.query.championship || 'HTTV 25/26';
    const userId = req.user?.id ?? null;

    const result = await clickTtHttpPageService.fetchRegionMeetingFilter({
      association,
      championship,
      userId,
    });

    res.json({
      success: result.success,
      status: result.status,
      contentType: result.contentType,
      executionTimeMs: result.executionTimeMs,
      bodyLength: result.body?.length ?? 0,
      body: result.body,
    });
  } catch (error) {
    next(error);
  }
});

/**
 * GET /api/clicktt/fetch
 * Ruft eine beliebige URL ab (für manuelle Tests)
 * Query: url (erforderlich)
 */
router.get('/fetch', authenticate, async (req, res, next) => {
  try {
    const url = req.query.url;
    const userId = req.user?.id ?? null;

    if (!url) {
      return res.status(400).json({ error: 'url ist erforderlich' });
    }

    if (!isAllowedProxyUrl(url)) {
      return res.status(400).json({
        error: 'Nur URLs von click-tt.de, httv.de oder liga.nu sind erlaubt',
      });
    }

    const result = await clickTtHttpPageService.fetchArbitraryUrl(url, {
      fetchType: 'arbitrary',
      userId,
    });

    res.json({
      success: result.success,
      status: result.status,
      contentType: result.contentType,
      executionTimeMs: result.executionTimeMs,
      bodyLength: result.body?.length ?? 0,
      body: result.body,
    });
  } catch (error) {
    next(error);
  }
});

/**
 * GET /api/clicktt/logs
 * Liefert die letzten HTTP-Page-Fetch-Logs
 * Query: limit (default 50), fetchType, association, success
 */
router.get('/logs', authenticate, async (req, res, next) => {
  try {
    const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200);
    const { fetchType, association, success } = req.query;
    const includePayloads = req.query.includePayloads === 'true';

    const where = {};
    if (fetchType) where.fetchType = fetchType;
    if (association) where.association = association;
    if (success !== undefined) where.success = success === 'true';

    const attributes = [
      'id',
      'fetchType',
      'baseDomain',
      'fullUrl',
      'association',
      'championship',
      'clubIdParam',
      'requestMethod',
      'httpStatus',
      'success',
      'responseSnippet',
      'contentType',
      'responseUrl',
      'errorMessage',
      'executionTimeMs',
      'createdAt',
    ];

    if (includePayloads) {
      attributes.push(
        'requestHeaders',
        'requestBody',
        'responseHeaders',
        'responseBody',
      );
    }

    const logs = await HttpPageFetchLog.findAll({
      where: Object.keys(where).length > 0 ? where : undefined,
      order: [['createdAt', 'DESC']],
      limit,
      attributes,
    });

    res.json({
      success: true,
      count: logs.length,
      includePayloads,
      logs,
    });
  } catch (error) {
    next(error);
  }
});

/**
 * GET /api/clicktt/url-info
 * Liefert Informationen zur URL-Struktur (Verband→Domain, Beispiel-URLs)
 */
router.get('/url-info', authenticate, async (req, res, next) => {
  try {
    const info = {
      associationToDomain: clickTtHttpPageService.ASSOCIATION_TO_DOMAIN,
      exampleUrls: {
        leaguePage: clickTtHttpPageService.buildLeaguePageUrl({
          association: 'HeTTV',
          championship: 'HTTV 25/26',
        }),
        leaguePageBezirk: clickTtHttpPageService.buildLeaguePageUrl({
          association: 'HeTTV',
          championship: 'K43 25/26',
        }),
        clubInfoDisplay: clickTtHttpPageService.buildClubInfoDisplayUrl({
          association: 'HeTTV',
          clubId: '1060',
        }),
        regionMeetingFilter: clickTtHttpPageService.buildRegionMeetingFilterUrl({
          association: 'HeTTV',
          championship: 'HTTV 25/26',
        }),
      },
      championshipExamples: [
        'HTTV 25/26',
        'HTTV 24/25',
        'K43 25/26',
        'K16 25/26',
        'RL-OL West 25/26',
      ],
    };

    res.json({ success: true, ...info });
  } catch (error) {
    next(error);
  }
});

export default router;
