import { useQuery } from '@tanstack/react-query';
import { getIndicator, getEMACross } from '../../api/indicators';
import type { IndicatorResult } from '../../lib/types';

const INDICATOR_TYPES = ['RSI', 'ADX', 'EMA', 'ATR', 'volume_profile', 'pivot_points'] as const;

const INDICATOR_LABELS: Record<string, string> = {
  RSI: 'RSI',
  ADX: 'ADX · trend strength',
  EMA: 'EMA',
  ATR: 'ATR · volatility',
  volume_profile: 'Volume profile',
  pivot_points: 'Pivot points',
};

interface IndicatorSelectorProps {
  symbol: string;
  /** Used to sort level clusters (pivots, HVN/LVN) by distance to price. */
  currentPrice?: number;
}

const signalColors: Record<string, string> = {
  bullish: 'text-emerald-300',
  bearish: 'text-red-300',
  neutral: 'text-gray-300',
};

function fmtVal(val: number | string): string {
  if (typeof val !== 'number') return String(val);
  if (!Number.isFinite(val)) return '—';
  if (Math.abs(val) >= 1000) return val.toLocaleString('en-US', { maximumFractionDigits: 0 });
  return val.toFixed(2);
}

function prettyKey(key: string): string {
  return key.replace(/_/g, ' ');
}

/** Extract a numeric list from an array or a "[1.2, 3.4, …]"-ish string. */
function parseNums(val: unknown): number[] | null {
  if (Array.isArray(val)) {
    const ns = val.filter((x): x is number => typeof x === 'number' && Number.isFinite(x));
    return ns.length >= 2 ? ns : null;
  }
  if (typeof val === 'string') {
    const ns = (val.match(/-?\d+(?:\.\d+)?/g) ?? []).map(Number).filter(Number.isFinite);
    return ns.length >= 2 ? ns : null;
  }
  return null;
}

const MAX_CLUSTERS = 4;

/**
 * A long list of price levels (swing highs, HVN/LVN, pivots) is noise — merge
 * values within ~0.75% of price into ranges and show only the clusters nearest
 * the current price.
 */
function clusterLevels(nums: number[], refPrice?: number): { shown: string[]; more: number } {
  const sorted = [...nums].sort((a, b) => a - b);
  const mid = refPrice ?? (sorted[0] + sorted[sorted.length - 1]) / 2;
  const tol = Math.max(Math.abs(mid) * 0.0075, (sorted[sorted.length - 1] - sorted[0]) / 40, 0.01);
  const clusters: { lo: number; hi: number }[] = [];
  for (const n of sorted) {
    const last = clusters[clusters.length - 1];
    if (last && n - last.hi <= tol) last.hi = n;
    else clusters.push({ lo: n, hi: n });
  }
  if (refPrice != null) {
    clusters.sort((a, b) => {
      const da = Math.abs((a.lo + a.hi) / 2 - refPrice);
      const db = Math.abs((b.lo + b.hi) / 2 - refPrice);
      return da - db;
    });
  }
  const shown = clusters.slice(0, MAX_CLUSTERS).map((c) =>
    c.hi - c.lo < tol / 10 ? fmtVal(c.lo) : `${fmtVal(c.lo)}–${fmtVal(c.hi)}`,
  );
  return { shown, more: clusters.length - Math.min(clusters.length, MAX_CLUSTERS) };
}

/**
 * One-line human read of an indicator — same slot as EMA cross's
 * bullish/bearish/neutral. Vocab matches what the metric means (directional
 * vs regime), not forced into bullish/bearish for everything.
 */
