"""DA-13b: 3단계 — Kei 텍스트 편집자 (텍스트 정리).

디자인 팀장의 레이아웃 컨셉 + 원본 콘텐츠를 받아,
Kei API를 통해 도메인 전문가로서 각 슬롯 텍스트를 정리한다.
팀장의 글자 수 가이드를 참고하되 내용 의미가 우선.

1차: Kei API (persona + RAG + 도메인 지식)
fallback: Anthropic API 직접 호출
"""
from __future__ import annotations

import json
import logging
import re
from typing import Any

import anthropic
import httpx

from src.config import settings
from src.design_director import BLOCK_SLOTS

logger = logging.getLogger(__name__)

EDITOR_PROMPT = """당신은 도메인 전문가이자 콘텐츠 편집자이다.
원본 콘텐츠의 핵심 내용을 유지하면서 각 블록의 슬롯에 맞게 텍스트를 정리한다.

## 핵심 원칙
- **내용의 의미와 정확성이 글자 수보다 우선한다**
- 팀장이 제시한 글자 수 가이드는 참고. 의미를 살리려면 가이드를 초과해도 된다.
- 디자인 실무자가 텍스트에 맞게 디자인을 조정할 것이므로, 텍스트를 억지로 자르지 않는다.

## 편집 규칙
- 전체 컨텍스트와 핵심 용어를 보존한다
- 세련된 표현으로 편집한다 (원본 그대로가 아님)
- 개조식(불릿, 번호)으로 작성한다. 줄글 금지.
- **불릿 항목은 반드시 각각 별도 줄(\n)로 작성한다.** 한 줄에 여러 항목을 넣지 마라.
  - 올바른 예: "• 추진과제: 건설산업 디지털화\n• 실행과제: BIM 전면 도입\n• 출처: 국토교통부"
  - 잘못된 예: "• 추진과제: 건설산업 디지털화 • 실행과제: BIM 전면 도입 • 출처: 국토교통부"
- 출처가 있는 내용은 출처를 반드시 보존한다
- 출처가 없는 수치나 통계를 만들지 않는다

## 표 편집 규칙
- 표는 표로 유지한다 (다른 형태로 전환하지 않음)
- 팀장이 요약 요청하면 핵심 행/열만 선택하고 "...외 N건" 표기

## 자세히보기 편집 규칙
- detail_target인 꼭지는 두 버전을 작성:
  - summary: 슬라이드 표면에 보일 요약 (3줄 이내)
  - detail: 펼치면 보일 전체 내용

## JSON 형식으로만 응답한다. 설명 없이 JSON만."""


