import * as vscode from 'vscode';
import { AIService } from '../../core/services';
import { logError, logInfo } from '../../utils';
import { TELEGRAM_TOKEN_SECRET_KEY } from '../../extension/telegramCommands';
import { TelegramHttpClient } from '../../integrations/telegram/telegramClient';
import type { DiscoveredCandidate } from './stockDiscovery';

/**
 * Discover 결과를 LLM 에게 던져 *매력도 Top 5* 추출 + 채팅/텔레그램 발송.
 *
 * 호출 흐름 (slashStocks 의 cmdDiscover 끝에 자동 chain):
 *   1) buildAnalysisPrompt(candidates)  — 20개 후보 데이터를 LLM 입력 형식으로 직렬화
 *   2) AIService.chat(...)              — Astra 의 기본 모델로 평가
 *   3) parseTopFive(output)             — LLM 응답을 구조화된 5개 항목으로 파싱 (관대)
 *   4) renderForChat / renderForTelegram — 사용자에게 보일 메시지 두 가지 형식
 *   5) sendToTelegram(...)              — 텔레그램 chatId 로 발송 (실패 silent)
 *
 * 작은 모델 (gemma 4B 등) 이 형식을 흩뜨려도 깨지지 않게 — parseTopFive 는 *느슨한*
 * 번호 매칭으로 5개 라인만 추출, 실패 시 raw text 그대로 fallback.
 */

export interface TopFiveItem {
    rank: number;
    name: string;
    symbol: string;
    /** LLM 이 한 줄로 정리한 매력 포인트. */
    pitch: string;
    /** 원본 후보 (선택적으로 ROE/영업이익률 등 인용용). */
    candidate?: DiscoveredCandidate;
}

export interface TopFiveResult {
    items: TopFiveItem[];
    /** LLM 의 종합 코멘트 (1-2 문장). 없을 수 있음. */
    summary?: string;
    /** LLM 응답 원문 (디버그 / fallback 출력용). */
    raw: string;
}

const SYSTEM_PROMPT = [
    '당신은 한국 주식 발굴 결과를 검토하는 가치 투자 분석가다.',
    '제공된 후보 종목들의 *재무 지표* 와 *통과 키워드* 를 보고 가장 매력적인 5개를 골라라.',
    '',
    '**평가 기준 (우선순위 순):**',
    '  1. 통과 키워드 수 — 많을수록 우수 (3개 통과 < 5개 통과 < 6개 통과)',
    '  2. ROE 절대 수치 — 15% 이상 강하게 가산',
    '  3. 영업이익률 — 20% 이상 가산 (가격 결정력 + 마진 안정)',
    '  4. 유보율 — 1,000% 이상 통과, 3,000% 이상 안정성 가산',
    '  5. PBR — 낮을수록 가산, 단 1.0 미만은 *value trap* 가능성 cross-check',
    '',
    '**출력 형식 (반드시 이대로):**',
    '🎯 매력도 Top 5',
    '',
    '1. <종목명> (<6자리 심볼>) — <한 줄 매력 포인트 30자 이내>',
    '   근거: ROE x%, 영업이익률 y%, 유보율 z%, <통과 N키워드 인용>',
    '2. ...',
    '...',
    '5. ...',
    '',
    '종합: <1-2 문장 — 이번 발굴 batch 의 공통 특징 또는 주의점>',
    '',
    '*다른 텍스트 절대 추가 금지.* 출력 첫 줄은 정확히 "🎯 매력도 Top 5" 이어야 한다.',
].join('\n');

