import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useEffect, useRef } from 'react'
import axios from 'axios'
import type {
  Quote, GeoNews, GeoRiskScore, PatternMatch, TradeIdea,
  EconomicEvent, HistoricalCandle, BacktestResult
} from '../types'

export const api = axios.create({ baseURL: '/api' })

// ── Market ────────────────────────────────────────────────────────────────────
export const useAllQuotes = () =>
  useQuery<Record<string, Quote[]>>({
    queryKey: ['quotes'],
    queryFn: () => api.get('/market/quotes').then(r => r.data),
    refetchInterval: 60_000,
  })

export type TickerValidation = { valid: boolean; symbol: string; name?: string; price?: number; change_pct?: number; reason?: string }
export const validateTicker = (symbol: string): Promise<TickerValidation> =>
  api.get('/market/validate', { params: { symbol } }).then(r => r.data)

export const useMarketCustomTickers = () =>
  useQuery<{ ticker: string; name: string; added_at: string }[]>({
    queryKey: ['market-custom-tickers'],
    queryFn: () => api.get('/market/custom-tickers').then(r => r.data),
  })

export const useAddMarketTicker = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (ticker: string) => api.post(`/market/custom-tickers/${encodeURIComponent(ticker)}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['market-custom-tickers'] })
      qc.invalidateQueries({ queryKey: ['quotes'] })
    },
  })
}

export const useRemoveMarketTicker = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (ticker: string) => api.delete(`/market/custom-tickers/${encodeURIComponent(ticker)}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['market-custom-tickers'] })
      qc.invalidateQueries({ queryKey: ['quotes'] })
    },
  })
}

export const useHistory = (symbol: string, period = '1y', interval = '1d') =>
  useQuery<HistoricalCandle[]>({
    queryKey: ['history', symbol, period, interval],
    queryFn: () =>
      api.get(`/market/history/${encodeURIComponent(symbol)}`, { params: { period, interval } }).then(r => r.data),
    enabled: !!symbol,
  })

// ── Geo ───────────────────────────────────────────────────────────────────────
export const useGeoNews = () =>
  useQuery<GeoNews[]>({
    queryKey: ['geo-news'],
    queryFn: () => api.get('/geo/news').then(r => r.data),
    refetchInterval: 3_600_000,
  })

export const useGeoRiskScore = () =>
  useQuery<GeoRiskScore>({
    queryKey: ['geo-risk-score'],
    queryFn: () => api.get('/geo/risk-score').then(r => r.data),
    refetchInterval: 3_600_000,
  })

export const usePatternMatches = () =>
  useQuery<PatternMatch[]>({
    queryKey: ['pattern-matches'],
    queryFn: () => api.get('/geo/pattern-matches').then(r => r.data),
  })

export const usePatternRelevance = (days: number) =>
  useQuery({
    queryKey: ['pattern-relevance', days],
    queryFn: () => api.get('/geo/pattern-relevance', { params: { days } }).then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useTradeIdeas = () =>
  useQuery<TradeIdea[]>({
    queryKey: ['trade-ideas'],
    queryFn: () => api.get('/geo/trade-ideas').then(r => r.data),
  })

export const useCalendar = () =>
  useQuery<EconomicEvent[]>({
    queryKey: ['calendar'],
    queryFn: () => api.get('/geo/calendar').then(r => r.data),
  })

// Real Forex Factory–backed calendar (used by CalendarPage.tsx and InstrumentModels)
export interface FFEvent {
  event_date: string; event_time: string | null; currency: string
  impact: 'high' | 'medium' | 'low'; event_name: string
  actual_value: number | null; forecast_value: number | null; previous_value: number | null
}
export const useEcoCalendar = (params: { period?: string; limit?: number; impacts?: string } = {}) =>
  useQuery<{ events: FFEvent[] }>({
    queryKey: ['eco-calendar', params],
    queryFn: () => api.get('/eco/calendar', { params: { period: 'recent', limit: 50, impacts: 'high,medium', ...params } }).then(r => r.data),
    staleTime: 5 * 60_000,
    refetchInterval: 5 * 60_000,
  })

// ── Instruments Watchlist (Dashboard "radar" card) ────────────────────────────
export interface WatchlistItem { ticker: string; name: string; asset_class: string; sort_order: number; added_at: string }
export interface WatchlistQuoteItem extends WatchlistItem { price: number | null; change_pct: number | null }

export const useInstrumentsWatchlist = () =>
  useQuery<WatchlistItem[]>({
    queryKey: ['instruments-watchlist'],
    queryFn: () => api.get('/watchlist/').then(r => r.data),
    staleTime: 60_000,
  })

export const useInstrumentsWatchlistQuotes = () =>
  useQuery<{ items: WatchlistQuoteItem[] }>({
    queryKey: ['instruments-watchlist-quotes'],
    queryFn: () => api.get('/watchlist/quotes').then(r => r.data),
    refetchInterval: 60_000,
    staleTime: 30_000,
  })

export const useAddWatchlistInstrument = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (ticker: string) => api.post(`/watchlist/${encodeURIComponent(ticker)}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['instruments-watchlist'] })
      qc.invalidateQueries({ queryKey: ['instruments-watchlist-quotes'] })
    },
  })
}

export const useWatchlistHistory = (ticker: string, period: string) =>
  useQuery({
    queryKey: ['instruments-watchlist-history', ticker, period],
    queryFn: () => api.get(`/watchlist/history/${encodeURIComponent(ticker)}`, { params: { period } }).then(r => r.data),
    enabled: !!ticker,
    staleTime: 5 * 60_000,
  })

export const useRemoveWatchlistInstrument = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (ticker: string) => api.delete(`/watchlist/${encodeURIComponent(ticker)}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['instruments-watchlist'] })
      qc.invalidateQueries({ queryKey: ['instruments-watchlist-quotes'] })
    },
  })
}

// Which Saxo OPTIONS CHAIN Options Lab should analyze for this underlying (e.g.
// CL=F -> MCL:XCME) — independent of useSetWatchlistSaxoQuoteLink below, often a
// different Saxo instrument.
export const useSetWatchlistSaxoOptionLink = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ ticker, saxoSymbol }: { ticker: string; saxoSymbol: string | null }) =>
      api.put(`/watchlist/${encodeURIComponent(ticker)}/saxo-option-link`, { saxo_symbol: saxoSymbol }).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['instruments-watchlist'] })
      qc.invalidateQueries({ queryKey: ['instruments-watchlist-quotes'] })
      qc.invalidateQueries({ queryKey: ['saxo-iv-watchlist'] })
      qc.invalidateQueries({ queryKey: ['saxo-watchlist'] })
    },
  })
}

// Which Saxo spot/futures symbol prices this instrument in the Cockpit, instead of
// yfinance's unadjusted continuous-futures tickers.
export const useSetWatchlistSaxoQuoteLink = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ ticker, saxoSymbol }: { ticker: string; saxoSymbol: string | null }) =>
      api.put(`/watchlist/${encodeURIComponent(ticker)}/saxo-quote-link`, { saxo_symbol: saxoSymbol }).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['instruments-watchlist'] })
      qc.invalidateQueries({ queryKey: ['instruments-watchlist-quotes'] })
    },
  })
}

// Links an Instrument Analysis instrument (backend/config/instruments.json — a separate
// catalog from instruments_watchlist above) to a Saxo quote symbol, so its chart/indicators/
// wavelets are Saxo-sourced instead of yfinance. InstrumentDashboard.tsx doesn't use React
// Query for its own data (plain axios + useState), so this mutation carries no
// invalidateQueries — the caller re-fetches the instrument list/snapshot itself on success.
export const useSetInstrumentSaxoLink = () =>
  useMutation({
    mutationFn: ({ instrumentId, saxoSymbol }: { instrumentId: string; saxoSymbol: string | null }) =>
      api.put(`/instruments/${encodeURIComponent(instrumentId)}/saxo-link`, { saxo_symbol: saxoSymbol }).then(r => r.data),
  })

