// ── Story persistence tracking keys (E3) ─────────────────────────────────────
// Hash: firstSeen, lastSeen, mentionCount, currentScore,
//       title, link, severity, lang, description, publishedAt,
//       entityCorroborationCount, isOpinion, isFeelGood,
//       isEphemeralLiveCoverage, category
// sourceCount is not a hash field for current rows: distinct feed names live in
// story:sources:v1 and should be counted from the Set. peakScore is held in
// story:peak:v1's ZSet; the hash-side peakScore reader remains a reserved
// placeholder for future score-history support.
// description is authoritative per-mention: written unconditionally on every
// HSET (empty string when the current mention has no body), so an earlier
// mention's body never silently grounds LLMs for the current mention.
export const STORY_TRACK_KEY_PREFIX = 'story:track:v1:';
// Set: unique feed names that have mentioned this story
export const STORY_SOURCES_KEY_PREFIX = 'story:sources:v1:';
// Sorted set: single member "peak" with score = highest importanceScore seen
export const STORY_PEAK_KEY_PREFIX = 'story:peak:v1:';
// Sorted set: accumulator for digest mode notifications (score = pubDate epoch ms)
export const DIGEST_ACCUMULATOR_KEY_PREFIX = 'digest:accumulator:v1:';

/**
 * Story tracking keys — written by list-feed-digest.ts, read by digest cron (E2).
 * All keys use 32-char SHA-256 hex prefix of the normalised title as ${titleHash}.
 *
 *   story:track:v1:${titleHash}     Hash   firstSeen/lastSeen/title/link/severity/mentionCount/currentScore/lang/description/publishedAt/entityCorroborationCount/isOpinion/isFeelGood/isEphemeralLiveCoverage/category (always-written)
 *   story:sources:v1:${titleHash}   Set    feed IDs (SADD per appearance)
 *   story:peak:v1:${titleHash}      ZSet   single member "peak", score = highest importanceScore (ZADD GT)
 *   digest:accumulator:v1:${variant}:${lang} ZSet  member=titleHash, score=lastSeen_ms (updated every appearance)
 *
 * TTLs are split, not uniform:
 *   - story:track:v1 + story:sources:v1 + story:peak:v1 use STORY_TTL (7d) — sustained multi-day stories
 *   - digest:accumulator:v1 uses DIGEST_ACCUMULATOR_TTL (48h) — lookback window for digest content
 * A previous comment here claimed "TTL for all: 48h" alongside a dead
 * `STORY_TRACKING_TTL_S = 172800` export; both were leftovers from before
 * the split and were removed to prevent future readers from misreading
 * the rollout window for category/isFeelGood/isOpinion residue.
 * Shadow scoring key (written by notification-relay.cjs, which owns the live
 * value — the constant here is documentation only, not imported):
 *   shadow:score-log:v5            ZSet   score=epoch_ms, member=JSON{ts,importanceScore,severity,eventType,title,source,publishedAt,corroborationCount,variant}
 *   shadow:score-log:v3            ZSet   legacy (weight rebalance) — self-prunes via 7d ZREMRANGEBYSCORE
 *   shadow:score-log:v2            ZSet   legacy (stale-score fix) — self-prunes
 *   shadow:score-log:v1            ZSet   legacy (pre-PR #3069) — self-prunes
 */
export const STORY_TRACK_KEY = (titleHash: string) => `story:track:v1:${titleHash}`;
export const STORY_SOURCES_KEY = (titleHash: string) => `story:sources:v1:${titleHash}`;
export const STORY_PEAK_KEY = (titleHash: string) => `story:peak:v1:${titleHash}`;
export const DIGEST_ACCUMULATOR_KEY = (variant: string, lang = 'en') => `digest:accumulator:v1:${variant}:${lang}`;
export const DIGEST_LAST_SENT_KEY = (userId: string, variant: string) => `digest:last-sent:v1:${userId}:${variant}`;
// NOTE: notification-relay.cjs owns the live value (shadow:score-log:v5 since prompt upgrade).
// This export is documentation/discoverability; changing it here does NOT affect the relay.
// If you modify the key, also update scripts/notification-relay.cjs SHADOW_SCORE_LOG_KEY.
export const SHADOW_SCORE_LOG_KEY = 'shadow:score-log:v5';
export const STORY_TTL = 604800;           // 7 days — enough for sustained multi-day stories
export const DIGEST_ACCUMULATOR_TTL = 172800; // 48h — lookback window for digest content