function buildAnalysisPrompt(candidates: DiscoveredCandidate[]): string {
    const lines: string[] = [
        `발굴 후보 ${candidates.length}개. 매력도 Top 5 골라라.`,
        '',
    ];
    for (const c of candidates) {
        const f = c.fundamentals;
        lines.push(
            `· ${c.name} (${c.symbol}) [${c.market} · ${c.marketCapEok.toLocaleString()}억]`,
            `    ROE ${f.roe?.toFixed(1) ?? '-'}% · 영업이익률 ${f.operatingMargin?.toFixed(1) ?? '-'}% · 유보율 ${f.retentionRatio?.toLocaleString() ?? '-'}% · 부채비율 ${f.debtRatio?.toFixed(1) ?? '-'}% · PER ${f.per?.toFixed(1) ?? '-'} · PBR ${f.pbr?.toFixed(1) ?? '-'}`,
            `    통과 (${c.passedKeywords.length}): ${c.passedKeywords.join(', ')}`,
            '',
        );
    }
    return lines.join('\n');
}

/**
 * LLM 응답을 5개 항목으로 파싱. 작은 모델이 형식 흩뜨려도 잡힐 수 있게 관대하게:
 *   - "1. <이름> (<6자리>) — <문구>" / "1) <이름> ..." / "1: ..." 모두 받음
 *   - 종목 매핑은 6자리 심볼 정규식으로 cross-check
 */
function parseTopFive(raw: string, candidates: DiscoveredCandidate[]): TopFiveResult {
    const items: TopFiveItem[] = [];
    const lines = raw.split('\n');
    const symbolMap = new Map(candidates.map(c => [c.symbol, c]));

    const itemRe = /^\s*([1-5])[\.\)\:]\s+(.+?)\s*[\(\[](\d{6})[\)\]]\s*[—\-:]\s*(.+)$/;
    for (const line of lines) {
        const m = line.match(itemRe);
        if (!m) continue;
        const rank = parseInt(m[1], 10);
        if (items.find(i => i.rank === rank)) continue;
        items.push({
            rank,
            name: m[2].trim(),
            symbol: m[3],
            pitch: m[4].trim(),
            candidate: symbolMap.get(m[3]),
        });
    }
    items.sort((a, b) => a.rank - b.rank);

    // 종합 코멘트 추출 — "종합:" 또는 "총평:" 라인.
    const summaryMatch = raw.match(/(?:종합|총평)\s*[:：]\s*(.+?)(?:\n\n|$)/s);
    const summary = summaryMatch ? summaryMatch[1].replace(/\s+/g, ' ').trim() : undefined;

    return { items, summary, raw };
}

export async function analyzeTopCandidates(
    candidates: DiscoveredCandidate[],
    onProgress?: (msg: string) => void,
): Promise<TopFiveResult> {
    if (candidates.length === 0) {
        return { items: [], raw: '' };
    }
    onProgress?.('🤖 LLM 분석 시작 — 후보 ' + candidates.length + '개 평가 중...');
    const ai = new AIService();
    try {
        const result = await ai.chat({
            system: SYSTEM_PROMPT,
            user: buildAnalysisPrompt(candidates),
            timeoutMs: 90_000,
        });
        if (result.empty || !result.content.trim()) {
            logError('Discovery analyzer: LLM 빈 응답.');
            return { items: [], raw: '' };
        }
        const parsed = parseTopFive(result.content, candidates);
        logInfo('Discovery analyzer: parsed Top 5.', {
            parsedCount: parsed.items.length,
            model: result.model,
        });
        return parsed;
    } catch (e: any) {
        logError('Discovery analyzer: LLM 호출 실패.', { error: e?.message ?? String(e) });
        return { items: [], raw: '' };
    }
}

/** 채팅 webview 용 — markdown 친화적 멀티라인. */
export function renderTopFiveForChat(result: TopFiveResult): string {
    if (result.items.length === 0) {
        return result.raw
            ? `\n🤖 **LLM 분석 결과** (형식 파싱 실패 — 원문 표시)\n\n${result.raw}\n`
            : '\n⚠️ LLM 분석 실패 (빈 응답 또는 timeout).\n';
    }
    const lines: string[] = ['\n🎯 **Astra 매력도 Top 5**\n'];
    for (const it of result.items) {
        lines.push(`${it.rank}. **${it.name}** (${it.symbol}) — ${it.pitch}`);
        if (it.candidate) {
            const f = it.candidate.fundamentals;
            lines.push(`   근거: ROE ${f.roe?.toFixed(1) ?? '-'}%, 영업이익률 ${f.operatingMargin?.toFixed(1) ?? '-'}%, 유보율 ${f.retentionRatio?.toLocaleString() ?? '-'}%, 통과 ${it.candidate.passedKeywords.length}개 (${it.candidate.passedKeywords.join(', ')})`);
        }
    }
    if (result.summary) {
        lines.push('', `💬 ${result.summary}`);
    }
    return lines.join('\n') + '\n';
}

