import { fallbackAnalysis } from "./policy.mjs";

const DEFAULT_API = "https://veersec.io";
const DEFAULT_APP = "https://veersec.io";

function deriveAppBase(apiBase) {
  try {
    const url = new URL(apiBase);
    if ((url.hostname === "localhost" || url.hostname === "127.0.0.1") && (url.port === "4000" || url.port === "3001")) {
      url.port = "3000";
      return url.toString().replace(/\/$/, "");
    }
    return apiBase;
  } catch {
    return DEFAULT_APP;
  }
}

function buildDetailUrl(inspection, appBase) {
  const base = String(appBase || DEFAULT_APP).replace(/\/$/, "");
  if (inspection?.trustPassportUrl) return `${base}${String(inspection.trustPassportUrl).startsWith("/") ? "" : "/"}${inspection.trustPassportUrl}`;
  const scoreId = inspection?.scoreId || null;
  const protocolId = inspection?.protocolId || inspection?.protocol?.id || inspection?.exactContractTrust?.protocol?.id || null;
  const passportId = inspection?.exactContractTrust?.protocol?.public_id || null;
  if (protocolId) {
    const suffix = scoreId ? `?score=${encodeURIComponent(scoreId)}` : "";
    return `${base}/investor/protocol/${encodeURIComponent(protocolId)}${suffix}`;
  }
  if (scoreId) return `${base}/investor?score=${encodeURIComponent(scoreId)}`;
  if (passportId) return `${base}/passport/${encodeURIComponent(passportId)}`;
  return `${base}/investor`;
}

function parseAddressWord(word) {
  const raw = String(word || "").replace(/^0x/, "");
  if (!/^0{24}[0-9a-fA-F]{40}$/.test(raw)) return null;
  return `0x${raw.slice(-40)}`.toLowerCase();
}

function firstTransaction(method, params) {
  if (method === "wallet_sendCalls") {
    const candidate = Array.isArray(params) ? params[0] : null;
    const calls = Array.isArray(candidate?.calls) ? candidate.calls : Array.isArray(candidate) ? candidate : [];
    return calls.find((call) => call && typeof call === "object") || null;
  }
  const first = Array.isArray(params) ? params[0] : null;
  return first && typeof first === "object" && !Array.isArray(first) ? first : null;
}

function normalizeInspectPayload(payload) {
  const method = String(payload?.method || "");
  const params = Array.isArray(payload?.params) ? payload.params : [];
  const tx = firstTransaction(method, params) || {};
  const data = String(tx.data || tx.input || payload?.data || "0x").toLowerCase();
  const functionSelector = /^0x[0-9a-f]{8}/.test(data) ? data.slice(0, 10) : null;
  const approval = {};
  if ((functionSelector === "0x095ea7b3" || functionSelector === "0xa22cb465") && data.length >= 138) {
    approval.spender = parseAddressWord(data.slice(10, 74));
    approval.tokenAddress = String(tx.to || payload?.to || "").toLowerCase() || null;
  }
  return {
    chainId: tx.chainId || payload?.chainId || payload?.chain_id || null,
    from: tx.from || payload?.from || null,
    to: tx.to || payload?.to || null,
    value: tx.value || payload?.value || "0",
    data,
    domain: payload?.origin || payload?.domain || null,
    method,
    functionSelector,
    timestamp: new Date().toISOString(),
    ...approval
  };
}

function riskToVerdictRisk(riskLevel) {
  const value = String(riskLevel || "").toLowerCase();
  if (value === "low") return "green";
  if (value === "medium") return "yellow";
  if (value === "high" || value === "critical") return "red";
  return "grey";
}

function attachVerdict(inspection, appBase) {
  const enriched = {
    ...inspection,
    contractAddress: inspection?.contractAddress || inspection?.request?.to || inspection?.exactContractTrust?.contract_address || null,
    detailUrl: buildDetailUrl(inspection, appBase)
  };
  if (enriched?.verdict) return enriched;
  const risk = riskToVerdictRisk(inspection?.riskLevel);
  return {
    ...enriched,
    verdict: {
      risk,
      label: inspection?.warningTitle || "VeerSec Contract Shield",
      action: risk === "green" ? "allow" : risk === "red" ? "block" : "confirm",
      requires_confirmation: risk !== "green",
      requires_explicit_override: risk === "red",
      reasons: inspection?.reasons || []
    },
    privacy: {
      persisted_request_data: false,
      persisted_wallet_address: false,
      persisted_calldata: false,
      persisted_signature_or_message: false
    }
  };
}

async function getBases() {
  const stored = await chrome.storage.local.get(["veersecApiBase", "veersecAppBase"]);
  const apiBase = String(stored.veersecApiBase || DEFAULT_API).replace(/\/$/, "");
  const appBase = String(stored.veersecAppBase || deriveAppBase(apiBase)).replace(/\/$/, "");
  return { apiBase, appBase };
}

function getFingerprint(payload, chainId, account, origin) {
  const manifest = chrome.runtime.getManifest();
  const version = manifest.version;
  const method = payload.method || "";
  const params = JSON.stringify(payload.params || []);
  const requestId = payload.requestId || "";
  return `${version}|${origin}|${chainId}|${account}|${method}|${params}|${requestId}`;
}

// In-memory queue state
const memoryQueues = {};
const processingQueues = new Set();
const activeCallbacks = new Map();
const decisionCache = new Map();
const CACHE_EXPIRY_MS = 5000;

