// API envelope (before unwrapping)
export interface APIEnvelope<T = unknown> {
  status: 'success' | 'error';
  data: T | null;
  error: string | null;
}

// Auth
export interface TokenResponse {
  access_token: string;
  token_type: string;
}

// Watchlist
export interface WatchlistEntry {
  symbol: string;
  entry_type: 'auto' | 'manual';
  composite_score: number | null;
  dimensions: DimensionScore[];
  rr_ratio: number | null;
  rr_direction: string | null;
  momentum_percentile: number | null;
  strategy_rank: number | null;
  sr_levels: SRLevelSummary[];
  last_close: number | null;
  change_pct: number | null;
  price_date: string | null;
  added_at: string;
}

export interface DimensionScore {
  dimension: string;
  score: number;
}

export interface SRLevelSummary {
  price_level: number;
  type: 'support' | 'resistance';
  strength: number;
}

// S/R Zone
export interface SRZone {
  low: number;
  high: number;
  midpoint: number;
  strength: number;
  type: 'support' | 'resistance';
  level_count: number;
}

// OHLCV
export interface OHLCVBar {
  id: number;
  ticker_id: number;
  date: string;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  created_at: string;
}

// Scores
export interface SubScore {
  name: string;
  score: number;
  weight: number;
  raw_value: number | string | null;
  description: string;
}

export interface ScoreBreakdown {
  sub_scores: SubScore[];
  formula: string;
  unavailable: { name: string; reason: string }[];
}

export interface CompositeBreakdown {
  weights: Record<string, number>;
  available_dimensions: string[];
  missing_dimensions: string[];
  renormalized_weights: Record<string, number>;
  formula: string;
  base_score?: number | null;
  sentiment_score?: number | null;
  sentiment_adjustment?: number | null;
  max_sentiment_adjustment?: number | null;
}

export interface ScoreResponse {
  symbol: string;
  composite_score: number | null;
  composite_stale: boolean;
  weights: Record<string, number>;
  dimensions: DimensionScoreDetail[];
  missing_dimensions: string[];
  computed_at: string | null;
  composite_breakdown?: CompositeBreakdown | null;
}

export interface DimensionScoreDetail {
  dimension: string;
  score: number;
  is_stale: boolean;
  computed_at: string | null;
  breakdown?: ScoreBreakdown | null;
}

export interface RankingEntry {
  symbol: string;
  composite_score: number;
  composite_stale: boolean;
  dimensions: DimensionScoreDetail[];
}

export interface RankingsResponse {
  rankings: RankingEntry[];
  weights: Record<string, number>;
}

// Trade Setups
export interface TradeSetup {
  id: number;
  symbol: string;
  direction: string;
  entry_price: number;
  stop_loss: number;
  target: number;
  rr_ratio: number;
  composite_score: number;
  detected_at: string;
  confidence_score: number | null;
  targets: TradeTarget[];
  conflict_flags: string[];
  recommended_action: 'LONG_HIGH' | 'LONG_MODERATE' | 'SHORT_HIGH' | 'SHORT_MODERATE' | 'NEUTRAL' | null;
  reasoning: string | null;
  risk_level: 'Low' | 'Medium' | 'High' | null;
  actual_outcome: string | null;
  outcome_date: string | null;
  evaluated_at: string | null;
  current_price: number | null;
  momentum_percentile?: number | null;
  strategy_rank?: number | null;
  volatility_percentile?: number | null;
  reentry_gate_reset_required?: boolean;
  context_as_of?: TradeSetupContextAsOf | null;
  recommendation_summary?: RecommendationSummary;
}

export interface TradeSetupContextAsOf {
  setup_detected_at: string;
  score_computed_at: string | null;
  sentiment_at: string | null;
  price_date: string | null;
  price_updated_at: string | null;
}

// Performance / outcome statistics
export interface OutcomeBucketStats {
  total: number;
  wins: number;
  losses: number;
  expired: number;
  hit_rate: number | null;
  avg_r: number | null;
  total_r: number | null;
}

