"""DA-11 + DA-21: 슬라이드 조합 렌더러.

블록 배치 명세(JSON)를 받아 Jinja2로 HTML을 생성한다.
- 다중 페이지 지원
- 카테고리 경로 지원 (blocks/{category}/{name}.html)
- _legacy fallback (기존 경로 호환)
- 같은 area 블록 그룹핑 (겹침 방지)
"""
from __future__ import annotations

import logging
from collections import OrderedDict
from pathlib import Path
from typing import Any

import yaml
from jinja2 import Environment, FileSystemLoader

logger = logging.getLogger(__name__)

TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
STATIC_DIR = Path(__file__).parent.parent / "static"
CATALOG_PATH = TEMPLATES_DIR / "catalog.yaml"

# 카테고리 검색 순서
BLOCK_CATEGORIES = ["headers", "cards", "tables", "visuals", "emphasis", "media"]

# catalog.yaml에서 id → template 경로 매핑 로드 (BF-10: mtime 체크로 자동 갱신)
_CATALOG_MAP: dict[str, str] | None = None
_CATALOG_MTIME: float = 0.0

def _load_catalog_map() -> dict[str, str]:
    """catalog.yaml에서 블록 id → template 경로 매핑을 로드한다.

    파일 수정시간(mtime)을 확인하여, 변경 시에만 재로드한다.
    """
    global _CATALOG_MAP, _CATALOG_MTIME

    current_mtime = CATALOG_PATH.stat().st_mtime if CATALOG_PATH.exists() else 0.0

    if _CATALOG_MAP is not None and _CATALOG_MTIME == current_mtime:
        return _CATALOG_MAP  # 파일 변경 없음 → 캐시 재사용

    # 변경 감지 또는 첫 로드 → 새로 읽기
    _CATALOG_MTIME = current_mtime
    _CATALOG_MAP = {}
    if CATALOG_PATH.exists():
        try:
            with open(CATALOG_PATH, encoding="utf-8") as f:
                catalog = yaml.safe_load(f)
            for block in catalog.get("blocks", []):
                block_id = block.get("id", "")
                template = block.get("template", "")
                if block_id and template:
                    _CATALOG_MAP[block_id] = template
            logger.info(f"catalog.yaml 로드: {len(_CATALOG_MAP)}개 블록 매핑")
        except Exception as e:
            logger.warning(f"catalog.yaml 로드 실패: {e}")
    else:
        logger.warning(f"catalog.yaml 미발견: {CATALOG_PATH}")

    return _CATALOG_MAP


def create_jinja_env() -> Environment:
    """Jinja2 환경 생성."""
    return Environment(
        loader=FileSystemLoader(str(TEMPLATES_DIR)),
        autoescape=False,
    )


def _resolve_template_path(env: Environment, block_type: str) -> str | None:
    """블록 타입으로 템플릿 경로를 찾는다.

    검색 순서:
    0. catalog.yaml 매핑 (id → template 경로, 최우선)
    1. 정확한 경로 (blocks/cards/card-text-grid.html 등 — 팀장이 카테고리 포함 지정)
    2. 카테고리 폴더 검색 (blocks/{category}/{block_type}.html)
    3. _legacy fallback (blocks/_legacy/{block_type}.html)
    4. 루트 fallback (blocks/{block_type}.html)
    """
    candidates = []

    # 0. catalog.yaml에서 id → template 매핑 조회 (최우선)
    catalog_map = _load_catalog_map()
    if block_type in catalog_map:
        catalog_path = catalog_map[block_type]
        candidates.append(catalog_path)
        # .html 확장자 없는 경우 대비
        if not catalog_path.endswith(".html"):
            candidates.append(f"{catalog_path}.html")

    # 1. 이미 카테고리 경로가 포함된 경우 (예: "cards/card-text-grid")
    if "/" in block_type:
        candidates.append(f"blocks/{block_type}.html")
        candidates.append(f"blocks/{block_type}")  # .html 이미 포함된 경우

    # 2. 카테고리 폴더 검색
    for category in BLOCK_CATEGORIES:
        candidates.append(f"blocks/{category}/{block_type}.html")

    # 3. _legacy fallback
    candidates.append(f"blocks/_legacy/{block_type}.html")

    # 4. 루트 fallback
    candidates.append(f"blocks/{block_type}.html")

    for path in candidates:
        try:
            env.get_template(path)
            return path
        except Exception:
            continue

    return None