/**
 * Shared Redis pointer keys for simulation artifacts.
 * Defined here so TypeScript handlers and seed scripts agree on the exact string.
 * The MJS seed script keeps its own copy (cannot import TS source directly).
 */
export const SIMULATION_OUTCOME_LATEST_KEY = 'forecast:simulation-outcome:latest';
export const SIMULATION_PACKAGE_LATEST_KEY = 'forecast:simulation-package:latest';
export const REGULATORY_ACTIONS_KEY = 'regulatory:actions:v1';

/**
 * CII risk-score payload key family. Keep runtime-local mirrors in
 * api/_cii-risk-cache-keys.js and scripts/_cii-risk-cache-keys.mjs aligned.
 */
export const CII_RISK_SCORE_CACHE_KEYS = {
  live: 'risk:scores:sebuf:v8',
  stale: 'risk:scores:sebuf:stale:v8',
  trendHistoryPrefix: 'risk:scores:sebuf:trend-history:v8',
} as const;
export const CLIMATE_ANOMALIES_KEY = 'climate:anomalies:v2';
export const CLIMATE_AIR_QUALITY_KEY = 'climate:air-quality:v1';
export const CLIMATE_ZONE_NORMALS_KEY = 'climate:zone-normals:v1';
export const CLIMATE_CO2_MONITORING_KEY = 'climate:co2-monitoring:v1';
export const CLIMATE_OCEAN_ICE_KEY = 'climate:ocean-ice:v1';
export const CLIMATE_NEWS_KEY = 'climate:news-intelligence:v1';
export const HEALTH_AIR_QUALITY_KEY = 'health:air-quality:v1';

export const ENERGY_MIX_KEY_PREFIX = 'energy:mix:v1:';
export const ENERGY_EXPOSURE_INDEX_KEY = 'energy:exposure:v1:index';
export const GAS_STORAGE_KEY_PREFIX = 'energy:gas-storage:v1:';
export const GAS_STORAGE_COUNTRIES_KEY = 'energy:gas-storage:v1:_countries';
export const ELECTRICITY_KEY_PREFIX = 'energy:electricity:v1:';
export const ELECTRICITY_INDEX_KEY = 'energy:electricity:v1:index';
export const ENERGY_INTELLIGENCE_KEY = 'energy:intelligence:feed:v1';
export const CHOKEPOINT_FLOWS_KEY = 'energy:chokepoint-flows:v1';
export const ENERGY_SPINE_KEY_PREFIX = 'energy:spine:v1:';
export const ENERGY_SPINE_COUNTRIES_KEY = 'energy:spine:v1:_countries';
export const EMBER_ELECTRICITY_KEY_PREFIX = 'energy:ember:v1:';
export const EMBER_ELECTRICITY_ALL_KEY = 'energy:ember:v1:_all';
export const SPR_KEY = 'economic:spr:v1';
export const SPR_POLICIES_KEY = 'energy:spr-policies:v1';
export const PIPELINES_GAS_KEY = 'energy:pipelines:gas:v1';
export const PIPELINES_OIL_KEY = 'energy:pipelines:oil:v1';
export const STORAGE_FACILITIES_KEY = 'energy:storage-facilities:v1';
export const FUEL_SHORTAGES_KEY = 'energy:fuel-shortages:v1';
export const ENERGY_DISRUPTIONS_KEY = 'energy:disruptions:v1';
export const REFINERY_INPUTS_KEY = 'economic:refinery-inputs:v1';

/**
 * Per-country chokepoint exposure index. Request-varying — excluded from bootstrap.
 * Key: supply-chain:exposure:{iso2}:{hs2}:v1
 */
export const CHOKEPOINT_EXPOSURE_KEY = (iso2: string, hs2: string) =>
  `supply-chain:exposure:${iso2}:${hs2}:v1`;
export const CHOKEPOINT_EXPOSURE_SEED_META_KEY = 'seed-meta:supply_chain:chokepoint-exposure';

