"""Copilot 서비스 — context-first action-oriented 분석 엔진."""

import json
import logging
import uuid
from dataclasses import asdict, dataclass, field

import sentinel.config as config
from sentinel.integrations.langfuse_gateway import LangfuseGateway
from sentinel.schema import ActionResult, ActionType
from sentinel.services.trace_analysis import TraceAnalysisService

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


# ---------------------------------------------------------------------------
# Copilot 데이터 계약
# ---------------------------------------------------------------------------


@dataclass
class CopilotContext:
    """Copilot 호출 컨텍스트."""
    surface_type: str  # trace, prompt, report, eval
    object_id: str = ""
    object_metadata: dict = field(default_factory=dict)
    evidence_refs: list[str] = field(default_factory=list)
    allowed_actions: list[str] = field(default_factory=list)


@dataclass
class RecommendedAction:
    """추천 액션."""
    id: str = field(default_factory=lambda: uuid.uuid4().hex[:8])
    action_type: str = "analyze"
    label: str = ""
    description: str = ""
    approval_required: bool = False
    params: dict = field(default_factory=dict)


@dataclass
class CopilotResponse:
    """Copilot 응답."""
    summary: str = ""
    findings: list[str] = field(default_factory=list)
    evidence_refs: list[str] = field(default_factory=list)
    recommended_actions: list[RecommendedAction] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)

    def to_dict(self) -> dict:
        d = asdict(self)
        return d


# ---------------------------------------------------------------------------
# Service
# ---------------------------------------------------------------------------