export interface PerformanceStats {
  overall: OutcomeBucketStats;
  pending: number;
  maturing: number;
  by_direction: Record<string, OutcomeBucketStats>;
  by_action: Record<string, OutcomeBucketStats>;
  by_confidence: Record<string, OutcomeBucketStats>;
}

// Activation gate: what counts as an actionable signal
export interface ActivationConfig {
  min_momentum_percentile: number;
  min_rr: number;
  min_confidence: number;
  require_high_conviction: boolean;
  exclude_conflicts: boolean;
  exclude_neutral: boolean;
}

// Cron schedule for morning / near-close / after-close / intraday + fundamentals
export interface ScheduleConfig {
  schedule_timezone: string;
  schedule_daily_pipeline_cron: string;
  schedule_dolt_earnings_cron: string;
  schedule_sec_fundamentals_cron: string;
  schedule_near_close_pipeline_cron: string;
  schedule_after_close_pipeline_cron: string;
  schedule_intraday_pipeline_cron: string;
}

// Runtime sentiment LLM configuration
export interface SentimentProviderConfig {
  provider: string;
  model: string;
  base_url: string;
  api_key_configured: boolean;
  api_key_source: 'database' | 'environment' | 'none';
  web_search: boolean;
  valid_providers: string[];
  default_models: Record<string, string>;
  web_search_providers: string[];
  custom_base_url_providers: string[];
}

export interface PaperTrade {
  id: number;
  symbol: string;
  direction: 'long' | 'short';
  entry_price: number;
  shares: number;
  stop_loss: number;
  target: number;
  status: 'open' | 'closed';
  opened_at: string;
  close_price: number | null;
  closed_at: string | null;
  current_price: number | null;
  benchmark_return_pct: number | null;
  alpha_pct: number | null;
  alpha_usd: number | null;
  close_reason: 'time' | 'trailing' | 'stop' | 'target' | 'manual' | null;
  trailing_stop: number | null;
  trailing_distance_pct: number | null;
  sessions_held: number | null;
  sessions_remaining: number | null;
}

export interface ExitPolicy {
  mode: 'time' | 'trailing' | 'atr_trailing' | 'target';
  trailing_pct: number;
  atr_multiplier: number;
  hold_days: number;
}

export interface BacktestBucket {
  total: number;
  wins: number;
  losses: number;
  expired: number;
  hit_rate: number | null;
  avg_r: number | null;
  total_r: number | null;
  // Net of transaction costs — optional so a stale cached report still renders.
  net_avg_r?: number | null;
  net_total_r?: number | null;
  best_r?: number | null;
  worst_r?: number | null;
  avg_hold_days?: number | null;
  net_r_per_day?: number | null;
  // Robustness: distribution shape, and expectancy without the top winners.
  median_net_r?: number | null;
  profit_factor?: number | null;
  net_avg_r_ex_top5?: number | null;
}

export interface BacktestSweepRow extends BacktestBucket {
  min_momentum_percentile: number;
}

export interface BacktestTimeExitRow {
  hold_days: number;
  total: number;
  wins: number;
  win_rate: number | null;
  avg_r: number | null;
  total_r: number | null;
  net_avg_r?: number | null;
  net_total_r?: number | null;
  best_r?: number | null;
  worst_r?: number | null;
  avg_hold_days?: number | null;
  net_r_per_day?: number | null;
  median_net_r?: number | null;
  profit_factor?: number | null;
  net_avg_r_ex_top5?: number | null;
}

export interface BacktestPortfolioPolicy {
  policy?: string;
  starting_capital: number;
  final_equity: number;
  total_return_pct: number;
  cagr_pct: number | null;
  max_drawdown_pct: number;
  sharpe: number | null;
  trades: number;
  win_rate: number | null;
  avg_trade_pnl: number | null;
  best_trade_r: number | null;
  worst_trade_r: number | null;
  best_trade_pnl: number | null;
  worst_trade_pnl: number | null;
  avg_hold_days: number | null;
  skipped_book_full: number;
  spy_return_pct: number | null;
  yearly_returns?: { year: number; return_pct: number | null }[];
  exit_reasons?: Record<string, number>;
  equity_curve?: BacktestCurvePoint[];
  benchmark_curve?: BacktestCurvePoint[];
  start_date: string;
  end_date: string;
}

