from __future__ import annotations

import re
import shutil
import unicodedata
import os
from pathlib import Path
from typing import Dict, Iterable, Optional


ROOT = Path(__file__).resolve().parents[1]
DOCS_OUT = ROOT / ".mkdocs" / "docs"
WEB_ASSETS = ROOT / "webapp" / "assets"

SOURCE_DIRS = [
    "Belgica",
    "Conocimiento_General",
    "Control_Factual",
    "Plantillas",
    "Recursos",
]

ROOT_FILES = [
    "00_Inicio.md",
    "README.md",
    "OPERACIONES_GUIDE_LIBRARY.md",
    "GUIA_DESPLIEGUE_GUIDE_LIBRARY.md",
]

SUPPORT_FILES = [
    "mkdocs.yml",
    "scripts/build_guide_library.py",
]


def slugify(value: str) -> str:
    value = unicodedata.normalize("NFKD", value)
    value = "".join(ch for ch in value if not unicodedata.combining(ch))
    value = value.lower().strip()
    value = re.sub(r"[^\w\s-]", "", value, flags=re.UNICODE)
    value = re.sub(r"[\s_]+", "-", value)
    value = re.sub(r"-+", "-", value)
    return value.strip("-")


def iter_source_markdown() -> Iterable[Path]:
    for file_name in ROOT_FILES:
        path = ROOT / file_name
        if path.exists():
            yield path

    for dir_name in SOURCE_DIRS:
        base = ROOT / dir_name
        if base.exists():
            yield from sorted(base.rglob("*.md"))


def relative_to_root(path: Path) -> Path:
    return path.relative_to(ROOT)


def build_stem_map(files: Iterable[Path]) -> Dict[str, list[Path]]:
    mapping: Dict[str, list[Path]] = {}
    for path in files:
        mapping.setdefault(path.stem, []).append(relative_to_root(path))
    return mapping


def resolve_wikilink_target(
    current_source: Path,
    raw_target: str,
    stem_map: Dict[str, list[Path]],
) -> Optional[str]:
    target, _, heading = raw_target.partition("#")

    if not target:
        return f"#{slugify(heading)}" if heading else None

    target = target.strip()
    candidates = stem_map.get(Path(target).stem, [])
    if not candidates:
        return None

    current_dir = relative_to_root(current_source).parent
    preferred = None
    for candidate in candidates:
        if candidate.parent == current_dir:
            preferred = candidate
            break
    if preferred is None:
        preferred = candidates[0]

    current_output = output_relative_path(current_source)
    href = os.path.relpath(preferred, start=current_output.parent).replace("\\", "/")
    if heading:
        href += f"#{slugify(heading)}"
    return href


WIKILINK_RE = re.compile(r"\[\[([^\]|]+)(?:\|([^\]]+))?\]\]")


def convert_wikilinks(text: str, source: Path, stem_map: Dict[str, list[Path]]) -> str:
    def replace(match: re.Match[str]) -> str:
        target = match.group(1).strip()
        label = (match.group(2) or target.split("#", 1)[-1] or target).strip()
        href = resolve_wikilink_target(source, target, stem_map)
        if href is None:
            return label
        return f"[{label}]({href})"

    return WIKILINK_RE.sub(replace, text)


def output_relative_path(source: Path) -> Path:
    rel = relative_to_root(source)
    if rel.name == "00_Inicio.md":
        return Path("index.md")
    if rel.name == "README.md":
        return Path("Estado_Repositorio.md")
    return rel


def copy_markdown(source: Path, stem_map: Dict[str, list[Path]]) -> None:
    destination = DOCS_OUT / output_relative_path(source)

    destination.parent.mkdir(parents=True, exist_ok=True)
    text = source.read_text(encoding="utf-8")
    text = convert_wikilinks(text, source, stem_map)
    if relative_to_root(source).name == "README.md":
        text = text.replace("(00_Inicio.md)", "(index.md)")
    destination.write_text(text, encoding="utf-8", newline="\n")


def copy_static_assets() -> None:
    if not WEB_ASSETS.exists():
        return

    destination = DOCS_OUT / "assets"
    if destination.exists():
        shutil.rmtree(destination)
    shutil.copytree(WEB_ASSETS, destination)


def copy_support_files() -> None:
    for file_name in SUPPORT_FILES:
        source = ROOT / file_name
        if not source.exists():
            continue
        destination = DOCS_OUT / file_name
        destination.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(source, destination)


def main() -> None:
    files = list(iter_source_markdown())

    if DOCS_OUT.exists():
        shutil.rmtree(DOCS_OUT)
    DOCS_OUT.mkdir(parents=True, exist_ok=True)

    stem_map = build_stem_map(files)
    for source in files:
        copy_markdown(source, stem_map)

    copy_static_assets()
    copy_support_files()


if __name__ == "__main__":
    main()
