'use strict';

const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);

function extractLoopbackUrls(text) {
  const source = String(text || '');
  const matches = source.match(/https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:\/[^\s<>)\]]*)?/gi) || [];
  const seen = new Set();
  const urls = [];
  for (const match of matches) {
    const raw = match.replace(/[.,;:!?]+$/g, '');
    const normalized = normalizeLoopbackUrl(raw);
    if (!normalized || seen.has(normalized.href)) continue;
    seen.add(normalized.href);
    urls.push({ raw, ...normalized });
  }
  return urls;
}

function normalizeLoopbackUrl(raw) {
  try {
    const parsed = new URL(String(raw || '').trim());
    const hostname = parsed.hostname.toLowerCase();
    if (!LOOPBACK_HOSTS.has(hostname) && !LOOPBACK_HOSTS.has(parsed.host.toLowerCase())) return null;
    const port = parsed.port || (parsed.protocol === 'https:' ? '443' : '80');
    const pathname = parsed.pathname || '/';
    const normalizedPath = pathname === '/index.html' ? '/' : pathname.replace(/\/+$/, '') || '/';
    return {
      href: parsed.href,
      origin: `${parsed.protocol}//${hostname}:${port}`,
      protocol: parsed.protocol,
      hostname,
      port,
      pathname,
      routeKey: `${parsed.protocol}//${hostname}:${port}${normalizedPath}`,
    };
  } catch {
    return null;
  }
}

function analyzeLocalPreviewClaims(text, toolCalls = []) {
  const urls = extractLoopbackUrls(text);
  if (urls.length === 0) {
    return { ok: true, hasPositiveClaim: false, urls: [], unsupportedUrls: [], verifiedUrls: [] };
  }
  const positiveUrls = urls.filter((url) => _looksLikePositivePreviewClaim(text, url.raw));
  if (positiveUrls.length === 0) {
    return { ok: true, hasPositiveClaim: false, urls, unsupportedUrls: [], verifiedUrls: [] };
  }
  const evidence = _collectPreviewEvidence(toolCalls);
  const unsupportedUrls = [];
  const verifiedUrls = [];
  for (const url of positiveUrls) {
    const match = evidence.find((item) => _evidenceSupportsUrl(item, url));
    if (match) verifiedUrls.push({ ...url, evidence: match });
    else unsupportedUrls.push(url);
  }
  return {
    ok: unsupportedUrls.length === 0,
    hasPositiveClaim: true,
    urls: positiveUrls,
    unsupportedUrls,
    verifiedUrls,
    evidence,
  };
}

function buildLocalPreviewVerificationNudge(analysis) {
  const urls = (analysis?.unsupportedUrls || []).map((url) => url.raw).join(', ');
  return [
    `[System: Your previous response claimed local preview URL(s) were live or reachable without current successful preview-tool evidence: ${urls || '(local URL)'}.`,
    'Do not claim HTTP 200, "server is back", "restarted", "serving", "live", or "reachable" from memory, terminal text, or a background/nohup shell command.',
    'Call check_url for the exact URL now, or call start_static_server for the target directory if you need a managed server, then answer from that tool result.',
    'If the check fails, say the URL is not reachable and include the actual error. If the URL is localhost/127.0.0.1, say it is only verified from Wall-E host loopback; phone or remote browser access needs CTM remote/tunnel context.]',
  ].join(' ');
}

function buildUnsupportedLocalPreviewReply(analysis) {
  const urls = (analysis?.unsupportedUrls || []).map((url) => url.raw).join(', ') || 'the local preview URL';
  return [
    `I cannot honestly confirm ${urls} is live yet.`,
    '',
    'The previous answer attempted to claim a local preview was reachable, but this turn has no successful `check_url`, `start_static_server`, or `browser_screenshot` evidence for that URL. A `localhost` or `127.0.0.1` link also only proves Wall-E host loopback access; phone or remote browser access needs the CTM remote/tunnel path.',
  ].join('\n');
}

function _looksLikePositivePreviewClaim(text, rawUrl) {
  const source = String(text || '');
  const idx = source.indexOf(rawUrl);
  const window = idx >= 0
    ? source.slice(Math.max(0, idx - 180), Math.min(source.length, idx + rawUrl.length + 220))
    : source;
  const lower = window.toLowerCase();
  const positive = /\b(?:http\s*200|status\s*200|server(?:\s+is)?\s+(?:back|up|running)|back\s+up|reachable|accessible|available|live|serving|started|restarted|ready|works|open\s+(?:it|this|the)|visit|you\s+can\s+(?:open|use|view|access))\b/.test(lower)
    || /(?:→|->)\s*http\s*200/i.test(window);
  if (!positive) return false;
  const negative = /\b(?:not|isn['’]?t|cannot|can['’]?t|couldn['’]?t|failed|failure|refused|timed\s*out|unreachable|inaccessible|not\s+reachable|not\s+accessible|not\s+available|down|no\s+listener|connection\s+refused|does\s+not)\b/.test(lower);
  if (!negative) return true;
  return /\b(?:now|after|restarted|server(?:\s+is)?\s+back|http\s*200|status\s*200)\b/.test(lower)
    && !/\b(?:still|remains)\s+(?:not\s+)?(?:down|unreachable|inaccessible|broken)\b/.test(lower);
}

function _collectPreviewEvidence(toolCalls = []) {
  const evidence = [];
  for (const call of Array.isArray(toolCalls) ? toolCalls : []) {
    const name = call?.tool || call?.name;
    if (!['check_url', 'start_static_server', 'browser_screenshot'].includes(name)) continue;
    if (call?.ok !== true) continue;
    const result = call.result || {};
    const ok = result.ok === true
      || result.health?.ok === true
      || (name === 'browser_screenshot' && result.path && !result.error);
    if (!ok) continue;
    const candidateUrls = [
      result.url,
      result.health?.url,
      call.args?.url,
      call.input?.url,
    ].filter(Boolean);
    for (const candidate of candidateUrls) {
      const normalized = normalizeLoopbackUrl(candidate);
      if (!normalized) continue;
      evidence.push({
        tool: name,
        url: candidate,
        ...normalized,
      });
    }
  }
  return evidence;
}

function _evidenceSupportsUrl(evidence, claim) {
  if (!evidence || !claim) return false;
  if (evidence.routeKey === claim.routeKey) return true;
  if (evidence.origin !== claim.origin && !_sameLoopbackEndpoint(evidence, claim)) return false;
  const evidencePath = evidence.pathname || '/';
  const claimPath = claim.pathname || '/';
  return (evidencePath === '/index.html' && claimPath === '/')
    || (evidencePath === '/' && claimPath === '/index.html');
}

function _sameLoopbackEndpoint(a, b) {
  return a.protocol === b.protocol
    && String(a.port || '') === String(b.port || '')
    && LOOPBACK_HOSTS.has(String(a.hostname || '').toLowerCase())
    && LOOPBACK_HOSTS.has(String(b.hostname || '').toLowerCase());
}

module.exports = {
  analyzeLocalPreviewClaims,
  buildLocalPreviewVerificationNudge,
  buildUnsupportedLocalPreviewReply,
  extractLoopbackUrls,
  normalizeLoopbackUrl,
};