function interpretation(
  result: IndicatorResult,
  refPrice?: number,
): { text: string; tone: string } | null {
  const v = result.values as Record<string, unknown>;
  const num = (k: string) => (typeof v[k] === 'number' ? (v[k] as number) : null);
  const type = result.indicator_type.toLowerCase();

  switch (type) {
    case 'rsi': {
      const rsi = num('rsi') ?? num('value');
      if (rsi == null) return null;
      if (rsi >= 70) return { text: 'overbought', tone: 'text-red-300' };
      if (rsi <= 30) return { text: 'oversold', tone: 'text-emerald-300' };
      if (rsi >= 55) return { text: 'bullish momentum', tone: 'text-emerald-300' };
      if (rsi <= 45) return { text: 'bearish momentum', tone: 'text-red-300' };
      return { text: 'neutral', tone: 'text-gray-400' };
    }

    case 'adx': {
      const adx = num('adx') ?? num('value');
      const plusDi = num('plus_di');
      const minusDi = num('minus_di');
      if (adx == null) return null;
      if (adx < 20) return { text: 'no trend / range', tone: 'text-gray-400' };
      if (adx < 25) return { text: 'trend forming', tone: 'text-gray-300' };
      const bullish = plusDi != null && minusDi != null && plusDi > minusDi;
      const bearish = plusDi != null && minusDi != null && minusDi > plusDi;
      if (adx >= 40) {
        if (bullish) return { text: 'strong bullish trend', tone: 'text-emerald-300' };
        if (bearish) return { text: 'strong bearish trend', tone: 'text-red-300' };
        return { text: 'strong trend', tone: 'text-emerald-300' };
      }
      if (bullish) return { text: 'bullish trend', tone: 'text-emerald-300' };
      if (bearish) return { text: 'bearish trend', tone: 'text-red-300' };
      return { text: 'trending', tone: 'text-gray-300' };
    }

    case 'ema': {
      const ema = num('ema');
      const close = num('latest_close');
      if (ema == null || close == null || ema === 0) return null;
      const pct = ((close - ema) / ema) * 100;
      if (pct >= 0.5) return { text: 'above EMA · bullish', tone: 'text-emerald-300' };
      if (pct <= -0.5) return { text: 'below EMA · bearish', tone: 'text-red-300' };
      return { text: 'at EMA · neutral', tone: 'text-gray-400' };
    }

    case 'atr': {
      const atrPct = num('atr_percent');
      if (atrPct == null) return null;
      if (atrPct < 1.5) return { text: 'compressed', tone: 'text-emerald-300' };
      if (atrPct < 3) return { text: 'normal volatility', tone: 'text-gray-400' };
      if (atrPct < 5) return { text: 'elevated', tone: 'text-amber-300' };
      return { text: 'high volatility', tone: 'text-red-300' };
    }

    case 'volume_profile': {
      const vaLow = num('value_area_low');
      const vaHigh = num('value_area_high');
      const poc = num('poc');
      const price = refPrice ?? num('latest_close');
      if (price == null || vaLow == null || vaHigh == null) return null;
      if (price > vaHigh) return { text: 'above value · extended', tone: 'text-amber-300' };
      if (price < vaLow) return { text: 'below value · discounted', tone: 'text-emerald-300' };
      // Inside value area: near POC vs mid-range.
      if (poc != null) {
        const span = Math.max(vaHigh - vaLow, Math.abs(price) * 0.001);
        if (Math.abs(price - poc) / span <= 0.15) {
          return { text: 'at value · balanced', tone: 'text-gray-300' };
        }
      }
      return { text: 'in value area', tone: 'text-gray-400' };
    }

    case 'pivot_points': {
      const price = refPrice;
      if (price == null || !(price > 0)) return null;
      const highs = parseNums(v.swing_highs) ?? [];
      const lows = parseNums(v.swing_lows) ?? [];
      // parseNums requires ≥2 elements; allow singletons for pivots.
      const rawHighs = Array.isArray(v.swing_highs)
        ? (v.swing_highs as unknown[]).filter((x): x is number => typeof x === 'number')
        : highs;
      const rawLows = Array.isArray(v.swing_lows)
        ? (v.swing_lows as unknown[]).filter((x): x is number => typeof x === 'number')
        : lows;
      if (rawHighs.length === 0 && rawLows.length === 0) return null;
      const near = (level: number) => Math.abs(level - price) / price <= 0.02;
      const nearHigh = rawHighs.some(near);
      const nearLow = rawLows.some(near);
      if (nearHigh && nearLow) return { text: 'congested structure', tone: 'text-gray-400' };
      if (nearLow) return { text: 'near support pivot', tone: 'text-emerald-300' };
      if (nearHigh) return { text: 'near resistance pivot', tone: 'text-red-300' };
      return { text: 'between pivots', tone: 'text-gray-400' };
    }

    default:
      return null;
  }
}

/** One key/value line — level lists collapse to nearest-first range chips. */
function ValueRow({ name, val, refPrice }: { name: string; val: unknown; refPrice?: number }) {
  const nums = parseNums(val);
  if (nums) {
    const { shown, more } = clusterLevels(nums, refPrice);
    return (
      <div className="text-xs">
        <dt className="text-gray-500">{prettyKey(name)} <span className="text-gray-600">· nearest first</span></dt>
        <dd className="num mt-1 flex flex-wrap gap-1.5 text-gray-200">
          {shown.map((r) => (
            <span key={r} className="rounded bg-white/[0.05] px-1.5 py-0.5">{r}</span>
          ))}
          {more > 0 && <span className="self-center text-gray-600">+{more} more</span>}
        </dd>
      </div>
    );
  }
  return (
    <div className="flex items-baseline justify-between gap-3 text-xs">
      <dt className="shrink-0 text-gray-500">{prettyKey(name)}</dt>
      <dd className="num min-w-0 break-all text-right text-gray-200">{fmtVal(val as number | string)}</dd>
    </div>
  );
}