// Brings a Cockpit Watchlist ticker into Instrument Analysis without hand-authoring an
// instruments.json entry — its saxo_quote_symbol (already linked in Config -> Instruments
// Watchlist) is copied over automatically. Idempotent: a ticker that already resolves
// (real catalog entry or an earlier quick add) is returned unchanged.
export const useQuickAddInstrument = () =>
  useMutation({
    mutationFn: (ticker: string) =>
      api.post('/instruments/quick-add', { ticker }).then(r => r.data as { id: string; created: boolean }),
  })

// Free-form display name — no longer tied to yfinance's long_name lookup, since an
// instrument can now be entirely Saxo-sourced.
export const useRenameWatchlistInstrument = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ ticker, name }: { ticker: string; name: string }) =>
      api.put(`/watchlist/${encodeURIComponent(ticker)}/name`, { name }).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['instruments-watchlist'] })
      qc.invalidateQueries({ queryKey: ['instruments-watchlist-quotes'] })
    },
  })
}

// Latest cycle report — shares the 'cycle-report-latest' queryKey with RapportIA.tsx
export const useLatestCycleReport = () =>
  useQuery({
    queryKey: ['cycle-report-latest'],
    queryFn: () => api.get('/reports/cycle/latest').then(r => r.data),
    staleTime: 60_000,
  })

// ── Options ───────────────────────────────────────────────────────────────────
export const useIvSurface = (symbol: string) =>
  useQuery({
    queryKey: ['iv-surface', symbol],
    queryFn: () => api.get('/options/iv-surface', { params: { symbol } }).then(r => r.data),
    enabled: !!symbol,
  })

export const usePnlCurve = (params: {
  symbol: string; strike: number; expiry_days: number
  option_type: string; quantity: number; premium_paid: number
}) =>
  useQuery({
    queryKey: ['pnl-curve', params],
    queryFn: () => api.get('/options/pnl-curve', { params }).then(r => r.data),
    enabled: !!params.symbol && !!params.strike && !!params.premium_paid,
  })

// ── Backtest ──────────────────────────────────────────────────────────────────
export const useBacktest = () =>
  useMutation<BacktestResult, Error, Record<string, unknown>>({
    mutationFn: (data) => api.post('/backtest/run', data).then(r => r.data),
  })

// ── AI ────────────────────────────────────────────────────────────────────────
export const useAiStatus = () =>
  useQuery({
    queryKey: ['ai-status'],
    queryFn: () => api.get('/ai/status').then(r => r.data),
    refetchInterval: 30_000,
  })

export const useAiTopIdeas = () =>
  useQuery({
    queryKey: ['ai-top-ideas'],
    queryFn: () => api.get('/ai/top-ideas').then(r => r.data),
    enabled: false,
    retry: false,
  })

export const useAnalyzeSpeech = () =>
  useMutation({
    mutationFn: (data: { text: string; speaker?: string }) =>
      api.post('/ai/analyze-speech', data).then(r => r.data),
  })

export const useEvaluatePattern = () =>
  useMutation({
    mutationFn: (pattern: Record<string, unknown>) =>
      api.post('/ai/evaluate-pattern', { pattern }).then(r => r.data),
  })

export const useSuggestPattern = () =>
  useMutation({
    mutationFn: (context: string) =>
      api.post('/ai/suggest-pattern', { context }).then(r => r.data),
  })

export const useAiTopIdeasRefetch = () =>
  useMutation({
    mutationFn: () => api.get('/ai/top-ideas').then(r => r.data),
  })

// ── Portfolio ─────────────────────────────────────────────────────────────────
export const usePortfolioPositions = (status = 'open') =>
  useQuery({
    queryKey: ['portfolio', status],
    queryFn: () => api.get('/portfolio/positions', { params: { status } }).then(r => r.data),
    refetchInterval: status === 'open' ? 60_000 : false,
  })

export const usePortfolioSummary = () =>
  useQuery({
    queryKey: ['portfolio-summary'],
    queryFn: () => api.get('/portfolio/summary').then(r => r.data),
    refetchInterval: 60_000,
  })

export const usePnlHistory = () =>
  useQuery({
    queryKey: ['pnl-history'],
    queryFn: () => api.get('/portfolio/pnl-history').then(r => r.data),
  })

// P&L-vs-underlying-price payoff diagram for one position (services.portfolio_pricing.
// compute_payoff) — at-expiry (pure intrinsic) + today (current vol held fixed) curves.
export const usePositionPayoff = (posId: string, enabled: boolean) =>
  useQuery({
    queryKey: ['portfolio-payoff', posId],
    queryFn: () => api.get(`/portfolio/positions/${posId}/payoff`).then(r => r.data),
    enabled,
  })

// Reprices every open position under a handful of named macro scenarios (Risk-Off,
// Risk-On, inflation persistante, dollar fort, baisse des matières premières) to surface
// when several differently-named positions are really the same underlying bet.
export const usePortfolioScenarioExposure = () =>
  useQuery({
    queryKey: ['portfolio-scenario-exposure'],
    queryFn: () => api.get('/portfolio/scenario-exposure').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useAddPosition = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (data: Record<string, unknown>) =>
      api.post('/portfolio/add', data).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['portfolio'] })
      qc.invalidateQueries({ queryKey: ['portfolio-summary'] })
    },
  })
}

export const useClosePosition = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, close_value }: { id: string; close_value: number }) =>
      api.post(`/portfolio/close/${id}`, { close_value }).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['portfolio'] })
      qc.invalidateQueries({ queryKey: ['portfolio-summary'] })
      qc.invalidateQueries({ queryKey: ['pnl-history'] })
    },
  })
}

// ── Config ────────────────────────────────────────────────────────────────────
export const useConfig = () =>
  useQuery({
    queryKey: ['config'],
    queryFn: () => api.get('/config/').then(r => r.data),
  })

export const useSources = () =>
  useQuery({
    queryKey: ['sources'],
    queryFn: () => api.get('/config/sources').then(r => r.data),
  })

export const useUpdateSources = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (sources: Record<string, unknown>) =>
      api.put('/config/sources', { sources }).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['sources'] }),
  })
}

export const useUpdateApiKeys = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (keys: Record<string, string>) =>
      api.put('/config/api-keys', keys).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['config'] })
      qc.invalidateQueries({ queryKey: ['ai-status'] })
    },
  })
}

// ── AI Pattern Scoring ────────────────────────────────────────────────────────
export const useScorePatterns = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (params: { top_n?: number; category_filter?: string; template?: string }) =>
      api.post('/ai/score-patterns', params).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['last-scores'] }),
  })
}

export const useLastScores = () =>
  useQuery({
    queryKey: ['last-scores'],
    queryFn: () => api.get('/ai/last-scores').then(r => r.data),
    staleTime: Infinity,
  })

export const useSuggestNewPatterns = () =>
  useMutation({
    mutationFn: () => api.post('/ai/suggest-new-patterns').then(r => r.data),
  })

export const useAnalysisConfig = () =>
  useQuery({
    queryKey: ['analysis-config'],
    queryFn: () => api.get('/config/analysis').then(r => r.data),
  })

export const useSaveAnalysisConfig = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (cfg: { top_n?: number; category_filter?: string; template?: string }) =>
      api.put('/config/analysis', cfg).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['analysis-config'] }),
  })
}

// ── Patterns ──────────────────────────────────────────────────────────────────
export const useAllPatterns = () =>
  useQuery({
    queryKey: ['all-patterns'],
    queryFn: () => api.get('/patterns/all').then(r => r.data),
  })

