/**
 * Brand-keyword guardrail.
 *
 * Detects affiliates routing paid search traffic through brand-term bids on
 * Google/Bing/Yahoo/DuckDuckGo. If the referrer URL is a search engine and the
 * query contains a brand keyword, the click is flagged SEARCH_ENGINE_BRAND_BID.
 *
 * Rule: setCookie=false when isSuspicious=true — do not attribute.
 */
type GuardResult = {
    isSuspicious: boolean;
    codes: string[];
    /** Whether the referral cookie should be set for this request. */
    setCookie: boolean;
};
type BrandKeywordSettings = {
    brandKeywordBlocklist: string[];
    disallowedRefererHosts: string[];
};
/**
 * Evaluate brand-keyword bidding.
 *
 * @param refererUrl - The raw Referer header value (or empty string).
 * @param settings   - Affiliate security settings with brandKeywordBlocklist.
 */
declare function evaluateBrandKeyword(refererUrl: string, settings: BrandKeywordSettings): GuardResult;

/**
 * Geo guardrail.
 *
 * The host marketplace serves a configured set of countries; traffic from outside
 * the allow-list is flagged GEO_OUT_OF_RANGE and does not receive a referral cookie.
 *
 * The `allowedCountries` override allows future expansion; defaults to ['IL'].
 */

/**
 * Evaluate geo-mismatch.
 *
 * @param country        - ISO 3166-1 alpha-2 country code from CF-IPCountry header.
 * @param allowedCountries - Override allowed list; defaults to ['IL'] when empty.
 */
declare function evaluateGeo(country: string, allowedCountries: string[]): GuardResult;

/**
 * Cookie-stuffing guardrail.
 *
 * Cookie stuffing is when an affiliate injects a referral cookie without the
 * user actually navigating to the site (e.g. via an invisible iframe, XHR, or
 * programmatic fetch). The Sec-Fetch-Mode and Sec-Fetch-Dest headers reliably
 * distinguish top-level navigation from programmatic requests.
 *
 * A legitimate referral arrives as a top-level document navigation:
 *   Sec-Fetch-Mode: navigate
 *   Sec-Fetch-Dest: document
 *
 * Anything else (cors, no-cors, same-origin with Sec-Fetch-Dest: empty) is
 * suspicious.
 *
 * Note: when Sec-Fetch-Mode is absent (old browsers, curl, server-side proxies),
 * we allow the request — false positives in ambiguous cases are costlier than
 * missed detections.
 */

type SecFetchHeaders = {
    secFetchMode: string | null;
    secFetchDest: string | null;
    secFetchSite: string | null;
};
/**
 * Evaluate cookie-stuffing signals.
 *
 * @param headers - Sec-Fetch-* header values (null = header absent).
 */
declare function evaluateCookieStuffing(headers: SecFetchHeaders): GuardResult;

/**
 * Referer evaluation guardrail.
 *
 * Two signals:
 *   1. DISALLOWED_REFERER — referer host is in the settings blocklist.
 *   2. NO_REFERER         — no referer header at all (may indicate direct link
 *                           injection or cloaked traffic). Suspicious but NOT
 *                           a hard block — still sets cookie.
 *
 * Hard block (setCookie=false) only fires for DISALLOWED_REFERER.
 */

/**
 * Evaluate referer header.
 *
 * @param refererUrl          - Raw Referer header value (empty string if absent).
 * @param disallowedHosts     - Blocklisted referer hostnames (e.g. ['competitor.com']).
 */
declare function evaluateReferer(refererUrl: string, disallowedHosts: string[]): GuardResult;

/**
 * Velocity guardrail.
 *
 * Detects rapid repeated hits from the same IP hash within a short window.
 * A single IP submitting >N touch requests in T minutes is a velocity burst,
 * consistent with bot traffic or affiliate link spam.
 *
 * Implementation uses an in-memory Map keyed by ipHash (rotated daily via the
 * HMAC IP hash). In production on CF Workers, each isolate has its own Map;
 * this provides per-isolate rate limiting which is sufficient for burst
 * detection (a bot will consistently hit the same isolate within a request
 * burst). For cross-isolate rate limiting, the touch endpoint can be extended
 * to use Durable Objects — deferred to a future task.
 *
 * Thresholds (defaults):
 *   - MAX_HITS_PER_WINDOW: 10 touches per IP per window
 *   - WINDOW_MS: 60_000 (1 minute)
 */

/**
 * Evaluate velocity burst.
 *
 * @param ipHash  - HMAC-derived daily-keyed IP hash (16-char hex).
 * @param nowMs   - Current timestamp in ms (injectable for testing).
 */
declare function evaluateVelocity(ipHash: string, nowMs?: number): GuardResult;
/** Reset for testing only. */
declare function _resetVelocityMap(): void;

/**
 * IP fingerprint guardrail + HMAC IP hashing.
 *
 * Replaces the predictable SHA-256 hash in touch.ts (Wave 2 security debt).
 * Uses HMAC-SHA-256 keyed by PII_KEY (the existing pgcrypto secret used for
 * phone/email encryption) so the hash cannot be reverse-engineered without
 * the key, even if the attacker knows the date and IP.
 *
 * The HMAC input mixes the date-string so a hash leaked from one day cannot
 * be replayed against future traffic.
 *
 * Output shape: 8-byte (16 hex char) prefix — identical to the previous
 * SHA-256 output, so no schema or storage changes are required.
 *
 * Security property improvement over plain SHA-256:
 *   - SHA-256(date:ip) → anyone with the date+ip can reproduce the hash.
 *   - HMAC-SHA-256(PII_KEY, date:ip) → requires the secret key; attacker
 *     knowing date+ip still cannot reproduce the hash without PII_KEY.
 */

