import type { AggUiLabels, VenueMarket, VenueMarketOutcome } from "@agg-build/hooks";
import type { EventListItemEvent, EventListItemVenueInfoMap, VenueEventWithMarkets } from "./event-list-item.types";
export declare const resolveEventListItemEvent: (fetchedEvent: VenueEventWithMarkets | undefined) => EventListItemEvent | undefined;
export declare const normalizeProbability: (value: number | null | undefined) => number | undefined;
export declare const isYesLabel: (label: string) => boolean;
export declare const resolveYesOutcome: (market: Pick<VenueMarket, "venueMarketOutcomes">) => VenueMarketOutcome | undefined;
export declare const resolveYesOutcomeProbability: (market: Pick<VenueMarket, "venueMarketOutcomes">) => number | undefined;
export declare const formatMarketProbabilityPercent: (value: number | null | undefined, formatPercent?: (value: number) => string) => string;
export declare const formatPriceGapPercent: (value: number | null | undefined) => string | undefined;
export declare const sortOutcomes: (outcomes: VenueMarketOutcome[]) => VenueMarketOutcome[];
export type EventListItemVisibleOutcome = {
    venue: VenueMarket["venue"];
    market: VenueMarket;
    outcome: VenueMarketOutcome;
    title: string;
};
export declare const resolveVisibleOutcomes: (venueMarkets: VenueMarket[]) => EventListItemVisibleOutcome[];
/**
 * Dedupe venue markets while collapsing matched-cluster siblings to a single
 * row. Walks left-to-right; for each market we see, mark its own id AND every
 * `matchedVenueMarkets[].id` as "seen", and skip any later row whose own id
 * already appears in the seen set.
 *
 * Why cluster-aware (not just id): the home card preview and the load-more
 * (`/venue-markets`) endpoint can pick different cluster representatives for
 * the same matched group (one keeps the highest-volume venue, the other keeps
 * the canonical cluster target). When the EventItem component merges the two
 * lists for cross-page rendering, a naive id-only dedup leaves both rows in
 * place and the user sees the same logical market (e.g. "Bayern Munich")
 * twice — once per representative. Tracking sibling ids closes that gap
 * without needing the backend to expose `matchEntry.targetVenueMarketId` on
 * every list shape.
 *
 * Order semantics: first occurrence wins per cluster. Callers should pass
 * the more authoritative list first (e.g. `[...allVenueMarkets, ...lazyLoaded]`
 * if the home-card row is preferred, swap if the lazy-loaded row is preferred).
 */
export declare const dedupeVenueMarketsById: (venueMarkets: VenueMarket[]) => VenueMarket[];
/** Returns the outcome's display text. Prefers `title` (the descriptive
 *  human-readable text — e.g. "Trump Wins") when present and non-empty,
 *  falls back to `label` (the canonical short identifier — e.g. "Yes").
 *  For semantic checks (isYesLabel, sort keys, lookup keys) keep using
 *  `outcome.label` directly — never this helper. */
export declare const resolveOutcomeTitle: (outcome: VenueMarketOutcome) => string;
export declare const selectPrimaryVenueMarket: (venueMarkets: VenueMarket[]) => VenueMarket | undefined;
export declare const resolveTileTitle: (event: VenueEventWithMarkets, primaryVenueMarket: VenueMarket | undefined, titleOverride: string | undefined) => string;
export declare const resolveTileImage: (event: VenueEventWithMarkets, primaryVenueMarket: VenueMarket | undefined, imageOverride: string | null | undefined) => string | undefined;
export declare const formatCountLabel: (count: number, singular: string, plural: string) => string;
export declare const getVenueSummary: (venueMarkets: VenueMarket[]) => {
    marketCount: number;
    venueCount: number;
    singleVenue: import("@agg-build/hooks").Venue | undefined;
};
export declare const resolveVenueLabel: (venue: VenueMarket["venue"] | undefined, venueInfo: EventListItemVenueInfoMap | undefined, labels: AggUiLabels) => string;
type DisplayVolumeMarket = {
    id: string;
    venue: string;
    volume?: number | null;
    matchedVenueMarkets?: readonly DisplayVolumeMarket[] | null;
    matchEntry?: {
        targetVenueMarketId?: string | null;
        targetVenueMarket?: DisplayVolumeMarket | null;
    } | null | undefined;
};
export type BestMidpointResult = {
    /** Raw midpoint value (0–1 range, not yet clamped). */
    midpoint: number;
    venueMarketId: string;
    venue: string;
};
/**
 * Builds a venue-market-id → spread lookup from the raw midpoint rows returned
 * by `useVenueMarketMidpoints`. Matched sibling rows are included in the map;
 * first-write wins to match the same priority as `midpointsByVenueMarketId`.
 */
export declare function buildSpreadByVenueMarketId(midpointRows: ReadonlyArray<{
    venueMarketId: string;
    spread: number | null;
    matched: ReadonlyArray<{
        venueMarketId: string;
        spread: number | null;
    }>;
}>): Map<string, number | null>;
/**
 * Returns the best available midpoint across a market and its matched venue
 * markets. Candidates are collected in declaration order (primary first, then
 * matched in array order) and deduplicated by ID. Among valid (non-null)
 * midpoints, the candidate with the **lowest** YES midpoint wins — this gives
 * the buyer the best available price across venues.
 *
 * The `spreadByVenueMarketId` parameter is accepted for call-site compatibility
 * but is no longer used in the selection logic.
 */
export declare function resolveBestMidpointForMarket(market: {
    id: string;
    venue: string;
    matchedVenueMarkets?: ReadonlyArray<{
        id: string;
        venue: string;
    }> | null;
}, midpointsByVenueMarketId: Map<string, number | null>, spreadByVenueMarketId?: Map<string, number | null>): BestMidpointResult | undefined;
export declare const resolveDisplayVolume: (eventVolume: number | null | undefined, venueMarkets: ReadonlyArray<DisplayVolumeMarket>) => number | undefined;
export {};