export interface BacktestCurvePoint {
  date: string;
  equity: number;
  return_pct: number | null;
}

export interface BacktestRecommendation {
  headline: string | null;
  items: { topic: string; text: string }[];
  note?: string;
}

export interface BacktestResearchRecommendation {
  items: { topic: string; text: string; candidate?: boolean }[];
  note?: string;
}

export interface BacktestPortfolioSim {
  params: {
    starting_capital: number;
    max_positions: number;
    risk_per_trade_pct: number;
    notional_cap_pct: number;
    cost_per_side_pct: number;
    hold_days: number;
  };
  policies: BacktestPortfolioPolicy[];
  note?: string;
}

export interface BacktestStrategyVariant extends BacktestPortfolioPolicy {
  variant: string;
  label: string;
  ranking: 'raw' | 'residual' | string;
  cutoff: number;
  max_positions: number;
  risk_per_trade_pct: number;
  risk_scale: string | null;
}

export interface BacktestStrategyVariants {
  variants: BacktestStrategyVariant[];
  note?: string;
}

export interface BacktestPortfolioMonitorRun extends BacktestPortfolioPolicy {
  strategy: string;
  label: string;
  description: string;
  is_production: boolean;
  comparison_arm?: 'live_immediate' | 'live_gate_reset' | null;
  entry_variant: string;
  exit_policy: string;
  reentry_policy?: 'immediate' | 'gate_reset';
  lookback: string;
  lookback_label: string;
}

export interface BacktestPortfolioMonitor {
  production_strategy: string;
  strategies: {
    strategy: string;
    label: string;
    description: string;
    is_production: boolean;
    comparison_arm?: 'live_immediate' | 'live_gate_reset' | null;
    reentry_policy?: 'immediate' | 'gate_reset';
  }[];
  lookbacks: { lookback: string; label: string }[];
  runs: BacktestPortfolioMonitorRun[];
  note?: string;
}

export interface BacktestGateAblationRow extends BacktestBucket {
  variant: string;
  // The same variant graded under the hold-to-horizon time exit.
  hold_days?: number;
  hold_avg_r?: number | null;
  hold_net_avg_r?: number | null;
  hold_total_r?: number | null;
}

export interface BacktestSignalEvalRow {
  signal: string;
  weeks: number;
  avg_cross_section: number | null;
  mean_ic: number;
  ic_t_stat: number | null;
  ic_positive_pct: number;
  mean_quintile_spread: number | null;
  reliable: boolean;
}

export interface BacktestReport {
  generated_at: string;
  tickers: number;
  candidates: number;
  qualified: number;
  params: {
    step_days: number;
    step_sessions?: number;
    entry_cadence?: 'weekly' | 'daily';
    signal_eval_cadence?: 'weekly';
    horizon_days: number;
    min_lookback: number;
    cost_per_side_pct?: number;
    target_model?: 'production_gtl' | 'structural_sr';
    target_model_label?: string;
    is_production_target_model?: boolean;
    production_reentry_policy?: 'gate_reset';
  };
  overall_qualified: BacktestBucket;
  overall_all: BacktestBucket;
  by_direction: Record<string, BacktestBucket>;
  min_momentum_percentile: number;
  sweep: BacktestSweepRow[];
  gate_ablation?: BacktestGateAblationRow[];
  gate_ablation_note?: string;
  time_exit_sweep?: BacktestTimeExitRow[];
  portfolio_sim?: BacktestPortfolioSim;
  strategy_variants?: BacktestStrategyVariants;
  exit_policy_variants?: { variants: BacktestStrategyVariant[]; note?: string };
  portfolio_monitor?: BacktestPortfolioMonitor | null;
  recommendation?: BacktestRecommendation;
  research_recommendation?: BacktestResearchRecommendation;
  signal_eval?: BacktestSignalEvalRow[];
  signal_eval_note?: string;
  note: string;
}

export interface MarketRegime {
  label: 'bullish' | 'bearish' | 'neutral' | 'unknown';
  benchmark?: string;
  price?: number | null;
  sma50?: number | null;
  sma200?: number | null;
  pct_above_200?: number | null;
  reason?: string | null;
  computed_at?: string;
}