def _preprocess_svg_data(block_type: str, block_data: dict[str, Any]) -> dict[str, Any]:
    """P2-B: SVG 시각화 블록의 좌표를 사전 계산한다.

    venn-diagram: items[]에 cx, cy, r 좌표 추가 + outer_r 등 레이아웃 데이터
    다른 블록: 변경 없이 그대로 반환
    """
    SVG_BLOCKS = {"venn-diagram", "relationship"}

    if block_type not in SVG_BLOCKS:
        return block_data

    items = block_data.get("items", [])
    if not items:
        return block_data

    # items에 이미 cx가 있으면 (수동 지정) 그대로 사용
    if items[0].get("cx") is not None:
        return block_data

    try:
        from src.svg_calculator import prepare_venn_data

        prepared = prepare_venn_data(
            items=items,
            center_label=block_data.get("center_label", ""),
            center_sub=block_data.get("center_sub", ""),
            description=block_data.get("description", ""),
        )
        # 기존 block_data에 계산 결과 병합
        block_data.update(prepared)
        logger.info(
            f"SVG 좌표 계산 완료: {block_type}, "
            f"{len(items)}개 원소, outer_r={prepared.get('outer_r')}"
        )
    except Exception as e:
        logger.warning(f"SVG 좌표 계산 실패 ({block_type}): {e}. Phase 1 fallback.")
        # fallback: 좌표 없이 Jinja2에 전달 → Phase 1 고정 SVG

    return block_data