export const usePatternTaxonomy = () =>
  useQuery({
    queryKey: ['pattern-taxonomy'],
    queryFn: () => api.get('/patterns/taxonomy').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const usePatternsByInstrument = (ticker: string) =>
  useQuery({
    queryKey: ['patterns-by-instrument', ticker],
    queryFn: () => api.get('/patterns/by-instrument', { params: { ticker } }).then(r => r.data),
    enabled: !!ticker,
    staleTime: 2 * 60_000,
  })

export const usePatternSimilarity = () =>
  useQuery({
    queryKey: ['pattern-similarity'],
    queryFn: () => api.get('/ai/pattern-similarity').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useMacroRegime = () => {
  const qc = useQueryClient()
  const query = useQuery({
    queryKey: ['macro-regime'],
    queryFn: () => api.get('/market/macro-regime').then(r => r.data),
    staleTime: 10 * 60 * 1000,
    refetchInterval: 15 * 60 * 1000,
  })
  const forceRefetch = async () => {
    const fresh = await api.get('/market/macro-regime?force=true').then(r => r.data)
    qc.setQueryData(['macro-regime'], fresh)
    return fresh
  }
  return { ...query, forceRefetch }
}

export const useSavePattern = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (pattern: Record<string, unknown>) =>
      api.post('/patterns/custom', pattern).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['all-patterns'] }),
  })
}

export const useDeletePattern = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: string) =>
      api.delete(`/patterns/custom/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['all-patterns'] }),
  })
}

export const usePatchPattern = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      id: string
      name?: string
      description?: string
      category?: string
      signal_direction?: string
      regime_tag?: string
    }) => api.patch(`/patterns/custom/${body.id}`, body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['all-patterns'] }),
  })
}

export const useTogglePattern = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: string) =>
      api.put(`/patterns/toggle/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['all-patterns'] }),
  })
}

export interface PatternCalibration {
  pattern_id: string
  pattern_name: string
  asset_class: string
  ai_estimate: number | null
  observed_avg_win_pct: number | null
  calibrated_expected_move: number | null
  calibration_weight: number
  calibration_weight_pct: number
  n_mature_trades: number
  bayes_win_rate: number
  source: 'pure_ai' | 'early' | 'mixed' | 'data_driven'
}

export const usePatternCalibration = () =>
  useQuery({
    queryKey: ['pattern-calibration'],
    queryFn: () => api.get('/patterns/calibration').then(r => r.data as PatternCalibration[]),
    staleTime: 60_000,
  })

export interface SimilarityResult {
  recommendation: 'merge_as_instance' | 'counter_scenario' | 'new_pattern'
  match_id: string | null
  match_name: string | null
  confidence: number
  reasoning: string
  suggested_regime_tag: string
}

export const useFindSimilarPattern = () =>
  useMutation({
    mutationFn: (pattern_id: string) =>
      api.post('/patterns/find-similar', { pattern_id }).then(r => r.data as SimilarityResult),
  })

export const useMergePatterns = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: { keep_id: string; discard_id: string }) =>
      api.post('/patterns/merge', body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['all-patterns'] })
      qc.invalidateQueries({ queryKey: ['last-scores'] })
    },
  })
}

// ── Auto-Cycle ───────────────────────────────────────────────────────────────

export const useCycleStatus = () =>
  useQuery({
    queryKey: ['cycle-status'],
    queryFn: () => api.get('/cycle/status').then(r => r.data),
    staleTime: 5_000,
    // Poll every 5s while a cycle is running, every 30s otherwise
    refetchInterval: (query) => ((query.state.data as any)?.running ? 5_000 : 30_000),
  })

export interface CycleStepParam { type: 'bool' | 'int' | 'float'; label?: string; default: unknown; min?: number; max?: number }
export interface CycleStepDef { id: string; label: string; description: string; group: string; params: Record<string, CycleStepParam> }

export const useCycleStepCatalog = () =>
  useQuery({
    queryKey: ['cycle-step-catalog'],
    queryFn: () => api.get('/cycle/step-catalog').then(r => r.data.steps as CycleStepDef[]),
    staleTime: Infinity,
  })

export const useCycleHistory = (limit = 20) =>
  useQuery({
    queryKey: ['cycle-history', limit],
    queryFn: () => api.get(`/cycle/history?limit=${limit}`).then(r => r.data),
    staleTime: 60_000,
  })

export const useUpdateCycleConfig = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (cfg: { enabled?: boolean; interval_hours?: number; similarity_threshold?: number; min_ev_threshold?: number; min_score_threshold?: number; trade_budget_eur?: number; preferred_horizon_min?: number; preferred_horizon_max?: number; journal_retention_days?: number; maturity_threshold_pct?: number; weekend_cycle_enabled?: boolean; weekend_cycle_times?: string; cycle_step_config?: Record<string, Record<string, unknown>> }) =>
      api.post('/cycle/config', cfg).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['cycle-status'] }),
  })
}

/** Keys to refresh once a cycle finishes — covers cockpit, macro, journal */
export const CYCLE_REFRESH_KEYS = [
  ['macro-regime'],
  ['last-scores'],
  ['all-patterns'],
  ['cycle-history'],
  ['cycle-status'],
  ['journal-summary'],
  ['journal-mtm'],
  ['journal-geo'],
  ['journal-macro'],
  ['geo-risk-score'],
  ['pattern-matches'],
  ['journal-portfolio-risk'],
]

export const useTriggerCycle = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: () => api.post('/cycle/trigger').then(r => r.data),
    onSuccess: () => {
      // Immediate status refresh so the spinner shows
      qc.invalidateQueries({ queryKey: ['cycle-status'] })
    },
  })
}

/**
 * Mount this hook once at a high level (e.g. CyclesSection or App).
 * It watches cycle-status and, when a running cycle finishes, refreshes
 * all cockpit + journal queries so the UI reflects the new scores/regime.
 */
export const useCycleWatcher = () => {
  const qc = useQueryClient()
  const { data: statusData } = useCycleStatus()
  const wasRunning = useRef(false)

  useEffect(() => {
    const running = (statusData as any)?.running ?? false
    if (running) {
      wasRunning.current = true
    } else if (wasRunning.current) {
      // Transition: was running → now done → refresh everything
      wasRunning.current = false
      CYCLE_REFRESH_KEYS.forEach(key => qc.invalidateQueries({ queryKey: key }))
    }
  }, [(statusData as any)?.running])
}

// ── Journal de Bord ──────────────────────────────────────────────────────────

export const useJournalSummary = () =>
  useQuery({
    queryKey: ['journal-summary'],
    queryFn: () => api.get('/journal/summary').then(r => r.data),
    staleTime: 2 * 60_000,
  })

export const useMacroHistory = (days = 15) =>
  useQuery({
    queryKey: ['journal-macro', days],
    queryFn: () => api.get(`/journal/macro-history?days=${days}`).then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useGeoHistory = (days = 30) =>
  useQuery({
    queryKey: ['journal-geo', days],
    queryFn: () => api.get(`/journal/geo-history?days=${days}`).then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useTradeMtm = (days = 30) =>
  useQuery({
    queryKey: ['journal-mtm', days],
    queryFn: () => api.get(`/journal/trade-mtm?days=${days}`).then(r => r.data),
    staleTime: 0,
    refetchInterval: 5 * 60_000,
    refetchIntervalInBackground: false,
  })

export const useClosedTrades = (days = 180) =>
  useQuery({
    queryKey: ['journal-closed', days],
    queryFn: () => api.get(`/journal/closed-trades?days=${days}`).then(r => r.data),
    staleTime: 30_000,
  })

export const useExitDefaults = () =>
  useQuery({
    queryKey: ['journal-exit-defaults'],
    queryFn: () => api.get('/journal/exit-defaults').then(r => r.data),
    staleTime: 60_000,
  })

export const useCloseTrade = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: number; body: {
      close_price: number; pnl_realized?: number;
      close_reason: string; close_note: string
    }}) => api.patch(`/journal/trades/${id}/close`, body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['journal-mtm'] })
      qc.invalidateQueries({ queryKey: ['journal-closed'] })
      qc.invalidateQueries({ queryKey: ['journal-summary'] })
    },
  })
}