/**
 * Compute a daily-keyed HMAC of the visitor's IP address.
 *
 * PII_KEY is the existing pgcrypto encryption secret — reused here so we
 * don't need a new CF secret binding.
 *
 * @param ip        - Visitor IP address (from cf-connecting-ip header).
 * @param dateStr   - ISO date string "YYYY-MM-DD" (UTC today).
 * @param secretKey - Host-injected PII_KEY (HMAC-SHA-256 secret).
 * @returns 16-char lowercase hex string (8-byte HMAC prefix).
 */
declare function hashIp(ip: string, dateStr: string, secretKey: string): Promise<string>;
/**
 * Evaluate IP fingerprint.
 *
 * Currently a pass-through that returns the HMAC hash for use by callers.
 * Future: detect shared-IP abuse (many distinct link IDs from same IP hash).
 *
 * @param ipHash  - Already-computed HMAC IP hash.
 */
declare function evaluateFingerprint(ipHash: string): GuardResult;

/**
 * Coupon-poaching guardrail.
 *
 * Detects affiliates driving traffic via coupon-aggregator sites (e.g.
 * couponmama.co.il, savii.co.il, zap.co.il/coupons). This traffic arrives
 * with a known set of coupon-site referer hostnames. These sites typically
 * list deals without an affiliate agreement, which inflates attributed
 * commissions unfairly.
 *
 * The coupon-site blocklist is checked against the referer host.
 * Hits get flagged COUPON_POACHING and setCookie=false — no attribution.
 */

/**
 * Evaluate coupon-poaching.
 *
 * @param refererUrl      - Raw Referer header value.
 * @param couponDomains   - Additional coupon domains to block (merged with defaults).
 */
declare function evaluateCouponPoaching(refererUrl: string, couponDomains?: string[]): GuardResult;

/**
 * Cloaking guardrail.
 *
 * Cloaking is when an affiliate redirects real users through an intermediate
 * page to hide the original traffic source from the merchant. Signals:
 *
 *   1. USER_AGENT_MISMATCH — User-Agent is a known bot/scraper UA but the
 *      request arrives as a navigating browser (Sec-Fetch-Mode: navigate).
 *      Consistent with affiliates running headless browsers to simulate clicks.
 *
 *   2. REFERER_REDIRECT_CHAIN — Referer is the affiliate's own domain but the
 *      cf-connecting-ip country doesn't match any IL origin.
 *      (Deferred — requires cross-signal correlation; stub returns false.)
 *
 * setCookie=false for bot-UA cloaking. NO_REFERER is suspicious but still
 * sets cookie (handled by referer-eval).
 */

/**
 * Evaluate cloaking signals.
 *
 * @param userAgent     - User-Agent header value.
 * @param secFetchMode  - Sec-Fetch-Mode header value (null if absent).
 */
declare function evaluateCloaking(userAgent: string, secFetchMode: string | null): GuardResult;

/**
 * Referral security guardrails — barrel export + pipeline runner.
 *
 * Middleware order (per spec §G):
 *   1. cookie-stuffing  — reject programmatic injections first (cheapest check)
 *   2. velocity         — reject bursts before any DB/network work
 *   3. referer-eval     — flag/reject disallowed origins
 *   4. brand-keyword    — flag search engine brand bids
 *   5. coupon-poaching  — flag coupon aggregator traffic
 *   6. cloaking         — flag bot UA + headless browser signals
 *   7. geo              — flag non-IL traffic
 *   8. fingerprint      — compute HMAC IP hash (always runs; no reject)
 *
 * Each guardrail returns a GuardResult with:
 *   - isSuspicious: boolean
 *   - codes: string[]   (suspicion codes logged to AE blob7)
 *   - setCookie: boolean (false = do not attribute this click)
 *
 * runSecurityPipeline returns the merged verdict. If ANY guardrail sets
 * setCookie=false, the pipeline result is setCookie=false.
 */

type SecurityPipelineInput = {
    ipHash: string;
    refererUrl: string;
    userAgent: string;
    country: string;
    secFetch: SecFetchHeaders;
    settings: BrandKeywordSettings & {
        couponDomains?: string[];
        allowedCountries?: string[];
    };
};
type SecurityPipelineResult = {
    /** Whether any guardrail fired. */
    isSuspicious: boolean;
    /** All suspicion codes from all guardrails that fired. */
    codes: string[];
    /** False if any guardrail vetoed attribution. */
    setCookie: boolean;
};
/**
 * Run all 8 security guardrails in order and merge results.
 *
 * Short-circuits after cookie-stuffing or velocity if they reject (setCookie=false)
 * to avoid unnecessary work, but still collects all codes.
 */
declare function runSecurityPipeline(input: SecurityPipelineInput): SecurityPipelineResult;

export { type GuardResult, type SecurityPipelineInput, type SecurityPipelineResult, _resetVelocityMap, evaluateBrandKeyword, evaluateCloaking, evaluateCookieStuffing, evaluateCouponPoaching, evaluateFingerprint, evaluateGeo, evaluateReferer, evaluateVelocity, hashIp, runSecurityPipeline };
