import type { AlertNotificationDto } from '../services/alertApi';
import { extractStockStrategyPairs } from './alertMessageParser';

/** Jackson 등에서 LocalDateTime이 배열로 올 때 ISO 문자열로 변환 */
export function normalizeAlertDateTime(raw: unknown): string | null {
  if (raw == null) return null;
  if (typeof raw === 'string' && raw.trim()) return raw.trim();
  if (Array.isArray(raw) && raw.length >= 3) {
    const y = Number(raw[0]);
    const mo = Number(raw[1]);
    const d = Number(raw[2]);
    const h = raw.length > 3 ? Number(raw[3]) : 0;
    const mi = raw.length > 4 ? Number(raw[4]) : 0;
    const se = raw.length > 5 ? Number(raw[5]) : 0;
    if (![y, mo, d, h, mi, se].every((n) => Number.isFinite(n))) return null;
    const dt = new Date(y, mo - 1, d, h, mi, se);
    return Number.isFinite(dt.getTime()) ? dt.toISOString() : null;
  }
  return null;
}

/** 알림 발생 캔들 시각(가능하면 candleTime, 없으면 triggeredAt) */
export function alertMarkerTimeIsoFromDto(n: Pick<AlertNotificationDto, 'triggeredAt' | 'candleTime'>): string | null {
  const fromCandle = normalizeAlertDateTime(n.candleTime as unknown);
  if (fromCandle) return fromCandle;
  const fromTrig = normalizeAlertDateTime(n.triggeredAt);
  if (fromTrig) return fromTrig;
  return null;
}

/** 전략명 문자열만 보고 매수/매도 쪽 알림인지 추정 */
export function inferAlertSignalFromStrategyName(strategyName: string): 'BUY' | 'SELL' | null {
  const s = (strategyName || '').trim();
  if (!s) return null;
  const hint = s.replace(/\s/g, '');
  if (/매도$/.test(hint) && !/과매도$/.test(hint)) return 'SELL';
  if (/매수$/.test(hint) && !/과매수$/.test(hint)) return 'BUY';
  if (s.includes('데드크로스')) return 'SELL';
  if (s.includes('골든크로스')) return 'BUY';
  return null;
}

/**
 * 메시지/조건 설명·전략명에서 매수 알림 vs 매도 알림 추정.
 * 통합 알림이면 `koreanName`에 해당하는 줄의 전략명만 사용하는 것이 가장 정확함.
 */
export function inferAlertSignalFromNotification(
  n: Pick<AlertNotificationDto, 'message' | 'conditionDescription'>,
  koreanNameForRow?: string
): 'BUY' | 'SELL' | null {
  if (koreanNameForRow?.trim()) {
    const pairs = extractStockStrategyPairs(n.message || '');
    const pair = pairs?.find((p) => p.koreanName.trim() === koreanNameForRow.trim());
    if (pair?.strategyName) {
      const fromPair = inferAlertSignalFromStrategyName(pair.strategyName);
      if (fromPair) return fromPair;
    }
  }

  const raw = `${n.message || ''}\n${n.conditionDescription || ''}`;
  const pairsAll = extractStockStrategyPairs(n.message || '');
  if (pairsAll && pairsAll.length === 1) {
    const s = inferAlertSignalFromStrategyName(pairsAll[0].strategyName);
    if (s) return s;
  }

  const hint = raw.replace(/\s/g, '');
  if (/매도$/.test(hint) && !/과매도$/.test(hint)) return 'SELL';
  if (/매수$/.test(hint) && !/과매수$/.test(hint)) return 'BUY';
  if (raw.includes('데드크로스')) return 'SELL';
  if (raw.includes('골든크로스')) return 'BUY';

  const masked = raw.replace(/과매도/g, '').replace(/과매수/g, '');
  if (/매도조건|매도신호/.test(masked) && !/매수조건|매수신호/.test(masked)) return 'SELL';
  if (/매수조건|매수신호/.test(masked) && !/매도조건|매도신호/.test(masked)) return 'BUY';

  return null;
}

function candleTimeToMs(c: { time: string | number }): number {
  if (typeof c.time === 'number') {
    return c.time >= 1e12 ? c.time : c.time * 1000;
  }
  return new Date(c.time).getTime();
}

/** 알림 시각이 속한 봉(또는 직전 봉)에 맞춰 lightweight-charts용 Unix 초 */
export function pickMarkerUnixSecondsForAlert<C extends { time: string | number }>(
  chartData: C[],
  alertTimeIso: string
): number | null {
  if (!chartData.length) return null;
  const alertMs = new Date(alertTimeIso).getTime();
  if (!Number.isFinite(alertMs)) return null;

  let best: C | null = null;
  let bestMs = -Infinity;
  for (const c of chartData) {
    const t = candleTimeToMs(c);
    if (!Number.isFinite(t)) continue;
    if (t <= alertMs && t > bestMs) {
      bestMs = t;
      best = c;
    }
  }
  const pick = best ?? chartData[0];
  const sec = Math.floor(candleTimeToMs(pick) / 1000);
  return Number.isFinite(sec) ? sec : null;
}