export const useDeleteTrade = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: number) => api.delete(`/journal/trades/${id}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['journal-mtm'] })
      qc.invalidateQueries({ queryKey: ['journal-closed'] })
      qc.invalidateQueries({ queryKey: ['journal-summary'] })
    },
  })
}

export const useUpdateExitParams = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: number; body: {
      target_pct?: number; stop_loss_pct?: number; signal_threshold?: number
    }}) => api.patch(`/journal/trades/${id}/exit-params`, body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['journal-mtm'] }),
  })
}

export const useSaveExitDefaults = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      target_pct?: number; stop_loss_pct?: number;
      signal_reversal_mode?: string; signal_reversal_threshold?: number
    }) => api.put('/journal/exit-defaults', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['journal-exit-defaults'] }),
  })
}

export const useOptionsGate = () =>
  useQuery({
    queryKey: ['options-gate-config'],
    queryFn: () => api.get('/config/options-gate').then(r => r.data),
    staleTime: 60_000,
  })

export const useSaveOptionsGate = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      iv_gate_enabled?: boolean
      iv_gate_ivr_high?: number
      iv_gate_ivr_extreme?: number
      iv_gate_skew_threshold?: number
    }) => api.put('/config/options-gate', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['options-gate-config'] }),
  })
}

export const useTechIndicatorsConfig = () =>
  useQuery({
    queryKey: ['tech-indicators-config'],
    queryFn: () => api.get('/config/tech-indicators').then(r => r.data),
    staleTime: 60_000,
  })

export const useSaveTechIndicatorsConfig = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      tech_indicators_enabled?: boolean
      tech_indicators_list?: string
      tech_indicators_auto_calibrate?: boolean
    }) => api.put('/config/tech-indicators', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['tech-indicators-config'] }),
  })
}

export const useSimPortfolioRisk = () =>
  useQuery({
    queryKey: ['journal-portfolio-risk'],
    queryFn: () => api.get('/journal/portfolio-risk').then(r => r.data),
    staleTime: 30_000,
  })

// 5-axis risk radar (Concentration/Volatility/Correlation/Exposure/Drawdown) — heavier
// than useSimPortfolioRisk above (live per-position vol + a correlation matrix), kept
// as its own endpoint/query so it isn't refetched as eagerly.
export const usePortfolioRiskRadar = () =>
  useQuery({
    queryKey: ['journal-portfolio-risk-radar'],
    queryFn: () => api.get('/journal/portfolio-risk-radar').then(r => r.data),
    staleTime: 5 * 60_000,
  })

// Same 5-axis shape as usePortfolioRiskRadar above, computed on the REAL portfolio
// (services/database.py `portfolio` table) instead of the simulated trade log.
export const useRiskRadar = () =>
  useQuery({
    queryKey: ['risk-radar'],
    queryFn: () => api.get('/risk/radar').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useTradeCheck = () =>
  useMutation({
    mutationFn: (body: { underlying: string; strategy: string; asset_class: string }) =>
      api.post('/journal/trade-check', body).then(r => r.data),
  })

export const useSkippedTrades = (days = 30) =>
  useQuery({
    queryKey: ['journal-skipped', days],
    queryFn: () => api.get(`/journal/skipped-trades?days=${days}`).then(r => r.data),
    staleTime: 60_000,
  })

// ── Risk Profiles ─────────────────────────────────────────────────────────────

export const useRiskProfiles = () =>
  useQuery({
    queryKey: ['risk-profiles'],
    queryFn: () => api.get('/profiles').then(r => r.data),
    staleTime: 30_000,
  })

export const useUpsertProfile = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (profile: {
      id?: number; name: string; min_score: number; min_gain_pct: number;
      color?: string; enabled?: boolean; sort_order?: number
    }) => {
      if (profile.id) {
        return api.put(`/profiles/${profile.id}`, profile).then(r => r.data)
      }
      return api.post('/profiles', profile).then(r => r.data)
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['risk-profiles'] }),
  })
}

export const useDeleteProfile = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: number) => api.delete(`/profiles/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['risk-profiles'] }),
  })
}

export const usePreviewTradeScore = (score: number, gainPct: number, enabled = true) =>
  useQuery({
    queryKey: ['profile-preview', score, gainPct],
    queryFn: () => api.get(`/profiles/preview?score=${score}&gain_pct=${gainPct}`).then(r => r.data),
    enabled,
    staleTime: 0,
  })

// ── Reasoning Traces / Post-mortem ────────────────────────────────────────────

export const useTradePostmortem = (tradeId: number | null) =>
  useQuery({
    queryKey: ['postmortem', tradeId],
    queryFn: () => api.get(`/reasoning/postmortem/${tradeId}`).then(r => r.data),
    enabled: tradeId !== null,
    staleTime: 60_000,
  })

export const useAnalyzePostmortem = () =>
  useMutation({
    mutationFn: (tradeId: number) =>
      api.post(`/reasoning/postmortem/${tradeId}/analyze`).then(r => r.data),
  })

export const usePortfolioReportData = (days: number) =>
  useQuery({
    queryKey: ['portfolio-report-data', days],
    queryFn: () => api.get(`/reasoning/portfolio-report?days=${days}`).then(r => r.data),
    staleTime: 120_000,
  })

export const useGeneratePortfolioReport = () =>
  useMutation({
    mutationFn: (days: number) =>
      api.post(`/reasoning/portfolio-report/generate?days=${days}`).then(r => r.data),
  })

export const useAiReportsList = () =>
  useQuery({
    queryKey: ['ai-reports-list'],
    queryFn: () => api.get('/reasoning/reports').then(r => r.data),
    staleTime: 30_000,
  })

export const useAiReport = (reportId: number | null) =>
  useQuery({
    queryKey: ['ai-report', reportId],
    queryFn: () => api.get(`/reasoning/reports/${reportId}`).then(r => r.data),
    enabled: reportId !== null,
    staleTime: Infinity,
  })

// ── Super Contexte / Knowledge Base ──────────────────────────────────────────
export const useKnowledgeState = () =>
  useQuery({
    queryKey: ['knowledge-state'],
    queryFn: () => api.get('/knowledge/state').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useKnowledgeHistory = () =>
  useQuery({
    queryKey: ['knowledge-history'],
    queryFn: () => api.get('/knowledge/history').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useKnowledgeStateVersion = (stateId: number | null) =>
  useQuery({
    queryKey: ['knowledge-state-version', stateId],
    queryFn: () => api.get(`/knowledge/history/${stateId}`).then(r => r.data),
    enabled: stateId !== null,
    staleTime: Infinity,
  })

export const useKnowledgeEntries = () =>
  useQuery({
    queryKey: ['knowledge-entries'],
    queryFn: () => api.get('/knowledge/entries').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useSynthesizeKnowledge = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (force: boolean = false) =>
      api.post(`/knowledge/synthesize${force ? '?force=true' : ''}`).then(r => r.data),
    onSuccess: (_data) => {
      if (!_data?.skipped) {
        qc.invalidateQueries({ queryKey: ['knowledge-state'] })
        qc.invalidateQueries({ queryKey: ['knowledge-history'] })
        qc.invalidateQueries({ queryKey: ['knowledge-entries'] })
      }
    },
  })
}

export const usePatchKbEntryStatus = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, status }: { id: number; status: string }) =>
      api.patch(`/knowledge/entries/${id}/status`, { status }).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['knowledge-entries'] }),
  })
}

export const useDeleteAiReport = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: number) => api.delete(`/reasoning/reports/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['ai-reports-list'] }),
  })
}

export const useDeleteReasoningState = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: number) => api.delete(`/knowledge/history/${id}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['knowledge-history'] })
      qc.invalidateQueries({ queryKey: ['knowledge-state'] })
    },
  })
}