class CopilotService:
    """Embedded Copilot의 분석 엔진."""

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

    def analyze(self, context: CopilotContext) -> CopilotResponse:
        """context를 기반으로 분석하고 추천 action을 반환합니다."""
        handler = {
            "trace": self._analyze_trace,
            "prompt": self._analyze_prompt,
            "report": self._analyze_report,
            "eval": self._analyze_eval,
        }.get(context.surface_type)

        if handler:
            return handler(context)

        return CopilotResponse(
            summary=f"'{context.surface_type}' 타입은 아직 지원되지 않습니다.",
            warnings=[f"지원 타입: trace, prompt, report, eval"],
        )

    def execute_action(
        self, action_id: str, context: CopilotContext, params: dict | None = None
    ) -> ActionResult:
        """추천 action을 실행합니다."""
        action_map = {
            "find_similar": self._action_find_similar,
            "summarize_trace": self._action_summarize_trace,
            "build_dataset_draft": self._action_build_dataset_draft,
            "draft_prompt_change": self._action_draft_prompt_change,
        }

        # action_id에서 action type 추출 (prefix_xxxx)
        action_type = action_id.rsplit("_", 1)[0] if "_" in action_id else action_id

        handler = action_map.get(action_type)
        if not handler:
            return ActionResult(
                success=False,
                action_type=ActionType.READ,
                summary=f"알 수 없는 action: {action_id}",
            )

        return handler(context, params or {})

    def ask(self, question: str, context: CopilotContext) -> CopilotResponse:
        """자유 질문에 대해 context 기반으로 답변합니다."""
        context_info = f"surface: {context.surface_type}, object_id: {context.object_id}"
        if context.object_metadata:
            context_info += f"\nmetadata: {json.dumps(context.object_metadata, ensure_ascii=False, default=str)[:1000]}"

        prompt = (
            "당신은 LLMOps Copilot입니다. 아래 컨텍스트를 참고하여 질문에 답변하세요.\n\n"
            f"<CONTEXT>\n{context_info}\n</CONTEXT>\n\n"
            f"질문: {question}\n\n"
            "간결하고 실용적으로 답변하세요."
        )

        try:
            resp = config.get_model().invoke(prompt)
            return CopilotResponse(
                summary=resp.content,
                evidence_refs=[context.object_id] if context.object_id else [],
            )
        except Exception as e:
            return CopilotResponse(
                summary=f"답변 생성 실패: {e}",
                warnings=[str(e)],
            )

    # ------------------------------------------------------------------
    # Analyze handlers
    # ------------------------------------------------------------------

    def _analyze_trace(self, ctx: CopilotContext) -> CopilotResponse:
        trace_id = ctx.object_id
        try:
            summary = self.trace_svc.summarize_trace(trace_id)
        except Exception as e:
            return CopilotResponse(summary=f"Trace 분석 실패: {e}", warnings=[str(e)])

        actions = [
            RecommendedAction(
                id=f"find_similar_{uuid.uuid4().hex[:6]}",
                action_type="analyze",
                label="유사 Trace 검색",
                description="같은 이름/패턴의 트레이스를 검색합니다",
                params={"trace_id": trace_id},
            ),
            RecommendedAction(
                id=f"build_dataset_draft_{uuid.uuid4().hex[:6]}",
                action_type="draft",
                label="Dataset Draft 생성",
                description="이 트레이스를 Dataset 항목으로 변환합니다",
                approval_required=True,
                params={"trace_ids": [trace_id]},
            ),
            RecommendedAction(
                id=f"summarize_trace_{uuid.uuid4().hex[:6]}",
                action_type="analyze",
                label="상세 분석",
                description="트레이스의 품질과 패턴을 상세 분석합니다",
                params={"trace_id": trace_id},
            ),
        ]

        findings = summary.get("key_findings", [])
        warnings = summary.get("risk_factors", [])

        return CopilotResponse(
            summary=summary.get("summary", ""),
            findings=findings,
            evidence_refs=[trace_id],
            recommended_actions=actions,
            warnings=warnings,
        )

    def _analyze_prompt(self, ctx: CopilotContext) -> CopilotResponse:
        prompt_name = ctx.object_id
        try:
            prompt_data = self.gw.get_prompt(prompt_name)
        except Exception as e:
            return CopilotResponse(summary=f"Prompt 조회 실패: {e}", warnings=[str(e)])

        actions = [
            RecommendedAction(
                id=f"draft_prompt_change_{uuid.uuid4().hex[:6]}",
                action_type="draft",
                label="Prompt 개선안 생성",
                description="LLM 기반으로 프롬프트 개선안을 생성합니다",
                approval_required=True,
                params={"prompt_name": prompt_name},
            ),
        ]

        prompt_text = prompt_data.get("prompt", "")
        length = len(prompt_text)

        return CopilotResponse(
            summary=f"Prompt '{prompt_name}' v{prompt_data.get('version')} — {length}자",
            findings=[
                f"현재 label: {prompt_data.get('label')}",
                f"버전: {prompt_data.get('version')}",
            ],
            evidence_refs=[prompt_name],
            recommended_actions=actions,
        )

    def _analyze_report(self, ctx: CopilotContext) -> CopilotResponse:
        return CopilotResponse(
            summary="보고서 분석 — 기존 보고서의 핵심 인사이트를 확인하세요.",
            findings=["보고서 copilot 기능은 Sprint 3에서 확장 예정"],
            evidence_refs=[ctx.object_id],
        )

    def _analyze_eval(self, ctx: CopilotContext) -> CopilotResponse:
        return CopilotResponse(
            summary="평가 결과 분석 — 저점수 항목을 검토하세요.",
            findings=["평가 copilot 기능은 Sprint 3에서 확장 예정"],
            evidence_refs=[ctx.object_id],
        )

    # ------------------------------------------------------------------
    # Action handlers
    # ------------------------------------------------------------------

    def _action_find_similar(
        self, ctx: CopilotContext, params: dict
    ) -> ActionResult:
        trace_id = params.get("trace_id", ctx.object_id)
        try:
            results = self.trace_svc.find_similar_traces(trace_id)
            return ActionResult(
                success=True,
                action_type=ActionType.ANALYZE,
                summary=f"{len(results)}개 유사 trace 발견",
                data=results,
                evidence_refs=[trace_id],
            )
        except Exception as e:
            return ActionResult(
                success=False,
                action_type=ActionType.ANALYZE,
                summary=f"유사 trace 검색 실패: {e}",
            )

    def _action_summarize_trace(
        self, ctx: CopilotContext, params: dict
    ) -> ActionResult:
        trace_id = params.get("trace_id", ctx.object_id)
        try:
            summary = self.trace_svc.summarize_trace(trace_id)
            return ActionResult(
                success=True,
                action_type=ActionType.ANALYZE,
                summary=summary.get("summary", ""),
                data=summary,
                evidence_refs=[trace_id],
            )
        except Exception as e:
            return ActionResult(
                success=False,
                action_type=ActionType.ANALYZE,
                summary=f"trace 요약 실패: {e}",
            )

    def _action_build_dataset_draft(
        self, ctx: CopilotContext, params: dict
    ) -> ActionResult:
        from sentinel.services.draft_service import DraftService
        trace_ids = params.get("trace_ids", [ctx.object_id])
        try:
            draft_svc = DraftService(self.gw)
            draft = draft_svc.create_dataset_draft(trace_ids)
            return ActionResult(
                success=True,
                action_type=ActionType.DRAFT,
                summary=f"Dataset draft 생성 완료 ({len(draft.get('items', []))}개 항목)",
                data=draft,
                evidence_refs=trace_ids,
                approval_required=True,
            )
        except Exception as e:
            return ActionResult(
                success=False,
                action_type=ActionType.DRAFT,
                summary=f"Dataset draft 생성 실패: {e}",
            )

    def _action_draft_prompt_change(
        self, ctx: CopilotContext, params: dict
    ) -> ActionResult:
        from sentinel.services.draft_service import DraftService
        prompt_name = params.get("prompt_name", ctx.object_id)
        try:
            draft_svc = DraftService(self.gw)
            draft = draft_svc.create_prompt_change_draft(prompt_name)
            return ActionResult(
                success=True,
                action_type=ActionType.DRAFT,
                summary=f"Prompt change draft 생성 완료",
                data=draft,
                evidence_refs=[prompt_name],
                approval_required=True,
            )
        except Exception as e:
            return ActionResult(
                success=False,
                action_type=ActionType.DRAFT,
                summary=f"Prompt change draft 생성 실패: {e}",
            )
