import { logError, logInfo } from '../../utils';
import type { Fundamentals } from './naverFundamentals';

/**
 * 미국 종목 펀더멘털 — Yahoo Finance `quoteSummary` (무키, 비공식).
 *
 * Naver 가 한국 종목용이듯, 미국 종목은 Yahoo quoteSummary 가 ROE/PER/PBR/마진/
 * 성장률/부채를 한 번에 준다. API 키 불필요. 단, 최근 Yahoo 가 quoteSummary 에
 * **crumb + cookie** 인증을 요구한다(차트 v8 endpoint 와 달리). 그래서:
 *   1. fc.yahoo.com 또는 finance.yahoo.com 에서 Set-Cookie(A1/A3) 확보
 *   2. /v1/test/getcrumb 으로 cookie 와 짝이 되는 crumb 토큰 확보
 *   3. quoteSummary?crumb=… 호출 (Cookie 헤더 동봉)
 * cookie+crumb 는 모듈 레벨에 캐시하고, 401/"Invalid Crumb" 면 1회 재발급 후 재시도.
 *
 * 반환은 Naver 경로와 동일한 Fundamentals 모양 — criteriaEval/analysis 가 그대로 재사용.
 * 단위 변환: Yahoo 비율은 소수(0.15=15%) → ×100. debtToEquity 는 이미 %(D/E).
 *
 * Caveat: 비공식 — Yahoo 가 막거나 crumb 흐름을 바꾸면 깨질 수 있음. 개인 학습용 가정.
 */

const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
const MODULES = ['financialData', 'defaultKeyStatistics', 'summaryDetail', 'price', 'assetProfile'].join(',');

interface Session { cookie: string; crumb: string; }
let cachedSession: Session | null = null;

/** Set-Cookie 헤더(들)에서 name=value 쌍만 추출해 Cookie 헤더 문자열로 합성. */
function cookieHeaderFrom(res: Response): string {
    // Node18 undici: getSetCookie() 가 배열 반환(권장). 구버전 폴백: 단일 헤더 split.
    const anyHeaders = res.headers as any;
    let raw: string[] = [];
    if (typeof anyHeaders.getSetCookie === 'function') {
        raw = anyHeaders.getSetCookie();
    } else {
        const single = res.headers.get('set-cookie');
        if (single) raw = single.split(/,(?=\s*\w+=)/); // "A=1, B=2" 대략 분리
    }
    return raw.map(c => c.split(';')[0].trim()).filter(Boolean).join('; ');
}

/** cookie + crumb 발급. 실패 시 null. */
async function establishSession(timeoutMs: number): Promise<Session | null> {
    let cookie = '';
    for (const url of ['https://fc.yahoo.com/', 'https://finance.yahoo.com/']) {
        try {
            const res = await fetch(url, {
                headers: { 'User-Agent': UA, 'Accept': 'text/html' },
                signal: AbortSignal.timeout(timeoutMs),
                redirect: 'follow',
            });
            const c = cookieHeaderFrom(res);
            if (c) { cookie = c; break; }
        } catch { /* 다음 후보 */ }
    }
    if (!cookie) {
        logError('Yahoo quoteSummary: cookie 확보 실패.');
        return null;
    }
    try {
        const res = await fetch('https://query1.finance.yahoo.com/v1/test/getcrumb', {
            headers: { 'User-Agent': UA, 'Accept': 'text/plain', 'Cookie': cookie },
            signal: AbortSignal.timeout(timeoutMs),
        });
        if (!res.ok) {
            // 429(rate limit)/403 등 — body 가 "Too Many Requests" 같은 에러 문구일 수 있음.
            logError('Yahoo quoteSummary: getcrumb HTTP 오류.', { status: res.status });
            return null;
        }
        const crumb = (await res.text()).trim();
        // 진짜 crumb 은 ~11자, 공백 없음. "Too Many Requests"/HTML/빈 문자열은 거부 —
        // 공백이 있거나 너무 길면(>24) 에러 응답을 crumb 으로 오인하지 않도록 차단.
        if (!crumb || crumb.length > 24 || /\s/.test(crumb) || crumb.includes('<')) {
            logError('Yahoo quoteSummary: crumb 형식 비정상 — 거부.', { sample: crumb.slice(0, 40) });
            return null;
        }
        return { cookie, crumb };
    } catch (e: any) {
        logError('Yahoo quoteSummary: crumb fetch 예외.', { error: e?.message ?? String(e) });
        return null;
    }
}