export const useDeleteKbEntry = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: number) => api.delete(`/knowledge/entries/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['knowledge-entries'] }),
  })
}

// ── Options Volatility ────────────────────────────────────────────────────────
export const useIvSnapshot = (ticker: string) =>
  useQuery({
    queryKey: ['iv-snapshot', ticker],
    queryFn: () => api.get(`/options-vol/snapshot/${encodeURIComponent(ticker)}`).then(r => r.data),
    enabled: !!ticker,
    staleTime: 60 * 60_000, // 1h — IV doesn't change that fast
  })

export const useIvWatchlist = () =>
  useQuery({
    queryKey: ['iv-watchlist'],
    queryFn: () => api.get('/options-vol/watchlist').then(r => r.data),
    staleTime: 60 * 60_000,
  })

export const useIvBatch = (tickers: string) =>
  useQuery({
    queryKey: ['iv-batch', tickers],
    queryFn: () => api.get('/options-vol/batch', { params: { tickers } }).then(r => r.data),
    enabled: !!tickers,
    staleTime: 60 * 60_000,
  })

export const useIvHistory = (ticker: string, days = 90) =>
  useQuery({
    queryKey: ['iv-history', ticker, days],
    queryFn: () => api.get(`/options-vol/history/${encodeURIComponent(ticker)}`, { params: { days } }).then(r => r.data),
    enabled: !!ticker,
    staleTime: 60 * 60_000,
  })

export const useIvForTrade = (underlying: string) =>
  useQuery({
    queryKey: ['iv-for-trade', underlying],
    queryFn: () => api.get(`/options-vol/for-trade/${encodeURIComponent(underlying)}`).then(r => r.data),
    enabled: !!underlying,
    staleTime: 60 * 60_000,
  })

// ── Saxo-only IV analytics (Options Lab "Saxo" section) — computed from our own
// accumulated saxo_option_snapshots, never blended with the yfinance-based hooks above ──
export const useSaxoIvWatchlist = () =>
  useQuery({
    queryKey: ['saxo-iv-watchlist'],
    queryFn: () => api.get('/saxo/iv-watchlist').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useSaxoIvSnapshot = (symbol: string) =>
  useQuery({
    queryKey: ['saxo-iv-snapshot', symbol],
    queryFn: () => api.get(`/saxo/iv-snapshot/${encodeURIComponent(symbol)}`).then(r => r.data),
    enabled: !!symbol,
    staleTime: 5 * 60_000,
  })

export const useSaxoIvHistory = (symbol: string, days = 90) =>
  useQuery({
    queryKey: ['saxo-iv-history', symbol, days],
    queryFn: () => api.get(`/saxo/iv-history/${encodeURIComponent(symbol)}`, { params: { days } }).then(r => r.data),
    enabled: !!symbol,
    staleTime: 5 * 60_000,
  })

// ── Analytics Phase 4 — Bayesian + Clustering + Embeddings ───────────────────

export const useBayesianPosteriors = () =>
  useQuery({
    queryKey: ['bayesian-posteriors'],
    queryFn: () => api.get('/analytics/bayesian').then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useRegimeClusters = (days = 90) =>
  useQuery({
    queryKey: ['regime-clusters', days],
    queryFn: () => api.get('/analytics/regime-clusters', { params: { days } }).then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useRegimeTransitions = (days = 180) =>
  useQuery({
    queryKey: ['regime-transitions', days],
    queryFn: () => api.get('/analytics/regime-transitions', { params: { days } }).then(r => r.data),
    staleTime: 10 * 60_000,
  })

export const usePatternEmbeddings = () =>
  useQuery({
    queryKey: ['pattern-embeddings'],
    queryFn: () => api.get('/analytics/embeddings').then(r => r.data),
    staleTime: 10 * 60_000,
  })

// ── Analytics (Phase 2) ───────────────────────────────────────────────────────

export const usePatternReliability = () =>
  useQuery({
    queryKey: ['pattern-reliability'],
    queryFn: () => api.get('/analytics/reliability').then(r => r.data),
    staleTime: 10 * 60_000,
  })

export const useCalibration = (days = 365) =>
  useQuery({
    queryKey: ['calibration', days],
    queryFn: () => api.get('/analytics/calibration', { params: { days } }).then(r => r.data),
    staleTime: 10 * 60_000,
  })

// ── Risk Engine (Phase 3) ─────────────────────────────────────────────────────

export const useRiskExposure = () =>
  useQuery({
    queryKey: ['risk-exposure'],
    queryFn: () => api.get('/risk/exposure').then(r => r.data),
    staleTime: 2 * 60_000,
  })

export const usePnlTimeline = (days = 90) =>
  useQuery({
    queryKey: ['pnl-timeline', days],
    queryFn: () => api.get('/risk/timeline', { params: { days } }).then(r => r.data),
    staleTime: 5 * 60_000,
  })

export const useRiskClusters = () =>
  useQuery({
    queryKey: ['risk-clusters'],
    queryFn: () => api.get('/risk/clusters').then(r => r.data),
    staleTime: 2 * 60_000,
  })

export const usePatternCorrelations = () =>
  useQuery({
    queryKey: ['pattern-correlations'],
    queryFn: () => api.get('/risk/correlations').then(r => r.data),
    staleTime: 10 * 60_000,
  })

export const useKellySizing = (patternId: string, capital = 10000) =>
  useQuery({
    queryKey: ['kelly', patternId, capital],
    queryFn: () => api.get(`/risk/kelly/${encodeURIComponent(patternId)}`, { params: { capital } }).then(r => r.data),
    enabled: !!patternId,
    staleTime: 5 * 60_000,
  })

export const useRiskDashboard = () =>
  useQuery({
    queryKey: ['risk-dashboard'],
    queryFn: () => api.get('/risk/dashboard').then(r => r.data),
    staleTime: 2 * 60_000,
    refetchInterval: 5 * 60_000,
  })

// ── System Logs ───────────────────────────────────────────────────────────────

export interface LogFilters {
  level?: string
  source?: string
  cycle_id?: string
  ticker?: string
  date_from?: string
  date_to?: string
  limit?: number
}

export const useSystemLogs = (filters: LogFilters = {}) =>
  useQuery({
    queryKey: ['system-logs', filters],
    queryFn: () => api.get('/logs', { params: filters }).then(r => r.data),
    staleTime: 30_000,
    refetchInterval: 30_000,
  })

export const useLogSources = () =>
  useQuery({
    queryKey: ['log-sources'],
    queryFn: () => api.get('/logs/sources').then(r => r.data),
    staleTime: 60_000,
  })

// ── Wavelets — latest per-ticker signal from the watchlist scan (auto-cycle) ──
export const useWaveletWatchlistSignals = () =>
  useQuery({
    queryKey: ['wavelet-watchlist-signals'],
    queryFn: () => api.get('/wavelet/watchlist-signals').then(r => r.data as { signals: any[] }),
    staleTime: 5 * 60_000,
  })

// Instrument-level Curve Regime (services.curve_regime) per Watchlist instrument — cached
// on the same refresh cadence as the wavelet cache (services.wavelet_scheduler), NOT the
// global macro regime. Feeds the Dashboard's "Curve Regime" card.
export const useWatchlistCurveRegimes = () =>
  useQuery({
    queryKey: ['watchlist-curve-regimes'],
    queryFn: () => api.get('/watchlist/curve-regimes').then(r => r.data as { items: any[] }),
    staleTime: 5 * 60_000,
  })

export const useLogCycles = () =>
  useQuery({
    queryKey: ['log-cycles'],
    queryFn: () => api.get('/logs/cycles').then(r => r.data),
    staleTime: 60_000,
  })

export const useClearLogs = () =>
  useMutation({
    mutationFn: (days: number) => api.delete('/logs/clear', { params: { older_than_days: days } }).then(r => r.data),
  })

export const useCycleContextSnapshots = (limit = 30) =>
  useQuery({
    queryKey: ['cycle-context-snapshots'],
    queryFn: () => api.get('/cycle/contexts', { params: { limit } }).then(r => r.data),
    staleTime: 30_000,
  })

export const useCycleContextSnapshot = (runId: string | null) =>
  useQuery({
    queryKey: ['cycle-context-snapshot', runId],
    queryFn: () => api.get(`/cycle/contexts/${runId}`).then(r => r.data),
    enabled: !!runId,
    staleTime: 300_000,
  })

export const useReplayCycle = () =>
  useMutation({
    mutationFn: ({ runId, notes }: { runId: string; notes?: string }) =>
      api.post(`/cycle/contexts/${runId}/replay`, { override_notes: notes ?? null }).then(r => r.data),
  })

export const useAiCallLogs = (runId: string | null) =>
  useQuery({
    queryKey: ['ai-call-logs', runId],
    queryFn: () => api.get(`/cycle/ai-calls/${runId}`).then(r => r.data),
    enabled: !!runId,
    staleTime: 60_000,
  })

// ── IV Watchlist Management ───────────────────────────────────────────────────

export const useWatchlistTickers = () =>
  useQuery({
    queryKey: ['watchlist-tickers'],
    queryFn: () => api.get('/options-vol/watchlist-tickers').then(r => r.data),
    staleTime: 60_000,
  })

export const useAddWatchlistTicker = () =>
  useMutation({
    mutationFn: (ticker: string) => api.post(`/options-vol/watchlist-tickers/${encodeURIComponent(ticker)}`).then(r => r.data),
  })

export const useRemoveWatchlistTicker = () =>
  useMutation({
    mutationFn: (ticker: string) => api.delete(`/options-vol/watchlist-tickers/${encodeURIComponent(ticker)}`).then(r => r.data),
  })


// ── Institutional Reports ─────────────────────────────────────────────────────

export const useInstitutionalReports = (params: {
  report_type?: string
  category?: string
  importance?: number
  days?: number
}) =>
  useQuery({
    queryKey: ['institutional-reports', params],
    queryFn: () =>
      api.get('/institutional/reports', { params }).then(r => r.data),
    refetchInterval: 3_600_000,
  })

export const useInstitutionalStats = () =>
  useQuery({
    queryKey: ['institutional-stats'],
    queryFn: () => api.get('/institutional/stats').then(r => r.data),
    staleTime: 300_000,
  })

export const useRefreshInstitutionalReports = () =>
  useMutation({
    mutationFn: (report_type?: string) =>
      api.post('/institutional/refresh', null, { params: report_type ? { report_type } : {} }).then(r => r.data),
  })

// ── Pattern Lab ───────────────────────────────────────────────────────────────
export const usePatternLabRuns = () =>
  useQuery({
    queryKey: ['pattern-lab-runs'],
    queryFn: () => api.get('/pattern-lab/runs').then(r => r.data),
    staleTime: 30_000,
  })

export const useRunPatternLab = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      preset_id?: string; theme: string; analysis_date: string;
      horizon_days: number; assets: string[]; theme_hint: string;
    }) => api.post('/pattern-lab/run', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
  })
}

export const useEvaluatePatternLab = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (run_id: string) => api.post(`/pattern-lab/evaluate/${run_id}`, {}).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
  })
}

export const useSaveLabPattern = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      run_id: string; pattern_index: number; name?: string;
      category?: string; signal_direction?: string; asset_class?: string;
      action?: 'new' | 'instance' | 'counter';
      target_id?: string; regime_tag?: string; event_name?: string;
    }) => api.post('/pattern-lab/save-pattern', body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['all-patterns'] })
      qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] })
    },
  })
}

export type MatchResult = {
  recommendation: 'merge_as_instance' | 'counter_scenario' | 'new_pattern'
  match_id?: string
  match_name?: string
  confidence: number
  reasoning: string
  suggested_regime_tag?: string
}

export const useFindMatchingPattern = () =>
  useMutation({
    mutationFn: (body: { run_id: string; pattern_index: number }): Promise<MatchResult> =>
      api.post('/pattern-lab/find-matching', body).then(r => r.data),
  })

export type DiscoveredEvent = {
  label: string; date: string; category: string; horizon_days: number
  assets: string[]; hint: string; confidence: number
}

export const useDiscoverEvents = () =>
  useMutation({
    mutationFn: (body: { query: string; n?: number }): Promise<{ events: DiscoveredEvent[]; source: string; query: string }> =>
      api.post('/pattern-lab/discover', body).then(r => r.data),
  })

export const useDeleteLabRun = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (run_id: string) => api.delete(`/pattern-lab/runs/${run_id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
  })
}