/**
 * Per-country + per-chokepoint cost shock cache.
 * NOT in bootstrap — request-varying, PRO-gated.
 */
export const COST_SHOCK_KEY = (iso2: string, chokepointId: string) =>
  `supply-chain:cost-shock:${iso2}:${chokepointId}:v1` as const;

/**
 * Per-country + per-HS2 sector dependency cache.
 * NOT in bootstrap — request-varying, PRO-gated.
 */
export const SECTOR_DEPENDENCY_KEY = (iso2: string, hs2: string) =>
  `supply-chain:sector-dep:${iso2}:${hs2}:v1` as const;

/**
 * Route Explorer lane cache — per (fromIso2, toIso2, hs2, cargoType).
 * NOT in bootstrap — request-varying, PRO-gated.
 */
export const ROUTE_EXPLORER_LANE_KEY = (
  fromIso2: string,
  toIso2: string,
  hs2: string,
  cargoType: string,
) => `supply-chain:route-explorer-lane:${fromIso2}:${toIso2}:${hs2}:${cargoType}:v1` as const;

/**
 * Route impact cache — per (fromIso2, toIso2, hs2).
 * NOT in bootstrap — request-varying, PRO-gated. 24h Redis TTL.
 */
export const ROUTE_IMPACT_KEY = (fromIso2: string, toIso2: string, hs2: string) =>
  `supply-chain:route-impact:${fromIso2}:${toIso2}:${hs2}:v1` as const;

/**
 * Shared chokepoint status cache key — written by get-chokepoint-status, read by bypass-options and cost-shock handlers.
 */
export const CHOKEPOINT_STATUS_KEY = 'supply_chain:chokepoints:v4' as const;

/**
 * Static cache keys for the bootstrap endpoint.
 * Only keys with NO request-varying suffixes are included.
 */
