"""Нормализация путей документов, склейка RAG-строк и сидирование по target hints."""

from __future__ import annotations


def normalize_doc_path(path: str | None) -> str:
    value = str(path or "").strip().replace("\\", "/")
    if not value:
        return ""
    while "//" in value:
        value = value.replace("//", "/")
    while value.startswith("./"):
        value = value[2:]
    value = value.lstrip("/")
    docs_idx = value.lower().find("docs/")
    if docs_idx >= 0:
        value = value[docs_idx:]
    elif "/" not in value and value.lower().endswith(".md"):
        value = f"docs/{value}"
    return value.strip()


def row_path(row: dict) -> str:
    metadata = dict(row.get("metadata") or {})
    raw = row.get("path") or metadata.get("source_path") or ""
    return normalize_doc_path(str(raw))


def normalized_path_set(rows: list[dict]) -> set[str]:
    return {path for row in rows if (path := row_path(row))}


def path_variants_for_rag_query(path: str | None) -> list[str]:
    normalized = normalize_doc_path(path)
    if not normalized:
        return []
    variants = [normalized]
    if normalized.startswith("docs/"):
        variants.append(normalized.removeprefix("docs/"))
    else:
        variants.append(f"docs/{normalized}")
    basename = normalized.split("/")[-1]
    if basename and basename not in variants:
        variants.append(basename)
    return list(dict.fromkeys(variants))


def merge_row_lists(*groups: list[dict]) -> list[dict]:
    merged: list[dict] = []
    seen: set[tuple[str, str, str]] = set()
    for rows in groups:
        for row in rows:
            metadata = dict(row.get("metadata") or {})
            key = (
                row_path(row),
                str(row.get("layer") or ""),
                str(metadata.get("section_path") or ""),
            )
            if key in seen:
                continue
            seen.add(key)
            merged.append(row)
    return merged


class RagRowIndex:
    def __init__(self, rows: list[dict]) -> None:
        self._by_path: dict[str, list[dict]] = {}
        self._by_name: dict[str, list[dict]] = {}
        for row in rows:
            normalized = row_path(row)
            if not normalized:
                continue
            self._by_path.setdefault(normalized.lower(), []).append(row)
            basename = normalized.split("/")[-1].lower()
            self._by_name.setdefault(basename, []).append(row)

    def lookup(self, hint: str | None) -> list[dict]:
        matches: list[dict] = []
        seen_ids: set[int] = set()
        for variant in path_variants_for_rag_query(hint):
            key = variant.lower()
            for row in self._by_path.get(key, []):
                row_id = id(row)
                if row_id in seen_ids:
                    continue
                seen_ids.add(row_id)
                matches.append(row)
        basename = normalize_doc_path(hint).split("/")[-1].lower()
        for row in self._by_name.get(basename, []):
            row_id = id(row)
            if row_id in seen_ids:
                continue
            seen_ids.add(row_id)
            matches.append(row)
        return matches


def seed_candidates_from_target_hints(rows: list[dict], hints: list[str], index: RagRowIndex | None = None) -> list[dict]:
    hints_raw = [str(hint).strip() for hint in hints if str(hint or "").strip()]
    if not hints_raw or not rows:
        return rows
    rag_index = index or RagRowIndex(rows)
    seeded = [match for hint in hints_raw for match in rag_index.lookup(hint)]
    return merge_row_lists(seeded, rows)


__all__ = [
    "RagRowIndex",
    "merge_row_lists",
    "normalize_doc_path",
    "normalized_path_set",
    "path_variants_for_rag_query",
    "row_path",
    "seed_candidates_from_target_hints",
]