export const useInstrumentScan = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      ticker: string; instrument_name: string;
      start_date: string; end_date: string; horizon_days: number;
    }) => api.post('/pattern-lab/instrument-scan', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
  })
}

export const useEvaluateInstrumentScan = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (run_id: string) => api.post(`/pattern-lab/evaluate-instrument/${run_id}`, {}).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['pattern-lab-runs'] }),
  })
}

// ── Specialist Desks ───────────────────────────────────────────────────────────

export type MacroSensitivity = { regime: string; effect: string }
export type PriceDeltaThresholds = {
  significant_day: number; extreme_day: number
  significant_week: number; extreme_week: number
}
export type SpecialistReport = {
  id: string; name: string; source: string; cadence: string
  next_date: string | null; last_date: string | null
  importance: number; notes: string; url: string
  desks?: string[]
  created_at: string; updated_at: string
}
export type DeskConfig = {
  asset_class: string; display_name: string; icon: string
  fundamentals: string; macro_sensitivity: MacroSensitivity[]
  price_delta_thresholds: PriceDeltaThresholds; notes: string
  reports: SpecialistReport[]; updated_at: string
}

export const useAllDeskConfigs = () =>
  useQuery<DeskConfig[]>({
    queryKey: ['desk-configs'],
    queryFn: () => api.get('/specialist-desks/configs').then(r => r.data),
    staleTime: 60_000,
  })

export const useUpdateDeskConfig = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ asset_class, body }: { asset_class: string; body: Partial<DeskConfig> }) =>
      api.put(`/specialist-desks/configs/${asset_class}`, body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['desk-configs'] }),
  })
}

export const useAllSpecialistReports = () =>
  useQuery<SpecialistReport[]>({
    queryKey: ['specialist-reports'],
    queryFn: () => api.get('/specialist-desks/reports').then(r => r.data),
    staleTime: 60_000,
  })

export const useCreateSpecialistReport = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: Partial<SpecialistReport> & { desks?: string[] }) =>
      api.post('/specialist-desks/reports', body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

export const useUpdateSpecialistReport = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: string; body: Partial<SpecialistReport> & { desks?: string[] } }) =>
      api.put(`/specialist-desks/reports/${id}`, body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

export const useDeleteSpecialistReport = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: string) => api.delete(`/specialist-desks/reports/${id}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

export const useLinkReportDesk = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ report_id, asset_class }: { report_id: string; asset_class: string }) =>
      api.post(`/specialist-desks/reports/${report_id}/link/${asset_class}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

export const useUnlinkReportDesk = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ report_id, asset_class }: { report_id: string; asset_class: string }) =>
      api.delete(`/specialist-desks/reports/${report_id}/link/${asset_class}`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

// ── COT / Forward Curves / Surprise Index / Hawk-Dove — new types ─────────────

export interface CotEntry {
  id: number
  market_name: string
  commodity: string
  asset_class: string
  report_date: string
  mm_long: number
  mm_short: number
  open_interest: number
  net_position: number
  net_pct_oi: number
  change_net: number
  fetched_at: string
}

export interface ForwardCurveEntry {
  id: number
  asset: string
  asset_class: string
  front_price: number | null
  far_price: number | null
  slope_pct: number | null
  structure: 'contango' | 'backwardation' | 'flat' | 'unknown'
  months_spread: number
  fetched_at: string
}

export interface TextSentimentResult {
  score: number
  label: string
  summary: string
  key_phrases: string[]
  confidence: 'high' | 'medium' | 'low'
}

export interface ReportResultUpdate {
  actual_value?: number | null
  consensus_estimate?: number | null
  text_sentiment_score?: number | null
  text_sentiment_label?: string | null
  text_sentiment_summary?: string | null
}

// ── COT Data ──────────────────────────────────────────────────────────────────

export function useCotData() {
  return useQuery<CotEntry[]>({
    queryKey: ['specialist-desks', 'cot'],
    queryFn: () => api.get('/specialist-desks/cot').then(r => r.data),
    staleTime: 1000 * 60 * 60, // 1h
  })
}

export function useFetchCot() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: () => api.post('/specialist-desks/cot/fetch').then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['specialist-desks', 'cot'] }),
  })
}