/** One indicator, fetched independently — a quiet outlined card. */
function IndicatorCard({ symbol, type, refPrice }: { symbol: string; type: string; refPrice?: number }) {
  const query = useQuery({
    queryKey: ['indicator', symbol, type],
    queryFn: () => getIndicator(symbol, type),
    staleTime: 5 * 60_000,
    retry: 1,
  });

  return (
    <div className="rounded-xl border border-white/[0.07] p-4">
      <div className="flex items-baseline justify-between gap-2">
        <h4 className="num text-[10px] uppercase tracking-[0.16em] text-gray-500">
          {INDICATOR_LABELS[type] ?? type}
        </h4>
        {query.data && (
          <span className="num text-[10px] text-gray-600" title={`${query.data.bars_used} bars used · normalized score`}>
            score {query.data.score.toFixed(2)}
          </span>
        )}
      </div>

      {query.isLoading && (
        <div className="mt-3 animate-pulse space-y-2">
          <div className="h-3.5 w-3/4 rounded bg-white/[0.05]" />
          <div className="h-3.5 w-1/2 rounded bg-white/[0.05]" />
        </div>
      )}
      {query.isError && (
        <p className="mt-3 text-xs text-gray-500">
          Not available{query.error instanceof Error && query.error.message ? ` — ${query.error.message}` : ''}.
        </p>
      )}
      {query.data && (
        <>
          {(() => {
            const read = interpretation(query.data, refPrice);
            return read ? (
              <p className={`mt-1.5 text-sm font-semibold ${read.tone}`}>{read.text}</p>
            ) : null;
          })()}
          <dl className="mt-2.5 space-y-1.5">
            {Object.entries(query.data.values).map(([key, val]) => (
              <ValueRow key={key} name={key} val={val} refPrice={refPrice} />
            ))}
            {Object.keys(query.data.values).length === 0 && (
              <p className="text-xs text-gray-500">No values.</p>
            )}
          </dl>
        </>
      )}
    </div>
  );
}

/** EMA cross gets its own card — it carries a directional signal. */
function EMACrossCard({ symbol }: { symbol: string }) {
  const query = useQuery({
    queryKey: ['ema-cross', symbol],
    queryFn: () => getEMACross(symbol),
    staleTime: 5 * 60_000,
    retry: 1,
  });

  return (
    <div className="rounded-xl border border-white/[0.07] p-4">
      <h4 className="num text-[10px] uppercase tracking-[0.16em] text-gray-500">EMA cross</h4>
      {query.isLoading && (
        <div className="mt-3 animate-pulse space-y-2">
          <div className="h-3.5 w-3/4 rounded bg-white/[0.05]" />
          <div className="h-3.5 w-1/2 rounded bg-white/[0.05]" />
        </div>
      )}
      {query.isError && <p className="mt-3 text-xs text-gray-500">Not available.</p>}
      {query.data && (
        <>
          <p className={`mt-1.5 text-sm font-semibold ${signalColors[query.data.signal] ?? 'text-gray-300'}`}>
            {query.data.signal}
          </p>
          <dl className="mt-2.5 space-y-1">
            <div className="flex items-baseline justify-between gap-3 text-xs">
              <dt className="text-gray-500">short EMA ({query.data.short_period})</dt>
              <dd className="num text-gray-200">{query.data.short_ema.toFixed(2)}</dd>
            </div>
            <div className="flex items-baseline justify-between gap-3 text-xs">
              <dt className="text-gray-500">long EMA ({query.data.long_period})</dt>
              <dd className="num text-gray-200">{query.data.long_ema.toFixed(2)}</dd>
            </div>
          </dl>
        </>
      )}
    </div>
  );
}

/** All indicators at once — no dropdown, every value visible or one card away. */
export function IndicatorSelector({ symbol, currentPrice }: IndicatorSelectorProps) {
  return (
    <div className="glass p-5">
      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
        <EMACrossCard symbol={symbol} />
        {INDICATOR_TYPES.map((type) => (
          <IndicatorCard key={type} symbol={symbol} type={type} refPrice={currentPrice} />
        ))}
      </div>
    </div>
  );
}