// AI/Tech Regime-Change Monitor (standalone, observational)
export type RegimeBand = 'stable' | 'watch' | 'elevated' | 'breaking';

export interface RegimeSignal {
  id: string;
  label: string;
  score: number | null;
  available: boolean;
  details?: Record<string, unknown>;
}

export interface RegimePillar {
  id: string;
  label: string;
  score: number | null;
  weight: number;
  contribution: number;
  available: boolean;
  sensors: RegimeSignal[];
}

export interface RegimeReading {
  score: number | null;
  band: RegimeBand | null;
  // Per axis: State and Warning have different realized ranges, so they do not
  // share thresholds.
  bands?: { watch: number; elevated: number; breaking: number };
  coverage: number;
  minimum_coverage: number;
  available_pillars: string[];
  pillars: RegimePillar[];
  trend?: { delta_7: number | null; delta_30: number | null };
}

/** Qualitative capex / earnings-reaction context. Not part of either score. */
export interface RegimeFundamentalOverlay {
  available: boolean;
  pending: boolean;
  stale: boolean;
  effective_date: string | null;
  age_days: number | null;
  capex: Record<string, CapexState> | null;
  good_news_stock_down: GoodNewsReaction | null;
  capex_stress: number | null;
  earnings_stress: number | null;
  reasoning: string | null;
  source: string | null;
  fetched_at: string | null;
  /** Whether anything was actually collected. Live reading only; the snapshot's
   *  point-in-time overlay omits it. */
  observed?: boolean;
  observed_in_snapshot?: boolean;
}

export interface RegimeHistoryPoint {
  date: string;
  state: number | null;
  warning: number | null;
  state_coverage: number | null;
  warning_coverage: number | null;
  basket_hash: string | null;
}

export interface RegimeMonitor {
  available: boolean;
  reason?: string;
  methodology?: string;
  date?: string;
  state?: RegimeReading;
  warning?: RegimeReading;
  /** Point-in-time overlay recorded in the snapshot. */
  fundamental_overlay?: RegimeFundamentalOverlay;
  /** Current observation, even when it is not effective until the next session. */
  fundamental_context?: RegimeFundamentalOverlay;
  inputs?: {
    vix: number | null;
    vix_date: string | null;
    hy_oas: number | null;
    hy_oas_date: string | null;
    breadth_pct_above_200: number | null;
    breadth_date: string | null;
    fundamentals_fetched_at: string | null;
    fundamentals_effective_date: string | null;
    fundamentals_age_days: number | null;
  };
  basket?: {
    symbols: string[];
    hash: string;
    basket_asof: string;
    members_available: number | null;
    members_expected: number;
    history_kind: 'forward' | 'retrospective';
  };
  data_quality?: {
    minimum_coverage: number;
    oldest_market_input_age_days: number | null;
    stale_inputs: string[];
    inputs_fresh: boolean;
    snapshot_age_days?: number;
    is_fresh?: boolean;
    /** Upstream history spans, so a silently truncated series is visible. */
    credit_history_days?: number | null;
    vix_history_days?: number | null;
  };
  quadrant_config?: { state_divider: number; warning_divider: number; margin: number };
}

export interface RegimeFundamentals {
  methodology: 'v3';
  f1_score: number | null;
  f3_score: number | null;
  locked: boolean;
  reasoning: string | null;
  fetched_at: string | null;
  effective_date: string | null;
  source: string;
  capex: Record<string, CapexState>;
  good_news_stock_down: GoodNewsReaction;
}

export type CapexState = 'raising' | 'holding' | 'cutting' | 'unknown';
export type GoodNewsReaction = 'yes' | 'no' | 'mixed';

export interface RegimeFundamentalsUpdate {
  capex?: Record<string, CapexState>;
  good_news_stock_down?: GoodNewsReaction;
  locked?: boolean;
}

export interface RegimeConfig {
  breadth_basket: string[];
  basket_asof: string;
  fundamental_staleness_days: number;
}

