from __future__ import annotations

from dataclasses import asdict, is_dataclass
from hashlib import sha1
from pathlib import Path
import json
import re
from typing import Any


def stable_id(*parts: str) -> str:
    data = "".join(parts).encode("utf-8")
    return sha1(data).hexdigest()


def read_text(path: Path) -> str:
    return path.read_text(encoding="utf-8", errors="replace")


def extract_title_from_markdown(text: str, fallback: str) -> str:
    for line in text.splitlines():
        stripped = line.strip()
        if not stripped:
            continue
        if stripped.startswith("#"):
            return stripped.lstrip("#").strip() or fallback
        break
    return fallback


def markdown_body(text: str) -> str:
    return text.strip()


def word_count(text: str) -> int:
    return len(re.findall(r"\S+", text))


def _sanitize_json_value(value: Any) -> Any:
    if isinstance(value, str):
        return value.replace("\u2028", " ").replace("\u2029", " ")
    if isinstance(value, list):
        return [_sanitize_json_value(item) for item in value]
    if isinstance(value, dict):
        return {key: _sanitize_json_value(item) for key, item in value.items()}
    return value


def dump_jsonl(path: Path, records: list[dict[str, Any]]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8") as fh:
        for record in records:
            if is_dataclass(record):
                record = asdict(record)
            record = _sanitize_json_value(record)
            fh.write(json.dumps(record, ensure_ascii=False, sort_keys=True))
            fh.write("\n")