function getCachedDecision(fingerprint) {
  const entry = decisionCache.get(fingerprint);
  if (entry && (Date.now() - entry.timestamp) < CACHE_EXPIRY_MS) {
    return entry.result;
  }
  decisionCache.delete(fingerprint);
  return null;
}

function setCachedDecision(fingerprint, result) {
  decisionCache.set(fingerprint, {
    result,
    timestamp: Date.now()
  });
}

async function syncQueueStorage() {
  const serialized = {};
  for (const [key, items] of Object.entries(memoryQueues)) {
    if (items && items.length > 0) {
      serialized[key] = items.map(item => ({
        requestId: item.requestId,
        method: item.method,
        params: item.params,
        chainId: item.chainId,
        origin: item.origin,
        fingerprint: item.fingerprint,
        timestamp: item.timestamp
      }));
    }
  }
  try {
    await chrome.storage.session.set({ pending_requests: serialized });
  } catch (e) {
    console.error("Failed to sync queue storage", e);
  }
}

async function executeInspectTxFallback(apiBase, appBase, payload) {
  const response = await fetch(`${apiBase}/api/extension/inspect-tx`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(9000)
  });
  if (!response.ok) {
    throw new Error(`veersec_api_http_${response.status}`);
  }
  const data = await response.json();
  return attachVerdict(data, appBase);
}

async function executeRequest(requestItem) {
  const { apiBase, appBase } = await getBases();
  const payload = {
    ...normalizeInspectPayload(requestItem),
    params: requestItem.params || [],
    origin: requestItem.origin || null
  };

  try {
    const response = await fetch(`${apiBase}/api/public/extension/analyze`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
      signal: AbortSignal.timeout(9000)
    });
    if (response.status === 404) {
      return await executeInspectTxFallback(apiBase, appBase, payload);
    }
    if (!response.ok) {
      throw new Error(`veersec_api_http_${response.status}`);
    }
    let analysis = await response.json();
    if (analysis && analysis.contract_shield) {
      analysis = {
        ...analysis.contract_shield,
        ...analysis
      };
    }
    return attachVerdict(analysis, appBase);
  } catch (error) {
    if (error.message && error.message.includes("404")) {
      return await executeInspectTxFallback(apiBase, appBase, payload);
    }
    try {
      return await executeInspectTxFallback(apiBase, appBase, payload);
    } catch {
      return fallbackAnalysis(error?.message || "veersec_api_unavailable");
    }
  }
}

async function processQueue(queueKey) {
  if (processingQueues.has(queueKey)) {
    return;
  }
  processingQueues.add(queueKey);

  try {
    while (memoryQueues[queueKey] && memoryQueues[queueKey].length > 0) {
      const nextRequest = memoryQueues[queueKey][0];
      
      let result;
      try {
        result = await executeRequest(nextRequest);
      } catch (error) {
        result = fallbackAnalysis(error?.message || "veersec_api_unavailable");
      }

      setCachedDecision(nextRequest.fingerprint, result);

      const callbacks = activeCallbacks.get(nextRequest.fingerprint);
      if (callbacks) {
        for (const cb of callbacks) {
          try {
            cb(result);
          } catch (e) {
            console.error("Error calling sendResponse callback", e);
          }
        }
        activeCallbacks.delete(nextRequest.fingerprint);
      }

      memoryQueues[queueKey].shift();
      await syncQueueStorage();
    }
  } finally {
    processingQueues.delete(queueKey);
  }
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message?.type !== "veersec:analyze") return false;
  
  (async () => {
    const payload = message.payload || {};
    const method = String(payload.method || "");
    const params = Array.isArray(payload.params) ? payload.params : [];
    const chainId = payload.chainId || null;
    const origin = payload.origin || null;
    const tabId = sender.tab?.id || 0;
    
    const tx = firstTransaction(method, params) || {};
    const account = (tx.from || payload.from || "default").toLowerCase();
    const provider = payload.provider || "default";
    const queueKey = `${tabId}:${provider}:${account}`;
    
    const fingerprint = getFingerprint(payload, chainId, account, origin);
    
    const cached = getCachedDecision(fingerprint);
    if (cached) {
      sendResponse(cached);
      return;
    }
    
    if (activeCallbacks.has(fingerprint)) {
      activeCallbacks.get(fingerprint).push(sendResponse);
      return;
    }
    
    activeCallbacks.set(fingerprint, [sendResponse]);
    
    const requestItem = {
      requestId: payload.requestId || `req-${Date.now()}-${Math.random()}`,
      method,
      params,
      chainId,
      origin,
      fingerprint,
      timestamp: Date.now()
    };
    
    if (!memoryQueues[queueKey]) {
      memoryQueues[queueKey] = [];
    }
    memoryQueues[queueKey].push(requestItem);
    
    await syncQueueStorage();
    await processQueue(queueKey);
  })();
  
  return true;
});

// Initialize queues from session storage on startup
(async () => {
  try {
    const data = await chrome.storage.session.get("pending_requests");
    const stored = data.pending_requests || {};
    for (const [key, items] of Object.entries(stored)) {
      if (items && items.length > 0) {
        memoryQueues[key] = items;
        processQueue(key);
      }
    }
  } catch (e) {
    console.error("Failed to restore queues from session storage", e);
  }
})();