// Event study — measured lead time of early-warning indicators vs. drawdowns
export interface EventStudyReport {
  available: boolean;
  reason?: string;
  methodology?: string;
  generated_at?: string;
  evaluation?: 'exploratory' | 'holdout';
  summary?: string;
  params?: {
    benchmark: string;
    outcome: string;
    event_threshold_pct: number;
    event_cooldown_days: number;
    horizon_days: number;
    train_fraction: number;
    warn_percentile: number;
    warn_threshold: number;
    basket_hash: string;
    basket_asof: string;
    credit_sensor_from?: string | null;
  };
  /** How far the headline metrics can be trusted. See _reliability(). */
  reliability?: {
    events_detected: number;
    events_in_holdout: number;
    minimum_events: number;
    underpowered: boolean;
    sensors_expected: number;
    train_full_sensor_share: number;
    holdout_full_sensor_share: number;
    sensor_coverage_mismatch: boolean;
  };
  sample?: {
    start: string;
    end: string;
    train_end: string;
    test_start: string;
    sessions: number;
    holdout_sessions: number;
  };
  metrics?: {
    events: number;
    events_warned: number;
    events_missed: number;
    alarm_episodes: number;
    false_alarms: number;
    false_alarms_per_year: number;
    median_lead_days: number | null;
  };
  events?: { date: string; warned: boolean; lead_days: number | null }[];
  recent_breadth?: { date: string; breadth: number; warning: number | null }[];
}

export interface AlertConfig {
  enabled: boolean;
  telegram_chat_id: string;
  bot_token_configured: boolean;
  bot_token_source: 'database' | 'environment' | 'none';
  qualified_enabled: boolean;
  sr_proximity_enabled: boolean;
  score_drop_enabled: boolean;
  digest_enabled: boolean;
  regime_quadrant_enabled: boolean;
  trade_closed_enabled: boolean;
}

export interface AlertTestResult {
  ok: boolean;
  error?: string;
}

export interface SentimentTestResult {
  ok: boolean;
  provider: string;
  model: string;
  ticker?: string;
  classification?: string;
  confidence?: number;
  reasoning?: string | null;
  error?: string;
}

export interface TradeTarget {
  price: number;
  distance_from_entry: number;
  distance_atr_multiple: number;
  rr_ratio: number;
  probability: number;
  classification: 'Conservative' | 'Moderate' | 'Aggressive';
  sr_level_id: number;
  sr_strength: number;
  is_primary?: boolean;
}

export interface RecommendationSummary {
  action: string;
  reasoning: string | null;
  risk_level: 'Low' | 'Medium' | 'High' | null;
  composite_score: number;
}

// S/R Levels
export interface SRLevel {
  id: number;
  price_level: number;
  type: 'support' | 'resistance';
  strength: number;
  detection_method: string;
  created_at: string;
}

export interface SRLevelResponse {
  symbol: string;
  levels: SRLevel[];
  zones: SRZone[];
  visible_levels: SRLevel[];
  count: number;
}

export interface GateTargetLevel {
  price_level: number;
  type: 'support' | 'resistance';
  strength: number;
  detection_method: string;
  sources: string[];
  traffic_count: number;
}

export interface GateTargetLadderResponse {
  symbol: string;
  levels: GateTargetLevel[];
  count: number;
  lookback_bars: number;
}

// Sentiment
export interface CitationItem {
  url: string;
  title: string;
}

export interface SentimentScore {
  id: number;
  classification: 'bullish' | 'bearish' | 'neutral';
  confidence: number;
  source: string;
  timestamp: string;
  reasoning: string;
  citations: CitationItem[];
  recommendation: 'buy' | 'hold' | 'avoid' | null;
}

export interface SentimentResponse {
  symbol: string;
  scores: SentimentScore[];
  count: number;
  dimension_score: number | null;
  lookback_hours: number;
}

// Fundamentals
export interface MetricIndustry {
  label: string;
  median: number;
  favorable_percentile: number; // 0-100, polarity-aware (higher = more favorable)
  peer_count: number;
}

export interface MetricHistoryPoint {
  period_end: string | null; // YYYY-MM-DD
  value: number | null;
}