/** 텔레그램용 — Markdown V1, 짧고 깔끔. 4096자 제한 안에서. */
function pickChatIdForReport(): number | null {
    const cfg = vscode.workspace.getConfiguration('g1nation');
    const allowed = cfg.get<number[]>('telegram.allowedChatIds', []) || [];
    if (allowed.length > 0 && Number.isFinite(allowed[0]) && allowed[0] !== 0) {
        return allowed[0];
    }
    const fallback = cfg.get<number>('stocks.telegramChatId', 0);
    return fallback && Number.isFinite(fallback) ? fallback : null;
}

function formatKstNow(): string {
    const fmt = new Intl.DateTimeFormat('ko-KR', {
        timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit',
        hour: '2-digit', minute: '2-digit', hour12: false,
    });
    const parts = fmt.formatToParts(new Date());
    const get = (t: string) => parts.find(p => p.type === t)?.value || '';
    return `${get('year')}-${get('month')}-${get('day')} ${get('hour')}:${get('minute')} KST`;
}

export function renderTopFiveForTelegram(
    result: TopFiveResult,
    rangeLabel: string,
): string {
    if (result.items.length === 0) {
        return `🎯 *Astra 발굴 Top 5* (${rangeLabel})\n${formatKstNow()}\n\n⚠️ LLM 분석 실패 — 채팅 창에서 raw 결과 확인.`;
    }
    const lines: string[] = [
        `🎯 *Astra 발굴 Top 5* (${rangeLabel})`,
        formatKstNow(),
        '',
    ];
    for (const it of result.items) {
        lines.push(`${it.rank}\\. *${it.name}* (\`${it.symbol}\`)`);
        lines.push(`   ${it.pitch}`);
        if (it.candidate) {
            const f = it.candidate.fundamentals;
            lines.push(`   ROE ${f.roe?.toFixed(1) ?? '-'}% · OM ${f.operatingMargin?.toFixed(1) ?? '-'}% · 유보 ${f.retentionRatio?.toLocaleString() ?? '-'}%`);
        }
        lines.push('');
    }
    if (result.summary) {
        lines.push(`💬 ${result.summary}`);
    }
    const joined = lines.join('\n');
    return joined.length > 3800 ? joined.slice(0, 3800) + '\n…(잘림)' : joined;
}

export async function sendTopFiveToTelegram(
    context: vscode.ExtensionContext,
    text: string,
): Promise<{ ok: boolean; reason?: string }> {
    const chatId = pickChatIdForReport();
    if (chatId === null) {
        return { ok: false, reason: '텔레그램 chatId 미설정 (allowedChatIds 또는 stocks.telegramChatId)' };
    }
    const token = (await context.secrets.get(TELEGRAM_TOKEN_SECRET_KEY)) || '';
    if (!token.trim()) {
        return { ok: false, reason: '텔레그램 봇 토큰 없음' };
    }
    const client = new TelegramHttpClient({ getToken: () => token });
    try {
        await client.sendMessage({ chatId, text, parseMode: 'Markdown' });
        logInfo('Top 5 텔레그램 발송 완료.', { chatId, chars: text.length });
        return { ok: true };
    } catch (e: any) {
        logError('Top 5 텔레그램 발송 실패.', { chatId, error: e?.message ?? String(e) });
        return { ok: false, reason: e?.message ?? String(e) };
    }
}
