from __future__ import annotations

import re
from typing import Any


SECTION_RE = re.compile(r"^(#{1,3})\s+(.+)$", re.M)


def split_markdown_sections(md: str) -> list[dict[str, Any]]:
    """Split markdown into titled sections for partial accept/reject."""
    text = md or ""
    matches = list(SECTION_RE.finditer(text))
    if not matches:
        return [{"id": "body", "title": "Contenido", "level": 1, "content": text.strip()}]

    sections: list[dict[str, Any]] = []
    # preamble before first heading
    if matches[0].start() > 0:
        pre = text[: matches[0].start()].strip()
        if pre:
            sections.append({"id": "preamble", "title": "Encabezado", "level": 1, "content": pre})

    for i, m in enumerate(matches):
        start = m.start()
        end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
        block = text[start:end].strip()
        title = m.group(2).strip()
        level = len(m.group(1))
        sid = re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-") or f"sec-{i}"
        sections.append({"id": f"{sid}-{i}", "title": title, "level": level, "content": block})
    return sections


def merge_sections(
    base_md: str,
    proposed_md: str,
    accepted_ids: list[str] | None,
    accept_all: bool = False,
) -> str:
    base_secs = split_markdown_sections(base_md)
    prop_secs = split_markdown_sections(proposed_md)
    if accept_all or not accepted_ids:
        return proposed_md.strip()

    accepted = set(accepted_ids)
    # Map proposed by normalized title
    prop_by_title = {s["title"].lower(): s for s in prop_secs}
    out: list[str] = []
    used_titles: set[str] = set()

    for b in base_secs:
        key = b["title"].lower()
        # Find matching proposed section by title similarity
        prop = prop_by_title.get(key)
        if not prop:
            # try id match
            prop = next((p for p in prop_secs if p["id"] in accepted and p["title"].lower() == key), None)
        if prop and prop["id"] in accepted:
            out.append(prop["content"])
            used_titles.add(prop["title"].lower())
        else:
            out.append(b["content"])

    # Append accepted proposed sections that didn't exist in base
    for p in prop_secs:
        if p["id"] in accepted and p["title"].lower() not in used_titles:
            out.append(p["content"])

    return "\n\n".join(s for s in out if s).strip()