async def fill_content(
    content: str,
    layout_concept: dict[str, Any],
    analysis: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """3단계: 각 페이지의 각 블록 슬롯에 텍스트를 채운다.

    Args:
        content: 원본 텍스트 콘텐츠
        layout_concept: 디자인 팀장의 레이아웃 컨셉
        analysis: 1단계 실장의 꼭지 분석 결과 (참고용)

    Returns:
        슬롯이 채워진 layout_concept
    """
    for page_idx, page in enumerate(layout_concept.get("pages", [])):
        blocks = page.get("blocks", [])
        if not blocks:
            continue

        # 블록별 슬롯 + 글자 수 가이드 생성
        slot_requirements = []
        for i, block in enumerate(blocks):
            block_type = block.get("type", "")
            slots = BLOCK_SLOTS.get(block_type, {})
            char_guide = block.get("char_guide", {})

            topic_id = block.get("topic_id", i + 1)
            req_text = (
                f"블록 {i+1} ({block_type}, 영역: {block.get('area', '?')}, topic_id: {topic_id}):\n"
                f"  용도: {block.get('reason', '미지정')}\n"
                f"  크기: {block.get('size', 'medium')}\n"
                f"  필수 슬롯: {slots.get('required', [])}\n"
                f"  선택 슬롯: {slots.get('optional', [])}"
            )

            if char_guide:
                guide_lines = [f"    {k}: ~{v}자" for k, v in char_guide.items()]
                req_text += "\n  글자 수 가이드 (참고, 의미 우선):\n" + "\n".join(guide_lines)

            slot_requirements.append(req_text)

        page_label = ""
        if len(layout_concept.get("pages", [])) > 1:
            page_label = f" (페이지 {page_idx + 1}/{len(layout_concept['pages'])})"

        user_prompt = (
            f"## 원본 콘텐츠\n{content}\n\n"
            f"## 블록 배치{page_label}\n"
            + "\n".join(slot_requirements)
            + "\n\n## 요청\n"
            "위 블록별로 슬롯에 들어갈 텍스트를 정리하여 JSON으로 반환해줘.\n"
            "내용의 의미를 살려서 편집해. 글자 수 가이드는 참고만.\n"
            "자세히보기 대상 블록은 summary + detail 두 버전을 작성해.\n"
            "형식:\n"
            '{"blocks": [{"area": "...", "type": "...", "topic_id": 1, "data": {슬롯 키-값}}]}'
        )

        try:
            # 1차: Kei API (도메인 전문가 + RAG)
            result_text = await _call_kei_editor(user_prompt)

            # fallback: Anthropic 직접
            if result_text is None:
                logger.warning("Kei API 편집 실패. Anthropic 직접 호출로 fallback.")
                client = anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key)
                response = await client.messages.create(
                    model="claude-sonnet-4-20250514",
                    max_tokens=4096,
                    system=EDITOR_PROMPT,
                    messages=[{"role": "user", "content": user_prompt}],
                )
                result_text = response.content[0].text

            filled = _parse_json(result_text)

            if filled and "blocks" in filled:
                for filled_block in filled["blocks"]:
                    matched = False
                    # 1차: topic_id로 정확 매칭
                    if filled_block.get("topic_id"):
                        for orig_block in blocks:
                            if orig_block.get("topic_id") == filled_block.get("topic_id"):
                                orig_block["data"] = filled_block.get("data", {})
                                matched = True
                                break
                    # 2차: area + type으로 매칭 (topic_id 없을 때)
                    if not matched:
                        for orig_block in blocks:
                            if (
                                orig_block.get("area") == filled_block.get("area")
                                and orig_block.get("type") == filled_block.get("type")
                                and "data" not in orig_block
                            ):
                                orig_block["data"] = filled_block.get("data", {})
                                break

                logger.info(
                    f"텍스트 정리 완료 (페이지 {page_idx + 1}): "
                    f"{len(filled['blocks'])}개 블록"
                )
            else:
                logger.warning(f"텍스트 정리 파싱 실패 (페이지 {page_idx + 1}). 기본값.")
                _apply_defaults(blocks)

        except Exception as e:
            logger.error(f"텍스트 편집자 호출 실패: {e}", exc_info=True)
            _apply_defaults(blocks)

    return layout_concept


async def _call_kei_editor(prompt: str) -> str | None:
    """Kei API를 통해 텍스트 편집을 요청한다.

    Kei persona의 도메인 지식 + RAG를 활용하여
    건설/DX 분야 전문 용어를 정확하게 유지하면서 편집.
    """
    kei_url = getattr(settings, "kei_api_url", "http://localhost:8000")

    full_prompt = EDITOR_PROMPT + "\n\n" + prompt

    try:
        async with httpx.AsyncClient(timeout=None) as client:
            response = await client.post(
                f"{kei_url}/api/message",
                json={
                    "message": full_prompt,
                    "session_id": "design-agent-editor",
                    "mode": "chat",
                },
                timeout=None,
            )

            if response.status_code != 200:
                logger.warning(f"Kei API (editor) HTTP {response.status_code}")
                return None

            # SSE 응답에서 텍스트 수집
            full_text = _extract_sse_text(response.text)
            if full_text:
                return full_text

            logger.warning("Kei API (editor) 텍스트 추출 실패")
            return None

    except Exception as e:
        logger.warning(f"Kei API (editor) 호출 실패: {e}")
        return None