async function getSession(timeoutMs: number, force = false): Promise<Session | null> {
    if (cachedSession && !force) return cachedSession;
    cachedSession = await establishSession(timeoutMs);
    return cachedSession;
}

/** quoteSummary 필드는 { raw, fmt } 객체 — raw 숫자만 안전 추출. */
function raw(node: any): number | undefined {
    if (node === undefined || node === null) return undefined;
    if (typeof node === 'number') return Number.isFinite(node) ? node : undefined;
    const r = node.raw;
    return typeof r === 'number' && Number.isFinite(r) ? r : undefined;
}

/** 소수 비율(0.153) → 퍼센트(15.3). undefined 보존. */
function pct(node: any): number | undefined {
    const v = raw(node);
    return v === undefined ? undefined : v * 100;
}

async function fetchQuoteSummary(symbol: string, timeoutMs: number): Promise<any | null> {
    for (let attempt = 0; attempt < 2; attempt++) {
        const session = await getSession(timeoutMs, attempt > 0);
        if (!session) return null;
        const url = `https://query1.finance.yahoo.com/v10/finance/quoteSummary/${encodeURIComponent(symbol)}`
            + `?modules=${MODULES}&crumb=${encodeURIComponent(session.crumb)}`;
        try {
            const res = await fetch(url, {
                headers: { 'User-Agent': UA, 'Accept': 'application/json', 'Cookie': session.cookie },
                signal: AbortSignal.timeout(timeoutMs),
            });
            if (res.status === 401 || res.status === 403) {
                cachedSession = null; // crumb 만료 → 다음 attempt 에서 재발급
                continue;
            }
            if (res.status === 429) {
                // rate limit — 즉시 재시도는 무의미. 세션 무효화 후 null(호출자가 안내·폴백).
                cachedSession = null;
                logError('Yahoo quoteSummary: 429 rate limit.', { symbol });
                return null;
            }
            if (!res.ok) {
                logError('Yahoo quoteSummary HTTP 오류.', { symbol, status: res.status });
                return null;
            }
            const data: any = await res.json();
            const result = data?.quoteSummary?.result?.[0];
            if (!result) {
                const errDesc = data?.quoteSummary?.error?.description;
                if (errDesc && /crumb/i.test(errDesc) && attempt === 0) { cachedSession = null; continue; }
                return null;
            }
            return result;
        } catch (e: any) {
            logError('Yahoo quoteSummary fetch 예외.', { symbol, error: e?.message ?? String(e) });
            return null;
        }
    }
    return null;
}

/** 미국 종목 1개의 펀더멘털. 실패 시 null. */
export async function fetchYahooFundamentals(symbol: string, timeoutMs = 10000): Promise<Fundamentals | null> {
    const r = await fetchQuoteSummary(symbol, timeoutMs);
    if (!r) return null;

    const fd = r.financialData ?? {};
    const ks = r.defaultKeyStatistics ?? {};
    const sd = r.summaryDetail ?? {};
    const pr = r.price ?? {};
    const ap = r.assetProfile ?? {};

    const out: Fundamentals = {
        symbol,
        market: 'US',
        currency: (pr.currency || fd.financialCurrency || 'USD') as 'USD',
        source: 'Yahoo quoteSummary',
        roe: pct(fd.returnOnEquity),
        operatingMargin: pct(fd.operatingMargins),
        // 유보율(retentionRatio)은 미국 미보고 — undefined 유지(criteriaEval 이 market 로 대체 처리).
        debtRatio: raw(fd.debtToEquity),                 // 이미 % (D/E)
        revenueGrowthYoY: pct(fd.revenueGrowth),
        opProfitGrowthYoY: pct(fd.earningsGrowth),        // 영업이익 성장 근사치
        per: raw(sd.trailingPE) ?? raw(ks.trailingPE),
        pbr: raw(ks.priceToBook),
        eps: raw(ks.trailingEps),
        currentPrice: raw(fd.currentPrice) ?? raw(pr.regularMarketPrice),
        marketCapNative: raw(pr.marketCap) ?? raw(sd.marketCap),
        sectorHint: ap.sector || ap.industry || undefined,
    };
    logInfo('Yahoo quoteSummary OK.', { symbol, roe: out.roe, per: out.per, pbr: out.pbr });
    return out;
}
