const TRUSTED_PROVIDERS = ["thorchain", "chainflip", "mayan", "li.fi", "lifi", "1inch", "0x", "jupiter", "velora"];
const TRUSTED_CHAINS = ["ethereum", "bitcoin", "btc", "solana", "polygon", "arbitrum", "optimism", "base", "bnb"];
const TRUSTED_ASSETS = ["BTC", "ETH", "USDC", "USDT", "SOL", "WBTC", "MATIC", "POL", "ARB", "OP", "BNB", "DAI"];
const UTXO_GUARD_PRODUCT_NOTICE = "UTXO Guard licensed technology by Vestige Index. Redistribution, rebranding or commercial resale requires a valid license.";
const TRUSTED_DOMAIN_PROFILES = [
  {
    host: "vestigeindex.com",
    label: "Vestige Index",
    riskPercent: 5,
    maxRiskPercent: 10,
    category: "Non-custodial aggregator of aggregators",
    rationale: "Official Vestige Index domain. Non-custodial routing interface confirmed. Vestige Index is not a broker, custodian or liquidity provider and routes through external execution providers only.",
    assertions: [
      "No wallet-secret request detected.",
      "No private key, seed phrase, recovery phrase or mnemonic prompt found.",
      "External routing providers only.",
      "User confirms all actions inside the connected wallet.",
    ],
  },
  {
    host: "vestige-index.pages.dev",
    label: "Vestige Index preview",
    riskPercent: 8,
    maxRiskPercent: 10,
    category: "Verified Vestige Index Cloudflare preview",
    rationale: "Known Vestige Index test deployment. Non-custodial routing interface confirmed. Preview environments carry slightly higher operational risk than production.",
    assertions: [
      "No wallet-secret request detected.",
      "No private key, seed phrase, recovery phrase or mnemonic prompt found.",
      "External routing providers only.",
      "User confirms all actions inside the connected wallet.",
    ],
  },
  {
    host: "app.uniswap.org",
    label: "Uniswap",
    riskPercent: 10,
    category: "Established non-custodial DEX",
    rationale: "Official Uniswap app domain. Protocol risk remains, but the domain itself is an established non-custodial exchange surface.",
  },
  {
    host: "uniswap.org",
    label: "Uniswap",
    riskPercent: 10,
    category: "Established non-custodial DEX",
    rationale: "Official Uniswap domain. Risk reflects protocol, wallet approval and market execution risk, not custody risk.",
  },
  {
    host: "app.aave.com",
    label: "Aave",
    riskPercent: 12,
    category: "Established DeFi lending protocol",
    rationale: "Official Aave app domain. Users still carry smart-contract, liquidation and wallet approval risk.",
  },
  {
    host: "jup.ag",
    label: "Jupiter",
    riskPercent: 12,
    category: "Established Solana routing aggregator",
    rationale: "Official Jupiter domain. Risk reflects Solana routing, slippage and wallet confirmation risk.",
  },
  {
    host: "app.1inch.io",
    label: "1inch",
    riskPercent: 12,
    category: "Established EVM routing aggregator",
    rationale: "Official 1inch app domain. Risk reflects route, approval and execution conditions.",
  },
  {
    host: "app.thorchain.org",
    label: "THORChain",
    riskPercent: 15,
    category: "Cross-chain liquidity protocol",
    rationale: "Official THORChain app domain. Cross-chain execution carries additional route and settlement risk.",
  },
];

function normalizeText(value) {
  return String(value || "").toLowerCase();
}

function clampRisk(value) {
  return Math.max(0, Math.min(100, Math.round(Number(value) || 0)));
}

function riskFromPercent(riskPercent) {
  if (riskPercent >= 75) return "critical";
  if (riskPercent >= 50) return "high";
  if (riskPercent >= 25) return "medium";
  return "low";
}

function decisionFromRisk(risk) {
  if (risk === "critical") return "block";
  if (risk === "high") return "warn";
  if (risk === "medium") return "review";
  return "allow";
}

function layer(layer, passed, risk, scoreDelta, warnings) {
  return { layer, passed, risk, scoreDelta, warnings };
}

function hostnameFromUrl(url) {
  try {
    return new URL(url || "https://local.invalid").hostname.toLowerCase();
  } catch {
    return "";
  }
}

function findDomainProfile(url) {
  const hostname = hostnameFromUrl(url);
  if (!hostname) return null;
  return (
    TRUSTED_DOMAIN_PROFILES.find((profile) => hostname === profile.host || hostname.endsWith(`.${profile.host}`)) ||
    null
  );
}