export type MetricKey =
  | 'revenue_growth_yoy'
  | 'eps_growth_yoy'
  | 'operating_margin'
  | 'fcf_margin'
  | 'net_debt'
  | 'net_debt_to_ebitda'
  | 'share_count_change_yoy';

export interface MetricItem {
  key: MetricKey;
  value: number | null;
  history: MetricHistoryPoint[];
  industry: MetricIndustry | null;
  period_end: string | null;
  filed_date: string | null;
  caveat: string | null;
  source: string; // 'sec' | 'legacy_api'
}

export interface EarningsNext {
  date: string;
  session: string; // bmo | amc | unknown
  days_until: number;
}

export interface EarningsRecent {
  announce_date: string;
  period_end: string | null;
  eps_estimate: number | null;
  eps_actual: number | null;
  surprise_pct: number | null;
}

export interface EarningsObject {
  next: EarningsNext | null;
  recent: EarningsRecent[];
}

export interface Valuation {
  pe: number | null;
  fcf_yield: number | null;
  market_cap_est: number | null;
  pe_industry: MetricIndustry | null;
  fcf_yield_industry: MetricIndustry | null;
  price_date: string | null;
}

export interface FundamentalsReads {
  header: string | null;
  // fixed map over every metric key plus 'pe' and 'fcf_yield'; null when unavailable
  by_key: Record<string, string | null>;
}

export interface FundamentalResponse {
  symbol: string;
  // legacy fields (unchanged)
  pe_ratio: number | null;
  revenue_growth: number | null;
  earnings_surprise: number | null;
  market_cap: number | null;
  next_earnings_date: string | null;
  fetched_at: string | null;
  unavailable_fields: Record<string, string>;
  // additive v1 (SEC/Dolt-derived) — present, with null/empty when unavailable
  earnings: EarningsObject | null;
  metrics: MetricItem[] | null;
  valuation: Valuation | null;
  reads: FundamentalsReads | null;
  setup_eligible: boolean;
  setup_block_code: string | null;
  setup_block_reason: string | null;
}

// Indicators
export interface IndicatorResult {
  indicator_type: string;
  values: Record<string, unknown>;
  score: number;
  bars_used: number;
}

export interface EMACrossResult {
  short_ema: number;
  long_ema: number;
  short_period: number;
  long_period: number;
  signal: 'bullish' | 'bearish' | 'neutral';
}

// Tickers
export interface Ticker {
  id: number;
  symbol: string;
  name: string | null;
  created_at: string;
}

// Admin
export interface AdminUser {
  id: number;
  username: string;
  role: string;
  has_access: boolean;
  created_at: string | null;
  updated_at: string | null;
}

export interface SystemSetting {
  key: string;
  value: string;
  updated_at: string | null;
}

export interface RecommendationConfig {
  high_confidence_threshold: number;
  moderate_confidence_threshold: number;
  confidence_diff_threshold: number;
  signal_alignment_weight: number;
  sr_strength_weight: number;
  momentum_technical_divergence_threshold: number;
  fundamental_technical_divergence_threshold: number;
}

export type TickerUniverse = 'sp500' | 'nasdaq100' | 'nasdaq_all';

export interface TickerUniverseSetting {
  universe: TickerUniverse;
}

export interface TickerUniverseBootstrapResult {
  universe: TickerUniverse;
  /** Where the member list came from: wikipedia_sp500 | nasdaq_trader | cache | seed | … */
  source?: string;
  total_universe_symbols: number;
  added: number;
  already_tracked: number;
  deleted: number;
  /** Sample of newly added symbols (capped server-side). */
  added_symbols?: string[];
}

export interface PipelineReadiness {
  symbol: string;
  ohlcv_bars: number;
  ohlcv_last_date: string | null;
  dimensions: {
    technical: number | null;
    sr_quality: number | null;
    sentiment: number | null;
    fundamental: number | null;
    momentum: number | null;
  };
  sentiment_count: number;
  sentiment_last_at: string | null;
  has_fundamentals: boolean;
  fundamentals_fetched_at: string | null;
  sr_level_count: number;
  has_composite: boolean;
  composite_stale: boolean | null;
  trade_setup_count: number;
  missing_reasons: string[];
  ready_for_scanner: boolean;
}
