export interface SmaSpec {
  kind: 'sma';
  field: string;
  period: number;
  timeframe: string | null;
}

export interface EmaSpec {
  kind: 'ema';
  field: string;
  period: number;
  timeframe: string | null;
}

export interface SupertrendSpec {
  kind: 'supertrend';
  period: number;
  multiplier: number;
  timeframe: string | null;
}

export interface BollingerSpec {
  kind: 'bollinger';
  field: string;
  period: number;
  multiplier: number;
  timeframe: string | null;
}

export type IndicatorSpec = SmaSpec | EmaSpec | SupertrendSpec | BollingerSpec;

function walkExpr(expr: unknown, specs: IndicatorSpec[]): void {
  if (!expr || typeof expr !== 'object') return;
  const e = expr as Record<string, unknown>;

  if (e.kind === 'func') {
    if (e.name === 'sma' || e.name === 'ema') {
      const field = typeof e.field === 'string' ? e.field : 'close';
      const period = typeof e.period === 'number' ? e.period : 0;
      const timeframe = typeof e.timeframe === 'string' ? e.timeframe : null;
      if (period > 0) {
        specs.push({ kind: e.name as 'sma' | 'ema', field, period, timeframe });
      }
      return;
    }
    if (e.name === 'supertrend') {
      const period = typeof e.period === 'number' ? e.period : 0;
      const multiplier = typeof e.multiplier === 'string' ? parseFloat(e.multiplier) : 3.0;
      const timeframe = typeof e.timeframe === 'string' ? e.timeframe : null;
      if (period > 0) {
        specs.push({ kind: 'supertrend', period, multiplier: isNaN(multiplier) ? 3.0 : multiplier, timeframe });
      }
      return;
    }
    if (e.name === 'bollinger_upper' || e.name === 'bollinger_lower') {
      const field = typeof e.field === 'string' ? e.field : 'close';
      const period = typeof e.period === 'number' ? e.period : 0;
      const multiplier = typeof e.multiplier === 'string' ? parseFloat(e.multiplier) : 2.0;
      const timeframe = typeof e.timeframe === 'string' ? e.timeframe : null;
      if (period > 0) {
        specs.push({ kind: 'bollinger', field, period, multiplier: isNaN(multiplier) ? 2.0 : multiplier, timeframe });
      }
      return;
    }
    return;
  }

  if (e.kind === 'bin_op' || e.kind === 'apply_func') {
    walkExpr(e.left, specs);
    walkExpr(e.right, specs);
    walkExpr(e.inner, specs);
    walkExpr(e.input, specs);
    return;
  }

  // Field or literal — no indicators inside
}

function walkCondition(cond: unknown, specs: IndicatorSpec[]): void {
  if (!cond || typeof cond !== 'object') return;
  const c = cond as Record<string, unknown>;

  switch (c.kind) {
    case 'all_of':
    case 'any_of':
      if (Array.isArray(c.conditions)) {
        c.conditions.forEach((child) => walkCondition(child, specs));
      }
      break;
    case 'not':
      walkCondition(c.condition, specs);
      break;
    case 'compare':
    case 'cross_over':
    case 'cross_under':
      walkExpr(c.left, specs);
      walkExpr(c.right, specs);
      break;
    case 'event_count':
      walkCondition(c.condition, specs);
      break;
    case 'ema_crossover':
    case 'ema_trend':
    case 'rsi':
    case 'position':
    case 'price_level':
      break;
    case 'bollinger': {
      const period = typeof c.period === 'number' ? c.period : 0;
      const rawMult = c.num_std_dev;
      const multiplier = typeof rawMult === 'string'
        ? parseFloat(rawMult)
        : typeof rawMult === 'number' ? rawMult : 2.0;
      if (period > 0) {
        specs.push({ kind: 'bollinger', field: 'close', period, multiplier: isNaN(multiplier) ? 2.0 : multiplier, timeframe: null });
      }
      break;
    }
  }
}

export function extractIndicatorSpecs(strategyConfig: unknown): IndicatorSpec[] {
  if (!strategyConfig || typeof strategyConfig !== 'object') return [];
  const cfg = strategyConfig as Record<string, unknown>;

  if (cfg.type !== 'rule_based') return [];

  const rules = Array.isArray(cfg.rules) ? cfg.rules : [];
  const raw: IndicatorSpec[] = [];

  for (const rule of rules) {
    if (rule && typeof rule === 'object') {
      const r = rule as Record<string, unknown>;
      walkCondition(r.when, raw);
    }
  }

  // Deduplicate
  const seen = new Set<string>();
  return raw.filter((s) => {
    const key = s.kind === 'supertrend'
      ? `supertrend:${s.period}:${s.multiplier}:${s.timeframe ?? ''}`
      : s.kind === 'bollinger'
        ? `bollinger:${s.field}:${s.period}:${s.multiplier}:${s.timeframe ?? ''}`
        : `${s.kind}:${s.field}:${s.period}:${s.timeframe ?? ''}`;
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

/** @deprecated Use extractIndicatorSpecs instead. */
export function extractSmaSpecs(strategyConfig: unknown): SmaSpec[] {
  return extractIndicatorSpecs(strategyConfig).filter((s): s is SmaSpec => s.kind === 'sma');
}
