"""Reference Linkbase パーサー。

XBRL Reference Linkbase (``_ref.xml``) をパースし、
concept から参照情報（法令条項等）へのマッピングを提供する。

典型的な使い方::

    from xbrl_core.linkbase.reference import parse_reference_linkbase

    refs = parse_reference_linkbase(xml_bytes)
    for ref in refs:
        print(f"{ref.concept_name}: {ref.parts}")
"""

from __future__ import annotations

import logging
from dataclasses import dataclass

from lxml import etree

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

logger = logging.getLogger(__name__)

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


@dataclass(frozen=True, slots=True)
class ReferencePart:
    """参照情報の 1 パート。

    Attributes:
        local_name: パート要素のローカル名（例: ``"Name"``, ``"Number"``）。
        namespace: パート要素の名前空間 URI。
        value: パート要素のテキスト値。
    """

    local_name: str
    namespace: str
    value: str


@dataclass(frozen=True, slots=True)
class ReferenceInfo:
    """concept に紐付けられた参照情報。

    Attributes:
        concept_href: xlink:href の値。
        concept_name: href から抽出した concept ローカル名。
        role: 参照ロール URI。
        parts: 参照パートのタプル。
    """

    concept_href: str
    concept_name: str
    role: str
    parts: tuple[ReferencePart, ...]


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


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

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

    Returns:
        ReferenceInfo のタプル。

    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_006",
            f"Failed to parse Reference Linkbase XML: {source_label}: {exc}",
            source=source_label,
        ) from exc

    return _parse_reference_tree(root)


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


def _parse_reference_tree(root: etree._Element) -> tuple[ReferenceInfo, ...]:
    """パース済みの XML ルートから参照情報群を構築する。"""
    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"
    tag_loc = f"{{{NS_LINK}}}loc"
    tag_reference = f"{{{NS_LINK}}}reference"
    tag_reference_arc = f"{{{NS_LINK}}}referenceArc"

    # 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: reference → (role, parts)
    ref_map: dict[str, tuple[str, tuple[ReferencePart, ...]]] = {}
    for ref_elem in root.iter(tag_reference):
        key = ref_elem.get(attr_xlink_label)
        role = ref_elem.get(attr_xlink_role) or ""
        if not key:
            continue

        parts: list[ReferencePart] = []
        for child in ref_elem:
            tag = child.tag
            if not isinstance(tag, str) or not tag.startswith("{"):
                continue
            ns, local = tag[1:].split("}", 1)
            text = (child.text or "").strip()
            if text:
                parts.append(ReferencePart(local_name=local, namespace=ns, value=text))

        ref_map[key] = (role, tuple(parts))

    # Step 3: referenceArc で接続
    result: list[ReferenceInfo] = []
    for arc in root.iter(tag_reference_arc):
        if arc.get("use") == "prohibited":
            continue
        from_key = arc.get(attr_xlink_from)
        to_key = arc.get(attr_xlink_to)
        if from_key in loc_map and to_key in ref_map:
            concept_name, concept_href = loc_map[from_key]
            ref_role, ref_parts = ref_map[to_key]
            result.append(
                ReferenceInfo(
                    concept_href=concept_href,
                    concept_name=concept_name,
                    role=ref_role,
                    parts=ref_parts,
                )
            )

    logger.debug("Reference Linkbase パース: %d 参照", len(result))
    return tuple(result)