def _extract_sse_text(raw: str) -> str:
    """SSE 응답에서 토큰 텍스트를 수집한다."""
    import re as _re
    tokens = []
    events = _re.split(r'\r?\n\r?\n', raw)
    for event in events:
        if not event.strip():
            continue
        event_type = ""
        event_data = ""
        for line in event.split('\n'):
            line = line.strip('\r')
            if line.startswith('event:'):
                event_type = line[6:].strip()
            elif line.startswith('data:'):
                event_data = line[5:].strip()
        if not event_data:
            continue
        if event_type == 'token':
            try:
                token = json.loads(event_data)
                if isinstance(token, str):
                    tokens.append(token)
            except json.JSONDecodeError:
                tokens.append(event_data)
        elif event_type == 'done':
            break
    return "".join(tokens)


def _apply_defaults(blocks: list[dict[str, Any]]) -> None:
    """실패 시 기본 데이터 적용."""
    defaults = {
        # headers/
        "section-title-with-bg": {"title_ko": "(제목)"},
        "section-header-bar": {"title": "(섹션)"},
        "topic-left-right": {"title": "(소제목)", "description": ""},
        "topic-center": {"title": "(제목)"},
        "topic-numbered": {"number": "1", "title": "(단계)"},
        # cards/
        "card-image-3col": {"cards": []},
        "card-text-grid": {"cards": []},
        "card-dark-overlay": {"cards": []},
        "card-tag-image": {"cards": []},
        "card-icon-desc": {"cards": []},
        "card-compare-3col": {"cards": []},
        "card-step-vertical": {"steps": []},
        "card-image-round": {"cards": []},
        "card-stat-number": {"stats": []},
        "card-numbered": {"items": []},
        # tables/
        "compare-3col-badge": {"headers": [], "rows": []},
        "compare-2col-split": {"left_title": "A", "right_title": "B", "rows": []},
        "table-simple-striped": {"headers": [], "rows": []},
        # visuals/
        "venn-diagram": {"center_label": "관계도", "items": [], "center_sub": "", "description": ""},
        "circle-gradient": {"label": "(라벨)"},
        "compare-pill-pair": {"left_label": "A", "right_label": "B"},
        "process-horizontal": {"steps": []},
        "flow-arrow-horizontal": {"steps": []},
        "keyword-circle-row": {"keywords": []},
        "layer-diagram": {"layers": []},
        "timeline-vertical": {"events": []},
        "timeline-horizontal": {"events": []},
        "pyramid-hierarchy": {"levels": []},
        # emphasis/
        "quote-left-border": {"quote_text": "(인용)"},
        "quote-big-mark": {"quote_text": "(인용)"},
        "quote-question": {"question": "(질문)"},
        "conclusion-accent-bar": {"conclusion_text": "(결론)"},
        "comparison-2col": {"left_title": "A", "left_content": "-", "right_title": "B", "right_content": "-"},
        "banner-gradient": {"text": "(배너)"},
        "dark-bullet-list": {"bullets": []},
        "highlight-strip": {"segments": []},
        "callout-solution": {"title": "(솔루션)", "description": ""},
        "callout-warning": {"title": "(경고)", "description": ""},
        "tab-label-row": {"tabs": []},
        "divider-text": {"text": "구분"},
        # media/
        "image-row-2col": {"images": []},
        "image-grid-2x2": {"images": []},
        "image-side-text": {"image_src": ""},
        "image-full-caption": {"src": ""},
        "image-before-after": {"before_src": "", "after_src": ""},
        "details-block": {"summary_text": "(상세 내용)", "detail_content": ""},
    }
    for block in blocks:
        if "data" not in block:
            block["data"] = defaults.get(block.get("type", ""), {})


def _parse_json(text: str) -> dict[str, Any] | None:
    """텍스트에서 JSON을 추출한다."""
    patterns = [
        r"```json\s*(.*?)```",
        r"```\s*(.*?)```",
        r"(\{.*\})",
    ]
    for pattern in patterns:
        match = re.search(pattern, text, re.DOTALL)
        if match:
            try:
                return json.loads(match.group(1).strip())
            except json.JSONDecodeError:
                continue
    return None