// ── Forward Curves ────────────────────────────────────────────────────────────

export function useForwardCurves() {
  return useQuery<ForwardCurveEntry[]>({
    queryKey: ['specialist-desks', 'forward-curves'],
    queryFn: () => api.get('/specialist-desks/forward-curves').then(r => r.data),
    staleTime: 1000 * 60 * 60,
  })
}

export function useFetchForwardCurves() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: () => api.post('/specialist-desks/forward-curves/fetch').then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['specialist-desks', 'forward-curves'] }),
  })
}

// ── Surprise Index ────────────────────────────────────────────────────────────

export function useUpdateReportResult() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ id, body }: { id: string; body: ReportResultUpdate }) =>
      api.put(`/specialist-desks/reports/${id}/result`, body).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['specialist-reports'] })
      qc.invalidateQueries({ queryKey: ['desk-configs'] })
    },
  })
}

// ── Hawk/Dove Text Scorer ─────────────────────────────────────────────────────

export function useScoreText() {
  return useMutation({
    mutationFn: (body: { text: string; report_name: string; desk: string }) =>
      api.post('/specialist-desks/score-text', body).then(r => r.data) as Promise<TextSentimentResult>,
  })
}

// ── AI Chat widget — free-form, read-only, context-aware conversation ────────

export interface ChatMessage { role: 'user' | 'assistant'; content: string; created_at?: string; trade_proposal?: TradeProposalRef | null }

export const useChatContextBlocks = () =>
  useQuery({
    queryKey: ['ai-chat-blocks'],
    queryFn: () => api.get('/ai-chat/blocks').then(r => r.data.blocks as string[]),
    staleTime: Infinity,
  })

export const useChatHistory = (sessionId: string) =>
  useQuery({
    queryKey: ['ai-chat-history', sessionId],
    queryFn: () => api.get('/ai-chat/history', { params: { session_id: sessionId } }).then(r => r.data.messages as ChatMessage[]),
    enabled: !!sessionId,
    staleTime: Infinity,
  })

export interface TradeProposalRef { id: string; title?: string; underlying?: string; strategy?: string }

export const useSendChatMessage = () =>
  useMutation({
    mutationFn: (body: { session_id: string; message: string; enabled_blocks?: string[]; refresh_context?: boolean }) =>
      api.post('/ai-chat/', body).then(r => r.data as { reply: string; blocks_included: string[]; trade_proposal: TradeProposalRef | null }),
  })

export const useClearChatSession = () =>
  useMutation({
    mutationFn: (sessionId: string) => api.post('/ai-chat/clear', { session_id: sessionId }).then(r => r.data),
  })

// ── AI Chat widget — trade proposals (pending confirmation) ──────────────────

export interface AiTradeProposal {
  id: string
  session_id: string
  created_at: string
  status: 'pending' | 'confirmed' | 'rejected'
  title: string
  underlying: string
  strategy: string
  asset_class: string
  expiry_days: number
  capital_invested: number
  legs: Array<{ strike: number; option_type: string; quantity: number; position: string }>
  geo_trigger: string
  rationale: string
  portfolio_id?: string | null
}

export const useAiTradeProposals = (status: string = 'pending') =>
  useQuery({
    queryKey: ['ai-trade-proposals', status],
    queryFn: () => api.get('/ai-chat/trade-proposals', { params: { status } }).then(r => r.data.proposals as AiTradeProposal[]),
    refetchInterval: 30000,
  })

export const useConfirmAiTradeProposal = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (proposalId: string) => api.post(`/ai-chat/trade-proposals/${proposalId}/confirm`).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['ai-trade-proposals'] })
      qc.invalidateQueries({ queryKey: ['portfolio'] })
      qc.invalidateQueries({ queryKey: ['portfolio-summary'] })
    },
  })
}

export const useRejectAiTradeProposal = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (proposalId: string) => api.post(`/ai-chat/trade-proposals/${proposalId}/reject`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['ai-trade-proposals'] }),
  })
}

// ── Strategy Builder ────────────────────────────────────────────────────────

export type ChainRow = { strike: number; bid: number; ask: number; mid: number; last: number; iv: number; open_interest: number; volume: number }
export type ChainExpiry = { expiry_date: string; days_to_expiry: number; calls: ChainRow[]; puts: ChainRow[] }
export type ChainSlice = { symbol: string; proxy: string; spot: number; as_of?: string | null; expiries: ChainExpiry[] }

export type ManualGridCell = { days_to_expiry: number; strike_pct: number; iv: number | null }

export type StrategyScenario = {
  symbol: string
  horizon_days: number
  spot_shock_pct: number
  iv_level_shift: number
  skew_tilt: number
  term_shift: number
  manual_grid?: ManualGridCell[]
  rate?: number
  n_expiries?: number
  contract_size?: number
}

export type StrategyLeg = {
  expiry_date: string
  days_to_expiry: number
  strike: number
  option_type: 'call' | 'put'
  position: 'long' | 'short'
  quantity: number
}

export type Greeks = { delta: number; gamma: number; theta: number; vega: number }
export type PayoffPoint = { underlying: number; pnl: number }

export type PriceCombo = {
  entry_cost: number
  entry_cost_mid: number
  scenario_value: number
  scenario_value_mid: number
  net_pnl: number
  broker_spread_cost: number
  max_gain: number | null
  max_loss: number | null
  bounded_risk: boolean
  greeks_now: Greeks
  greeks_scenario: Greeks
  net_delta_now: number
  net_delta_scenario: number
  at_expiry: PayoffPoint[]
  at_scenario: PayoffPoint[]
  spot: number
  scenario_spot: number
  proxy: string
}

export type StrategyCandidate = {
  template_name: string
  legs: StrategyLeg[]
  score: number
  objective: string
} & PriceCombo

export const useOptionChainSlice = (symbol: string, horizonDays: number, nExpiries = 3, enabled = true) =>
  useQuery<ChainSlice>({
    queryKey: ['strategy-builder-chain', symbol, horizonDays, nExpiries],
    queryFn: () => api.get('/strategy-builder/chain', { params: { symbol, horizon_days: horizonDays, n_expiries: nExpiries } }).then(r => r.data),
    enabled: enabled && !!symbol,
    staleTime: 30_000,
    retry: 1,
  })

export const usePriceStrategy = () =>
  useMutation({
    mutationFn: (body: { scenario: StrategyScenario; legs: StrategyLeg[] }) =>
      api.post<PriceCombo>('/strategy-builder/price', body).then(r => r.data),
  })

export type OptimizeConstraints = {
  max_legs: number
  delta_threshold: number | null
  max_loss_cap?: number | null
  objective: 'net_pnl' | 'return_on_risk' | 'prob_weighted'
  top_n?: number
}

export const useOptimizeStrategy = () =>
  useMutation({
    mutationFn: (body: { scenario: StrategyScenario; constraints: OptimizeConstraints }) =>
      api.post<StrategyCandidate[]>('/strategy-builder/optimize', body).then(r => r.data),
  })

export type SavedScenario = {
  id: string; symbol: string; label: string; horizon_days: number
  spot_shock_pct: number; iv_level_shift: number; skew_tilt: number; term_shift: number
  manual_grid: ManualGridCell[]; created_at: string
}

export type SavedStrategyRecord = {
  id: string; scenario_id: string | null; symbol: string; template_name: string; objective: string
  legs: StrategyLeg[]; entry_cost: number | null; max_gain: number | null; max_loss: number | null
  net_pnl_scenario: number | null; net_delta: number | null; notes: string; created_at: string
}

export const useScenarios = (symbol?: string) =>
  useQuery<SavedScenario[]>({
    queryKey: ['strategy-scenarios', symbol],
    queryFn: () => api.get('/strategy-builder/scenarios', { params: symbol ? { symbol } : {} }).then(r => r.data),
  })