def _group_blocks_by_area(blocks: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """같은 area의 블록들을 하나로 그룹핑한다.

    CSS Grid에서 같은 area에 여러 div가 있으면 겹치므로,
    같은 area의 블록 HTML을 합쳐서 하나의 div로 만든다.
    """
    grouped = OrderedDict()
    for block in blocks:
        area = block["area"]
        if area not in grouped:
            grouped[area] = {"area": area, "htmls": []}
        grouped[area]["htmls"].append(block["html"])

    result = []
    for area, data in grouped.items():
        if len(data["htmls"]) == 1:
            html = data["htmls"][0]
        else:
            # 여러 블록을 flex-column으로 세로 쌓기
            inner = "\n".join(data["htmls"])
            html = (
                f'<div style="display:flex; flex-direction:column; '
                f'gap:var(--spacing-block); height:100%;">\n'
                f'{inner}\n</div>'
            )
        result.append({"area": area, "html": html})

    return result


def render_multi_page(layout_concept: dict[str, Any]) -> str:
    """다중 페이지 레이아웃 컨셉으로 완성 HTML을 생성한다."""
    env = create_jinja_env()
    title = layout_concept.get("title", "슬라이드")
    pages = layout_concept.get("pages", [])

    if not pages:
        logger.warning("페이지가 없습니다. 빈 HTML 반환.")
        return "<html><body><p>페이지가 없습니다.</p></body></html>"

    pages_rendered = []
    for page_idx, page in enumerate(pages):
        blocks_raw = []
        for block in page.get("blocks", []):
            block_type = block.get("type", "")
            block_data = block.get("data", {})

            # P2-B: SVG 시각화 블록은 좌표 사전 계산
            block_data = _preprocess_svg_data(block_type, block_data)

            # DA-21: 카테고리 경로 검색
            template_path = _resolve_template_path(env, block_type)

            if template_path:
                try:
                    block_template = env.get_template(template_path)
                    rendered_html = block_template.render(**block_data)
                except Exception as e:
                    logger.warning(f"블록 렌더링 실패 ({block_type}): {e}")
                    rendered_html = (
                        f'<div class="body-text">블록 렌더링 실패: {block_type}</div>'
                    )
            else:
                logger.warning(f"블록 템플릿 미발견: {block_type}")
                rendered_html = (
                    f'<div class="body-text">블록 템플릿 미발견: {block_type}</div>'
                )

            blocks_raw.append({
                "area": block.get("area", "main"),
                "html": rendered_html,
            })

        # Fix 1: 같은 area 블록 그룹핑
        blocks_grouped = _group_blocks_by_area(blocks_raw)

        # A-1: area별 CSS 변수 override 주입
        area_styles = page.get("area_styles", {})
        for grouped_block in blocks_grouped:
            grouped_block["style_override"] = area_styles.get(
                grouped_block["area"], ""
            )

        pages_rendered.append({
            "grid_areas": page.get("grid_areas", "'main'"),
            "grid_columns": page.get("grid_columns", "1fr"),
            "grid_rows": page.get("grid_rows", "auto"),
            "blocks": blocks_grouped,
            "page_number": page_idx + 1,
        })

    base_template = env.get_template("slide-base.html")
    html = base_template.render(
        slide_title=title,
        pages=pages_rendered,
        total_pages=len(pages_rendered),
    )

    # CSS 인라인 삽입
    tokens_css = (STATIC_DIR / "tokens.css").read_text(encoding="utf-8")
    base_css = (STATIC_DIR / "base.css").read_text(encoding="utf-8")
    base_css = base_css.replace("@import url('./tokens.css');", "")

    inline_css = f"<style>\n{tokens_css}\n{base_css}\n</style>"
    html = html.replace(
        '<link rel="stylesheet" href="/static/base.css">',
        inline_css,
    )

    logger.info(f"슬라이드 렌더링 완료: {title}, {len(pages_rendered)}페이지")
    return html


def render_slide(layout: dict[str, Any]) -> str:
    """하위 호환 렌더링. pages 구조가 있으면 render_multi_page로 위임."""
    if "pages" in layout:
        return render_multi_page(layout)

    env = create_jinja_env()

    blocks_raw = []
    for block in layout.get("blocks", []):
        block_type = block["type"]
        block_data = block.get("data", {})

        template_path = _resolve_template_path(env, block_type)

        if template_path:
            try:
                block_template = env.get_template(template_path)
                rendered_html = block_template.render(**block_data)
            except Exception as e:
                logger.warning(f"블록 렌더링 실패 ({block_type}): {e}")
                rendered_html = (
                    f'<div class="body-text">블록 렌더링 실패: {block_type}</div>'
                )
        else:
            logger.warning(f"블록 템플릿 미발견: {block_type}")
            rendered_html = (
                f'<div class="body-text">블록 템플릿 미발견: {block_type}</div>'
            )

        blocks_raw.append({
            "area": block["area"],
            "html": rendered_html,
        })

    blocks_grouped = _group_blocks_by_area(blocks_raw)

    base_template = env.get_template("slide-base.html")
    html = base_template.render(
        slide_title=layout.get("title", ""),
        pages=[{
            "grid_areas": layout.get("grid_areas", "'header' 'main' 'footer'"),
            "grid_columns": layout.get("grid_columns", "1fr"),
            "grid_rows": layout.get("grid_rows", "auto 1fr auto"),
            "blocks": blocks_grouped,
            "page_number": 1,
        }],
        total_pages=1,
    )

    tokens_css = (STATIC_DIR / "tokens.css").read_text(encoding="utf-8")
    base_css = (STATIC_DIR / "base.css").read_text(encoding="utf-8")
    base_css = base_css.replace("@import url('./tokens.css');", "")

    inline_css = f"<style>\n{tokens_css}\n{base_css}\n</style>"
    html = html.replace(
        '<link rel="stylesheet" href="/static/base.css">',
        inline_css,
    )

    logger.info(f"슬라이드 렌더링 완료: {layout.get('title', 'untitled')}")
    return html


def render_standalone_block(block_type: str, data: dict[str, Any]) -> str:
    """단일 블록을 독립 HTML로 렌더링 (테스트/미리보기용)."""
    env = create_jinja_env()
    template_path = _resolve_template_path(env, block_type)
    if not template_path:
        return f"<div>블록 미발견: {block_type}</div>"
    template = env.get_template(template_path)
    return template.render(**data)