function applyProfileRiskPolicy(profile, riskPercent) {
  if (!profile?.maxRiskPercent) return riskPercent;
  return clampRisk(Math.min(riskPercent, profile.maxRiskPercent));
}

function hasPreventiveSecretWarning(text) {
  const normalized = normalizeText(text);
  const secret = "(seed phrase|recovery phrase|mnemonic|private key|wallet secret|wallet password|wallet secrets)";
  const preventive = "(?:\\bnever\\b|\\bdo not\\b|\\bdon't\\b|\\bdoes not\\b|\\bwill never\\b|\\bwithout\\b|\\bno\\b|\\bnunca\\b|\\bjamais\\b|\\bne jamais\\b|\\bsin\\b)";
  return new RegExp(`${preventive}.{0,100}${secret}`, "i").test(normalized);
}

function hasSecretRequestPrompt(text) {
  const normalized = normalizeText(text);
  if (!normalized) return false;
  const secret = "(seed phrase|recovery phrase|mnemonic|private key|wallet secret|wallet password)";
  const request = "(enter|input|paste|type|submit|provide|import|restore|unlock|verify|confirm|required|field|placeholder)";
  return new RegExp(`${request}.{0,100}${secret}|${secret}.{0,100}${request}`, "i").test(normalized);
}

function normalizeSecretSignals(signals, pageText = "", secretPromptText = "") {
  const nextSignals = new Set(signals || []);
  // Preventive security warnings must not be treated as secret-request prompts.
  const hasDangerousPrompt = hasSecretRequestPrompt(secretPromptText) || (!hasPreventiveSecretWarning(pageText) && hasSecretRequestPrompt(pageText));
  if (!hasDangerousPrompt) {
    nextSignals.delete("seed_phrase");
    nextSignals.delete("private_key");
  }
  return [...nextSignals];
}

function isBrowseOnly(input) {
  const action = normalizeText(input.action || "");
  const signals = input.uiSignals || [];
  return !action || action === "browse" || (!signals.includes("approve") && !signals.includes("swap") && !signals.includes("bridge") && !signals.includes("claim"));
}

function detectUiSignals(text, secretPromptText = "") {
  const normalized = normalizeText(text);
  const signals = [];
  if (normalized.includes("connect wallet")) signals.push("connect_wallet");
  if (normalized.includes("approve")) signals.push("approve");
  if (normalized.includes("claim")) signals.push("claim");
  if (normalized.includes("airdrop")) signals.push("airdrop");
  if (normalized.includes("swap")) signals.push("swap");
  if (normalized.includes("bridge")) signals.push("bridge");
  // Preventive security warnings must not be treated as secret-request prompts.
  if (hasSecretRequestPrompt(secretPromptText) || (!hasPreventiveSecretWarning(text) && hasSecretRequestPrompt(text))) {
    if (/(seed phrase|recovery phrase|mnemonic)/i.test(`${text}\n${secretPromptText}`)) signals.push("seed_phrase");
    if (/(private key|wallet secret|wallet password)/i.test(`${text}\n${secretPromptText}`)) signals.push("private_key");
  }
  if (/unlimited/i.test(text)) signals.push("unlimited");
  return signals;
}

function contextQualityFromInput(input = {}, pageText = "", uiSignals = []) {
  const action = normalizeText(input.action || "");
  const hasReadablePage = pageText.trim().length > 160;
  const hasAction = Boolean(action && action !== "browse");
  const hasRouteContext = Boolean(
    input.provider ||
      input.chain ||
      input.network ||
      input.token ||
      input.tokenIn ||
      input.tokenOut ||
      input.contractAddress ||
      input.destination ||
      input.amountUSD ||
      input.slippageBps
  );
  const hasWalletSignal = uiSignals.some((signal) =>
    ["approve", "swap", "bridge", "claim", "connect_wallet", "seed_phrase", "private_key", "unlimited"].includes(signal)
  );
  return hasReadablePage || hasAction || hasRouteContext || hasWalletSignal ? "observed" : "insufficient";
}

function hasStrongCriticalEvidence(input = {}, pageText = "", uiSignals = []) {
  return (
    uiSignals.includes("seed_phrase") ||
    uiSignals.includes("private_key") ||
    /\b(wallet drainer|drain wallet|steal funds|steal crypto)\b/i.test(pageText) ||
    Number(input.slippageBps || 0) > 1500
  );
}