export const BOOTSTRAP_CACHE_KEYS: Record<string, string> = {
  earthquakes:      'seismology:earthquakes:v1',
  outages:          'infra:outages:v1',
  serviceStatuses:  'infra:service-statuses:v1',
  ddosAttacks:      'cf:radar:ddos:v1',
  trafficAnomalies: 'cf:radar:traffic-anomalies:v1',
  sectors:          'market:sectors:v2',
  etfFlows:         'market:etf-flows:v1',
  macroSignals:     'economic:macro-signals:v1',
  bisPolicy:        'economic:bis:policy:v1',
  bisExchange:      'economic:bis:eer:v1',
  bisCredit:        'economic:bis:credit:v1',
  bisDsr:           'economic:bis:dsr:v1',
  bisPropertyResidential: 'economic:bis:property-residential:v1',
  bisPropertyCommercial:  'economic:bis:property-commercial:v1',
  imfMacro:         'economic:imf:macro:v2',
  imfGrowth:        'economic:imf:growth:v1',
  imfLabor:         'economic:imf:labor:v1',
  imfExternal:      'economic:imf:external:v1',
  shippingRates:    'supply_chain:shipping:v2',
  chokepoints:      'supply_chain:chokepoints:v4',
  minerals:         'supply_chain:minerals:v2',
  giving:           'giving:summary:v1',
  climateAnomalies: 'climate:anomalies:v2',
  climateDisasters: 'climate:disasters:v1',
  co2Monitoring:    'climate:co2-monitoring:v1',
  oceanIce:         'climate:ocean-ice:v1',
  climateNews:      'climate:news-intelligence:v1',
  radiationWatch:  'radiation:observations:v1',
  thermalEscalation: 'thermal:escalation:v1',
  crossSourceSignals: 'intelligence:cross-source-signals:v1',
  wildfires:        'wildfire:fires:v1',
  marketQuotes:     'market:stocks-bootstrap:v1',
  commodityQuotes:  'market:commodities-bootstrap:v1',
  cyberThreats:     'cyber:threats-bootstrap:v2',
  techReadiness:    'economic:worldbank-techreadiness:v1',
  progressData:     'economic:worldbank-progress:v1',
  renewableEnergy:  'economic:worldbank-renewable:v1',
  positiveGeoEvents: 'positive_events:geo-bootstrap:v1',
  theaterPosture:   'theater_posture:sebuf:stale:v1',
  riskScores:       CII_RISK_SCORE_CACHE_KEYS.stale,
  naturalEvents:    'natural:events:v1',
  flightDelays:     'aviation:delays-bootstrap:v2',
  insights:         'news:insights:v1',
  predictions:      'prediction:markets-bootstrap:v1',
  cryptoQuotes:     'market:crypto:v1',
  gulfQuotes:       'market:gulf-quotes:v1',
  stablecoinMarkets: 'market:stablecoins:v1',
  unrestEvents:     'unrest:events:v1',
  iranEvents:       'conflict:iran-events:v1',
  ucdpEvents:       'conflict:ucdp-events:v1',
  temporalAnomalies: 'temporal:anomalies:v1',
  weatherAlerts:    'weather:alerts:v1',
  spending:         'economic:spending:v1',
  techEvents:       'research:tech-events-bootstrap:v1',
  gdeltIntel:       'intelligence:gdelt-intel:v1',
  correlationCards: 'correlation:cards-bootstrap:v1',
  securityAdvisories: 'intelligence:advisories-bootstrap:v1',
  forecasts:          'forecast:predictions:v2',
  customsRevenue:     'trade:customs-revenue:v1',
  sanctionsPressure: 'sanctions:pressure:v1',
  groceryBasket:     'economic:grocery-basket:v1',
  bigmac:            'economic:bigmac:v1',
  fuelPrices:        'economic:fuel-prices:v1',
  cryptoSectors:    'market:crypto-sectors:v1',
  defiTokens:       'market:defi-tokens:v1',
  aiTokens:         'market:ai-tokens:v1',
  otherTokens:      'market:other-tokens:v1',
  nationalDebt:     'economic:national-debt:v1',
  marketImplications: 'intelligence:market-implications:v1',
  fearGreedIndex:   'market:fear-greed:v1',
  hyperliquidFlow:  'market:hyperliquid:flow:v1',
  crudeInventories: 'economic:crude-inventories:v1',
  natGasStorage:    'economic:nat-gas-storage:v1',
  ecbFxRates:       'economic:ecb-fx-rates:v1',
  euGasStorage:     'economic:eu-gas-storage:v1',
  eurostatCountryData: 'economic:eurostat-country-data:v1',
  eurostatHousePrices: 'economic:eurostat:house-prices:v1',
  eurostatGovDebtQ:    'economic:eurostat:gov-debt-q:v1',
  eurostatIndProd:     'economic:eurostat:industrial-production:v1',
  euFsi:            'economic:fsi-eu:v1',
  shippingStress:   'supply_chain:shipping_stress:v1',
  socialVelocity:   'intelligence:social:reddit:v1',
  wsbTickers:       'intelligence:wsb-tickers:v1',
  pizzint:          'intelligence:pizzint:seed:v1',
  diseaseOutbreaks: 'health:disease-outbreaks:v1',
  economicStress:   'economic:stress-index:v1',
  faoFoodPriceIndex: 'economic:fao-ffpi:v1',
  electricityPrices:   'energy:electricity:v1:index',
  jodiOil:             'energy:jodi-oil:v1:_countries',
  chokepointBaselines: 'energy:chokepoint-baselines:v1',
  portwatchChokepointsRef: 'portwatch:chokepoints:ref:v1',
  portwatchPortActivity: 'supply_chain:portwatch-ports:v1:_countries',
  oilStocksAnalysis:    'energy:oil-stocks-analysis:v1',
  lngVulnerability:     'energy:lng-vulnerability:v1',
  sprPolicies:          'energy:spr-policies:v1',
  pipelinesGas:         'energy:pipelines:gas:v1',
  pipelinesOil:         'energy:pipelines:oil:v1',
  storageFacilities:    'energy:storage-facilities:v1',
  fuelShortages:        'energy:fuel-shortages:v1',
  energyDisruptions:    'energy:disruptions:v1',
  energyCrisisPolicies: 'energy:crisis-policies:v1',
  aaiiSentiment:        'market:aaii-sentiment:v1',
  breadthHistory:       'market:breadth-history:v1',
  consumerPricesOverview:   'consumer-prices:overview:ae',
  consumerPricesCategories: 'consumer-prices:categories:ae:30d',
  consumerPricesMovers:     'consumer-prices:movers:ae:30d',
  consumerPricesSpread:     'consumer-prices:retailer-spread:ae:essentials-ae',
};