export const useSaveScenario = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: StrategyScenario & { label?: string }) => api.post('/strategy-builder/scenarios', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['strategy-scenarios'] }),
  })
}

export const useDeleteScenario = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: string) => api.delete(`/strategy-builder/scenarios/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['strategy-scenarios'] }),
  })
}

export const useSavedStrategies = (symbol?: string) =>
  useQuery<SavedStrategyRecord[]>({
    queryKey: ['saved-strategies', symbol],
    queryFn: () => api.get('/strategy-builder/saved', { params: symbol ? { symbol } : {} }).then(r => r.data),
  })

export const useSaveStrategyRecord = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (body: {
      scenario_id?: string | null; symbol: string; template_name?: string; objective?: string
      legs: StrategyLeg[]; entry_cost?: number | null; max_gain?: number | null; max_loss?: number | null
      net_pnl_scenario?: number | null; net_delta?: number | null; notes?: string
    }) => api.post('/strategy-builder/saved', body).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saved-strategies'] }),
  })
}

export const useDeleteSavedStrategy = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (id: string) => api.delete(`/strategy-builder/saved/${id}`).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saved-strategies'] }),
  })
}

// ── Saxo connection ───────────────────────────────────────────────────────────

export type SaxoStatus = {
  connected: boolean; configured: boolean; environment: string
  expires_at?: string; refresh_expires_at?: string
}

export const useSaxoStatus = () =>
  useQuery<SaxoStatus>({
    queryKey: ['saxo-status'],
    queryFn: () => api.get('/saxo/status').then(r => r.data),
    refetchInterval: 60_000,
  })

export const useDisconnectSaxo = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: () => api.post('/saxo/disconnect').then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saxo-status'] }),
  })
}

export const useSaxoWatchlist = () =>
  useQuery<{ symbols: string[] }>({
    queryKey: ['saxo-watchlist'],
    queryFn: () => api.get('/saxo/watchlist').then(r => r.data),
  })

export const useUpdateSaxoWatchlist = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (symbols: string[]) => api.put('/saxo/watchlist', { symbols }).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saxo-watchlist'] }),
  })
}

export const useExpandSaxoWatchlist = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (keyword: string) => api.post('/saxo/watchlist/expand', { keyword }).then(r => r.data as { symbols: string[]; added: string[] }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saxo-watchlist'] }),
  })
}

export const useSnapshotSaxoNow = () =>
  useMutation({
    mutationFn: (symbol: string) => api.post(`/saxo/snapshot-now/${encodeURIComponent(symbol)}`).then(r => r.data),
  })

export const useSnapshotAllSaxoNow = () =>
  useMutation({
    mutationFn: () => api.post('/saxo/snapshot-now').then(r => r.data as { results: Record<string, number> }),
  })

export const useSaxoSettings = () =>
  useQuery<{ snapshot_minutes: number }>({
    queryKey: ['saxo-settings'],
    queryFn: () => api.get('/saxo/settings').then(r => r.data),
  })

export const useUpdateSaxoSettings = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (snapshotMinutes: number) => api.put('/saxo/settings', { snapshot_minutes: snapshotMinutes }).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['saxo-settings'] }),
  })
}

// Wavelet Watchlist refresh — Saxo-priced quotes + wavelet recompute, own cadence
// (services/wavelet_scheduler.py), independent of the once-a-day auto_cycle.
export const useWaveletRefreshSettings = () =>
  useQuery<{ enabled: boolean; refresh_minutes: number }>({
    queryKey: ['wavelet-refresh-settings'],
    queryFn: () => api.get('/wavelet/refresh-settings').then(r => r.data),
  })

export const useUpdateWaveletRefreshSettings = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (settings: { enabled: boolean; refresh_minutes: number }) =>
      api.put('/wavelet/refresh-settings', settings).then(r => r.data),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['wavelet-refresh-settings'] }),
  })
}

export const useWaveletRefreshNow = () =>
  useMutation({
    mutationFn: () => api.post('/wavelet/refresh-now').then(r => r.data as { signal_rows: number }),
  })

export type SaxoSnapshotRow = {
  id: string; symbol: string; snapshot_date: string; spot: number | null
  expiry_date: string; strike: number; option_type: 'call' | 'put'
  bid: number | null; ask: number | null; mid: number | null; volatility_pct: number | null
  delta: number | null; gamma: number | null; theta: number | null; vega: number | null
  is_synthetic?: number; created_at: string
}

export const useDedupeSaxoHistory = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: () => api.post('/saxo/history/dedupe').then(r => r.data as { rows_deleted: number }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['saxo-history'] })
      qc.invalidateQueries({ queryKey: ['saxo-symbols'] })
    },
  })
}

export const useSaxoHistory = (symbol?: string, asOf?: string) =>
  useQuery<SaxoSnapshotRow[]>({
    queryKey: ['saxo-history', symbol, asOf],
    queryFn: () => api.get('/saxo/history', {
      params: { ...(symbol ? { symbol } : {}), ...(asOf ? { as_of: asOf } : {}) },
    }).then(r => r.data),
  })

export const useDeleteSaxoHistory = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (symbol: string) => api.delete(`/saxo/history/${encodeURIComponent(symbol)}`).then(r => r.data as { symbol: string; rows_deleted: number }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['saxo-history'] })
      qc.invalidateQueries({ queryKey: ['saxo-symbols'] })
    },
  })
}

export type SaxoSymbolSummary = { symbol: string; rows_count: number; first_date: string; last_date: string }

export const useSaxoSymbols = () =>
  useQuery<SaxoSymbolSummary[]>({
    queryKey: ['saxo-symbols'],
    queryFn: () => api.get('/saxo/symbols').then(r => r.data),
  })

export type SaxoValidation = {
  symbol: string; valid: boolean; uic: number | null; error: string | null
  matched_symbol?: string; description?: string; asset_type?: string
}

export const useValidateSaxoWatchlist = () =>
  useQuery<SaxoValidation[]>({
    queryKey: ['saxo-validate'],
    queryFn: () => api.get('/saxo/validate').then(r => r.data),
    enabled: false,
  })

export type SaxoCatalogEntry = { uic: number; symbol: string; asset_type: string; description: string; updated_at: string }
export type SaxoCatalogSummary = { asset_type: string; count: number; last_refreshed: string }

export const useSaxoCatalog = (assetType?: string, q?: string, opts?: { enabled?: boolean; limit?: number }) =>
  useQuery<SaxoCatalogEntry[]>({
    queryKey: ['saxo-catalog', assetType, q, opts?.limit],
    queryFn: () => api.get('/saxo/catalog', {
      params: { ...(assetType ? { asset_type: assetType } : {}), ...(q ? { q } : {}), ...(opts?.limit ? { limit: opts.limit } : {}) },
    }).then(r => r.data),
    enabled: opts?.enabled ?? !!(assetType || q),
  })

export const useSaxoCatalogSummary = () =>
  useQuery<SaxoCatalogSummary[]>({
    queryKey: ['saxo-catalog-summary'],
    queryFn: () => api.get('/saxo/catalog/summary').then(r => r.data),
  })

export const useRefreshSaxoCatalog = () => {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (assetTypes?: string[]) => api.post('/saxo/catalog/refresh', { asset_types: assetTypes ?? null }).then(r => r.data),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['saxo-catalog'] })
      qc.invalidateQueries({ queryKey: ['saxo-catalog-summary'] })
    },
  })
}

export type SaxoQuote = {
  symbol: string; description: string | null; asset_type: string; uic: number
  bid: number | null; ask: number | null; last_updated: string | null; price_source: string | null
}

export const useTestSaxoQuote = () =>
  useMutation({
    mutationFn: ({ symbol, assetType }: { symbol: string; assetType?: string }) =>
      api.get<SaxoQuote>(`/saxo/quote/${encodeURIComponent(symbol)}`, { params: assetType ? { asset_type: assetType } : {} }).then(r => r.data),
  })