function checkL1Network(input) {
  const warnings = [];
  let scoreDelta = 0;
  const chain = normalizeText(input.chain || input.network || "");

  if (!chain && isBrowseOnly(input)) {
    return layer("L1 Network", true, "low", 0, []);
  }

  if (!chain) {
    warnings.push("No explicit network detected. Verify the wallet network before signing.");
    scoreDelta = 8;
  } else if (!TRUSTED_CHAINS.some((item) => chain.includes(item))) {
    warnings.push(`Unknown or unsupported network context: ${input.chain || input.network}.`);
    scoreDelta = 15;
  }

  return layer("L1 Network", scoreDelta < 12, scoreDelta >= 15 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL2Asset(input) {
  const warnings = [];
  let scoreDelta = 0;
  const token = String(input.token || input.tokenIn || input.tokenOut || "").toUpperCase();

  if (!token && isBrowseOnly(input)) {
    return layer("L2 Asset", true, "low", 0, []);
  }

  if (!token) {
    warnings.push("No asset symbol detected. Confirm the asset inside the wallet prompt.");
    scoreDelta = 8;
  } else if (!TRUSTED_ASSETS.includes(token)) {
    warnings.push(`Verify token identity and contract for ${token}.`);
    scoreDelta = 6;
  }

  if (/fake|airdrop|reward|bonus/.test(normalizeText(token))) {
    warnings.push("Suspicious asset naming pattern detected.");
    scoreDelta += 24;
  }

  return layer("L2 Asset", scoreDelta < 15, scoreDelta >= 25 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL3Contract(input) {
  const warnings = [];
  let scoreDelta = 0;
  const address = normalizeText(input.contractAddress || input.destination || "");

  if (!address) {
    return layer("L3 Contract", true, "low", 0, ["No contract address exposed on this surface."]);
  }

  if (!/^0x[a-f0-9]{40}$/.test(address) && !/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) {
    warnings.push("Destination or contract format should be verified.");
    scoreDelta = 12;
  }

  if (/0x0{8,}|f{8,}/.test(address)) {
    warnings.push("Suspicious placeholder-like contract pattern.");
    scoreDelta += 18;
  }

  return layer("L3 Contract", scoreDelta < 20, scoreDelta >= 25 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL4Transaction(input) {
  const warnings = [];
  let scoreDelta = 0;
  const signals = input.uiSignals || [];

  if ((input.action === "approve" || signals.includes("approve")) && signals.includes("unlimited")) {
    warnings.push("Unlimited approval language detected. Prefer limited approvals.");
    scoreDelta += 20;
  }

  if (Number(input.amountUSD || 0) > 10000) {
    warnings.push("Large notional amount. Re-check recipient, slippage and route.");
    scoreDelta += 12;
  }

  if (signals.includes("seed_phrase") || signals.includes("private_key")) {
    warnings.push("Critical wallet-secret prompt detected. Never enter seed phrases or private keys.");
    scoreDelta += 65;
  }

  return layer("L4 Transaction", scoreDelta < 18, scoreDelta >= 55 ? "critical" : scoreDelta >= 20 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL5Route(input) {
  const warnings = [];
  let scoreDelta = 0;
  const provider = normalizeText(input.provider || "");

  if (provider && !TRUSTED_PROVIDERS.some((item) => provider.includes(item))) {
    warnings.push(`Provider ${input.provider} is not in the curated route list.`);
    scoreDelta += 14;
  }

  if (Number(input.slippageBps || 0) > 500) {
    warnings.push(`High slippage: ${(Number(input.slippageBps) / 100).toFixed(2)}%.`);
    scoreDelta += 15;
  }

  if (input.feeUSD && input.amountUSD && Number(input.feeUSD) / Number(input.amountUSD) > 0.05) {
    warnings.push("High fee impact relative to trade amount.");
    scoreDelta += 12;
  }

  return layer("L5 Route", scoreDelta < 18, scoreDelta >= 25 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL6Interface(input) {
  const warnings = [];
  let scoreDelta = 0;
  const text = String(input.pageText || "");
  const signals = input.uiSignals || detectUiSignals(text, input.secretPromptText || "");

  if (signals.includes("seed_phrase") || signals.includes("private_key")) {
    warnings.push("Wallet-secret request prompt detected in an input, label, modal, form, placeholder or CTA.");
    scoreDelta += 70;
  }

  if (signals.includes("claim") && signals.includes("connect_wallet")) {
    warnings.push("Claim + wallet connection pattern detected. Confirm legitimacy.");
    scoreDelta += 16;
  }

  if (/urgent|limited time|verify wallet|synchronize wallet|free crypto|guaranteed/i.test(text)) {
    warnings.push("Pressure or reward-bait language detected.");
    scoreDelta += 12;
  }

  return layer("L6 Interface", scoreDelta < 20, scoreDelta >= 55 ? "critical" : scoreDelta >= 25 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function checkL7Behavior(input) {
  const warnings = [];
  let scoreDelta = 0;

  try {
    const hostname = new URL(input.url || "https://local.invalid").hostname.toLowerCase();
    if (/claim|airdrop|bonus|verify|wallet|free/.test(hostname) && !/vestigeindex\.com$/.test(hostname)) {
      warnings.push("Domain naming resembles common wallet-drain or claim campaigns.");
      scoreDelta += 18;
    }
  } catch {
    warnings.push("URL could not be parsed for behavioral checks.");
    scoreDelta += 6;
  }

  if (/you won|reward waiting|connect now|signature required/i.test(String(input.pageText || ""))) {
    warnings.push("Behavioral scam pattern detected.");
    scoreDelta += 20;
  }

  return layer("L7 Behavior", scoreDelta < 20, scoreDelta >= 30 ? "high" : scoreDelta ? "medium" : "low", scoreDelta, warnings);
}

function analyzeUTXOGuard(input = {}) {
  const pageText = String(input.pageText || "");
  const secretPromptText = String(input.secretPromptText || "");
  const uiSignals = normalizeSecretSignals(input.uiSignals || detectUiSignals(pageText, secretPromptText), pageText, secretPromptText);
  const nextInput = {
    ...input,
    pageText,
    secretPromptText,
    uiSignals,
  };
  const profile = findDomainProfile(nextInput.url);
  const contextQuality = profile ? "profiled" : contextQualityFromInput(nextInput, pageText, uiSignals);
  const strongCriticalEvidence = hasStrongCriticalEvidence(nextInput, pageText, uiSignals);
  const layers = [
    checkL1Network(nextInput),
    checkL2Asset(nextInput),
    checkL3Contract(nextInput),
    checkL4Transaction(nextInput),
    checkL5Route(nextInput),
    checkL6Interface(nextInput),
    checkL7Behavior(nextInput),
  ];
  const scoreDelta = layers.reduce((sum, item) => sum + item.scoreDelta, 0);
  const signalDelta = layers
    .filter((item) => ["L4 Transaction", "L5 Route", "L6 Interface", "L7 Behavior"].includes(item.layer))
    .reduce((sum, item) => sum + item.scoreDelta, 0);
  const rawRiskPercent = clampRisk(profile ? profile.riskPercent + signalDelta : scoreDelta);
  const riskPercent = applyProfileRiskPolicy(
    profile,
    contextQuality === "insufficient" && !strongCriticalEvidence
      ? clampRisk(Math.min(Math.max(rawRiskPercent, 18), 24))
      : rawRiskPercent,
  );
  const score = Math.max(0, Math.min(100, 100 - riskPercent));
  const warnings = layers.flatMap((item) => item.warnings);
  const contextWarning =
    contextQuality === "insufficient"
      ? "Insufficient context for a final judgement. UTXO Guard is using a conservative review baseline instead of a critical verdict."
      : null;
  const hasCritical = strongCriticalEvidence && layers.some((item) => item.risk === "critical");
  const risk = hasCritical ? "critical" : riskFromPercent(riskPercent);
  const decision = contextQuality === "insufficient" && !strongCriticalEvidence ? "review" : decisionFromRisk(risk);
  const secretRequestDetected = uiSignals.includes("seed_phrase") || uiSignals.includes("private_key");
  const profileAssertions = profile && !secretRequestDetected ? profile.assertions || [] : [];
  const recommendations =
    decision === "block"
      ? ["Do not sign. Close the page and verify the source from an official channel."]
      : decision === "warn"
        ? ["Review route, recipient, approval scope and wallet prompt before signing."]
        : decision === "review"
          ? ["Double-check wallet prompt and route details before continuing."]
          : ["Standard wallet verification still applies before signing."];

  return {
    score,
    riskPercent,
    risk,
    decision,
    contextQuality,
    contextConfidence: contextQuality,
    warnings: profile
      ? [`Recognized profile: ${profile.rationale}`, ...profileAssertions, ...warnings]
      : contextWarning
        ? [contextWarning, ...warnings]
        : warnings,
    recommendations,
    layers,
    trustProfile: profile,
    scoringEngine: "UTXO Score AI deterministic v1",
    productId: "utxo-guard",
    watermark: UTXO_GUARD_PRODUCT_NOTICE,
    explanation: profile
      ? `${profile.label} risk ${riskPercent}%. ${profile.category}.`
      : contextQuality === "insufficient"
        ? `UTXO Guard needs more page or transaction context. Current conservative review risk is ${riskPercent}%.`
      : `UTXO Guard risk ${riskPercent}%. Decision ${decision}.`,
  };
}

self.analyzeUTXOGuard = analyzeUTXOGuard;