export const PORTWATCH_PORT_ACTIVITY_KEY_PREFIX = 'supply_chain:portwatch-ports:v1:';
export const PORTWATCH_PORT_ACTIVITY_COUNTRIES_KEY = 'supply_chain:portwatch-ports:v1:_countries';

export const BOOTSTRAP_TIERS: Record<string, 'slow' | 'fast'> = {
  bisPolicy: 'slow', bisExchange: 'slow', bisCredit: 'slow',
  bisDsr: 'slow', bisPropertyResidential: 'slow', bisPropertyCommercial: 'slow',
  imfMacro: 'slow', imfGrowth: 'slow', imfLabor: 'slow', imfExternal: 'slow',
  minerals: 'slow', giving: 'slow', sectors: 'slow',
  progressData: 'slow', renewableEnergy: 'slow',
  etfFlows: 'slow', shippingRates: 'fast', wildfires: 'slow',
  climateAnomalies: 'slow', climateDisasters: 'slow', co2Monitoring: 'slow', oceanIce: 'slow', climateNews: 'slow', sanctionsPressure: 'slow', radiationWatch: 'slow', thermalEscalation: 'slow', crossSourceSignals: 'slow', cyberThreats: 'slow', techReadiness: 'slow',
  theaterPosture: 'fast', naturalEvents: 'slow',
  cryptoQuotes: 'slow', gulfQuotes: 'slow', stablecoinMarkets: 'slow',
  unrestEvents: 'slow', ucdpEvents: 'slow', techEvents: 'slow',
  earthquakes: 'fast', outages: 'fast', serviceStatuses: 'fast', ddosAttacks: 'fast', trafficAnomalies: 'fast',
  macroSignals: 'fast', chokepoints: 'fast', riskScores: 'fast',
  marketQuotes: 'fast', commodityQuotes: 'fast', positiveGeoEvents: 'fast',
  flightDelays: 'fast', insights: 'fast', predictions: 'fast',
  iranEvents: 'fast', temporalAnomalies: 'fast', weatherAlerts: 'fast',
  spending: 'fast', gdeltIntel: 'fast', correlationCards: 'fast',
  securityAdvisories: 'slow',
  forecasts: 'fast',
  customsRevenue: 'slow',
  consumerPricesOverview: 'slow', consumerPricesCategories: 'slow',
  consumerPricesMovers: 'slow', consumerPricesSpread: 'slow',
  groceryBasket: 'slow',
  bigmac: 'slow',
  fuelPrices: 'slow',
  cryptoSectors: 'slow',
  defiTokens: 'slow',
  aiTokens: 'slow',
  otherTokens: 'slow',
  nationalDebt: 'slow',
  marketImplications: 'slow',
  fearGreedIndex: 'slow',
  hyperliquidFlow: 'slow',
  crudeInventories: 'slow',
  natGasStorage: 'slow',
  ecbFxRates: 'slow',
  euGasStorage: 'slow',
  eurostatCountryData: 'slow',
  eurostatHousePrices: 'slow',
  eurostatGovDebtQ: 'slow',
  eurostatIndProd: 'slow',
  euFsi: 'slow',
  shippingStress: 'fast',
  socialVelocity: 'fast',
  wsbTickers: 'fast',
  pizzint: 'slow',
  diseaseOutbreaks: 'slow',
  economicStress: 'slow',
  faoFoodPriceIndex: 'slow',
  electricityPrices:   'slow',
  jodiOil:             'slow',
  chokepointBaselines: 'slow',
  portwatchChokepointsRef: 'slow',
  portwatchPortActivity: 'slow',
  oilStocksAnalysis: 'slow',
  lngVulnerability: 'slow',
  sprPolicies: 'slow',
  pipelinesGas: 'slow',
  pipelinesOil: 'slow',
  storageFacilities: 'slow',
  fuelShortages: 'slow',
  energyDisruptions: 'slow',
  energyCrisisPolicies: 'slow',
  aaiiSentiment: 'slow',
  breadthHistory: 'slow',
};

export const PORTWATCH_CHOKEPOINTS_REF_KEY = 'portwatch:chokepoints:ref:v1';
