"""Label Linkbase パーサー。

XBRL Label Linkbase (``_lab.xml``) をパースし、concept → ラベルテキスト
のマッピングを提供する。

``_parse_lab_xml_tree()`` ロジックを taxonomy/__init__.py から切り出し、
xbrl_core の独立ラベルリンクベースパーサーとして提供する。

典型的な使い方::

    from xbrl_core.linkbase.label import parse_label_linkbase

    labels = parse_label_linkbase(xml_bytes)
    for lab in labels:
        print(f"{lab.concept_href} → {lab.text} ({lab.lang})")
"""

from __future__ import annotations

import logging
import warnings
from dataclasses import dataclass

from lxml import etree

from xbrl_core.errors import XbrlParseError, XbrlWarning
from xbrl_core._linkbase_utils import extract_concept_from_href as _extract_concept_from_href
from xbrl_core._namespaces import NS_LINK, NS_XLINK, NS_XML

logger = logging.getLogger(__name__)


# ========== データモデル ==========


@dataclass(frozen=True, slots=True)
class RawLabel:
    """ラベルリンクベースから抽出した 1 件のラベル。

    Attributes:
        concept_href: xlink:href の値（元の参照）。
        concept_name: href から抽出した concept ローカル名。
        text: ラベルテキスト。
        lang: 言語コード（例: ``"ja"``, ``"en"``）。
        role: ラベルロール URI。
    """

    concept_href: str
    concept_name: str
    text: str
    lang: str
    role: str


# ========== 公開 API ==========


def parse_label_linkbase(
    xml_bytes: bytes,
    *,
    source_path: str | None = None,
) -> tuple[RawLabel, ...]:
    """Label Linkbase XML をパースし、RawLabel のタプルを返す。

    Args:
        xml_bytes: ``_lab.xml`` のバイト列。
        source_path: エラーメッセージ用のソースファイルパス。

    Returns:
        RawLabel のタプル。

    Raises:
        XbrlParseError: XML パースに失敗した場合。
    """
    try:
        root = etree.fromstring(xml_bytes)  # noqa: S320
    except etree.XMLSyntaxError as exc:
        source_label = source_path or "<bytes>"
        raise XbrlParseError(
            "XBRL_LINK_005",
            f"Failed to parse Label Linkbase XML: {source_label}: {exc}",
            source=source_label,
        ) from exc

    return _parse_label_tree(root)


# ========== 内部実装 ==========


def _parse_label_tree(root: etree._Element) -> tuple[RawLabel, ...]:
    """パース済みの XML ルートからラベル群を構築する。"""
    tag_loc = f"{{{NS_LINK}}}loc"
    tag_label = f"{{{NS_LINK}}}label"
    tag_label_arc = f"{{{NS_LINK}}}labelArc"
    attr_xlink_label = f"{{{NS_XLINK}}}label"
    attr_xlink_href = f"{{{NS_XLINK}}}href"
    attr_xlink_role = f"{{{NS_XLINK}}}role"
    attr_xlink_from = f"{{{NS_XLINK}}}from"
    attr_xlink_to = f"{{{NS_XLINK}}}to"
    attr_xml_lang = f"{{{NS_XML}}}lang"

    # Step 1: loc → (concept_name, href)
    loc_map: dict[str, tuple[str, str]] = {}
    for loc in root.iter(tag_loc):
        key = loc.get(attr_xlink_label)
        href = loc.get(attr_xlink_href)
        if key is None or href is None:
            continue
        concept = _extract_concept_from_href(href)
        if concept is not None:
            loc_map[key] = (concept, href)

    # Step 2: label → (role, lang, text)
    label_map: dict[str, tuple[str, str, str]] = {}
    for label in root.iter(tag_label):
        key = label.get(attr_xlink_label)
        role = label.get(attr_xlink_role)
        lang = label.get(attr_xml_lang)
        text = label.text or ""
        if key and role and lang:
            label_map[key] = (role, lang, text.strip())

    # Step 3: labelArc で接続
    result: list[RawLabel] = []
    for arc in root.iter(tag_label_arc):
        # prohibited/priority 検出
        if arc.get("use") == "prohibited" or arc.get("priority") is not None:
            warnings.warn(
                "labelArc に use='prohibited' または priority 属性が検出されました。"
                "arc override の完全処理は行いません",
                XbrlWarning,
                stacklevel=2,
            )
        from_key = arc.get(attr_xlink_from)
        to_key = arc.get(attr_xlink_to)
        if from_key in loc_map and to_key in label_map:
            concept_name, concept_href = loc_map[from_key]
            role, lang, text = label_map[to_key]
            result.append(
                RawLabel(
                    concept_href=concept_href,
                    concept_name=concept_name,
                    text=text,
                    lang=lang,
                    role=role,
                )
            )

    logger.debug("Label Linkbase パース: %d ラベル", len(result))
    return tuple(result)
