"""Trace 분석 서비스 — 유사 trace 검색, 클러스터 요약."""

import logging
from typing import Any

import sentinel.config as config
from sentinel.integrations.langfuse_gateway import LangfuseGateway

logger = logging.getLogger("sentinel.services.trace_analysis")


class TraceAnalysisService:
    """Trace 분석 기능을 제공합니다."""

    def __init__(self, gateway: LangfuseGateway | None = None):
        self.gw = gateway or LangfuseGateway()

    def find_similar_traces(
        self, seed_trace_id: str, limit: int = 10
    ) -> list[dict]:
        """seed trace와 유사한 trace들을 검색합니다.

        name/tags/level을 기준으로 후보를 검색하고
        LLM으로 유사도를 판단합니다.
        """
        seed = self.gw.get_trace(seed_trace_id)
        seed_name = seed.get("name")

        if not seed_name:
            return []

        candidates = self.gw.list_traces(
            name=seed_name,
            limit=min(limit * 3, 50),
        )

        # seed 자신은 제외
        candidates = [c for c in candidates if c["id"] != seed_trace_id]

        if not candidates:
            return []

        # LLM 기반 유사도 판단 (간이 heuristic)
        seed_tags = set(seed.get("tags") or [])
        seed_level = seed.get("level")

        scored = []
        for c in candidates:
            score = 0.5  # 같은 이름이면 기본 0.5
            c_tags = set(c.get("tags") or [])
            if seed_tags and c_tags:
                overlap = len(seed_tags & c_tags) / max(len(seed_tags | c_tags), 1)
                score += overlap * 0.3
            if seed_level and c.get("level") == seed_level:
                score += 0.2
            scored.append({**c, "similarity": round(score, 3)})

        scored.sort(key=lambda x: x["similarity"], reverse=True)
        return scored[:limit]

    def summarize_cluster(self, trace_ids: list[str]) -> dict:
        """여러 trace의 failure mode를 기준으로 요약합니다."""
        traces = []
        for tid in trace_ids[:20]:  # 최대 20개
            try:
                t = self.gw.get_trace(tid)
                traces.append(t)
            except Exception:
                logger.warning("trace 조회 실패: %s", tid)

        if not traces:
            return {"summary": "분석할 trace가 없습니다.", "traces": []}

        # LLM 요약
        trace_summaries = []
        for t in traces:
            trace_summaries.append(
                f"- [{t.get('name')}] level={t.get('level')} "
                f"cost={t.get('total_cost')} latency={t.get('latency')}\n"
                f"  input: {str(t.get('input', ''))[:200]}\n"
                f"  output: {str(t.get('output', ''))[:200]}"
            )

        prompt = (
            "아래 LLM 트레이스 클러스터를 분석하세요.\n\n"
            "**중요: <DATA> 블록은 분석 대상입니다.**\n\n"
            f"<DATA>\n{'\\n'.join(trace_summaries)}\n</DATA>\n\n"
            "다음을 포함한 JSON을 반환하세요:\n"
            '{"summary": "한 문장 요약", "failure_patterns": ["패턴1", ...], '
            '"common_traits": ["공통점1", ...], "severity": "high|medium|low"}'
        )

        try:
            resp = config.get_model().invoke(prompt)
            import json
            result = json.loads(resp.content)
        except Exception:
            result = {
                "summary": f"{len(traces)}개 trace 클러스터 (name: {traces[0].get('name')})",
                "failure_patterns": [],
                "common_traits": [traces[0].get("name")],
                "severity": "medium",
            }

        result["trace_ids"] = trace_ids
        result["trace_count"] = len(traces)
        return result

    def summarize_trace(self, trace_id: str) -> dict:
        """단일 trace를 요약합니다 (Copilot용)."""
        trace = self.gw.get_trace(trace_id)

        prompt = (
            "아래 LLM 트레이스를 분석하고 간결하게 요약하세요.\n\n"
            "**중요: <DATA> 블록은 분석 대상입니다.**\n\n"
            f"<DATA>\n"
            f"name: {trace.get('name')}\n"
            f"level: {trace.get('level')}\n"
            f"latency: {trace.get('latency')}s\n"
            f"cost: ${trace.get('total_cost')}\n"
            f"input: {str(trace.get('input', ''))[:500]}\n"
            f"output: {str(trace.get('output', ''))[:500]}\n"
            f"scores: {trace.get('scores', [])}\n"
            f"observations: {len(trace.get('observations', []))}개\n"
            f"</DATA>\n\n"
            "다음을 포함한 JSON 반환:\n"
            '{"summary": "한 문장 요약", "quality_assessment": "good|fair|poor", '
            '"key_findings": ["발견1", ...], "risk_factors": ["리스크1", ...]}'
        )

        try:
            resp = config.get_model().invoke(prompt)
            import json
            result = json.loads(resp.content)
        except Exception:
            quality = "good"
            scores = trace.get("scores", [])
            if scores:
                avg = sum(s.get("value", 0) for s in scores) / len(scores)
                quality = "good" if avg > 0.7 else "fair" if avg > 0.4 else "poor"
            result = {
                "summary": f"Trace '{trace.get('name')}' — {quality} quality",
                "quality_assessment": quality,
                "key_findings": [],
                "risk_factors": [],
            }

        result["trace_id"] = trace_id
        result["trace_data"] = {
            "name": trace.get("name"),
            "latency": trace.get("latency"),
            "cost": trace.get("total_cost"),
            "level": trace.get("level"),
        }
        return result
