#!/usr/bin/env python3
"""Horodate la dernière modification substantielle d’une fiche."""

import argparse
import re
import sys
import tomllib
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

from _schema import KNOWN_SECTIONS

ROOT_DIR = Path(__file__).resolve().parent.parent
CONTENT_DIR = ROOT_DIR / "src" / "content"
TIMEZONE_NAME = "Europe/Paris"

UPDATED_RE = re.compile(r"\s*(?:#\s*)?updated\s*=.*")
DATE_RE = re.compile(r"\s*date\s*=.*")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Horodater la dernière modification substantielle d’une fiche."
    )
    parser.add_argument(
        "fiche",
        help=(
            "chemin depuis la racine du dépôt ou depuis src/content/ "
            "(par exemple vocabulaire/bītum.md)"
        ),
    )
    return parser.parse_args()


def current_timestamp() -> str:
    try:
        timezone = ZoneInfo(TIMEZONE_NAME)
    except ZoneInfoNotFoundError as exc:
        raise RuntimeError(
            f"fuseau {TIMEZONE_NAME} indisponible ; installer les données de fuseaux horaires"
        ) from exc
    return datetime.now(timezone).isoformat(timespec="seconds")


def resolve_content_path(value: str) -> Path:
    supplied = Path(value).expanduser()
    if supplied.is_absolute():
        path = supplied
    elif supplied.parts[:2] == ("src", "content"):
        path = ROOT_DIR / supplied
    else:
        path = CONTENT_DIR / supplied

    path = path.resolve()
    try:
        relative = path.relative_to(CONTENT_DIR.resolve())
    except ValueError as exc:
        raise ValueError("la fiche doit se trouver dans src/content/") from exc

    if not path.is_file():
        raise ValueError(f"fichier introuvable : {path}")
    if path.suffix != ".md" or path.name == "_index.md":
        raise ValueError("le chemin doit désigner une fiche Markdown, pas un index")
    if len(relative.parts) < 2 or relative.parts[0] not in KNOWN_SECTIONS:
        raise ValueError("la fiche doit appartenir à une section éditoriale connue")
    return path


def update_front_matter(path: Path, timestamp: str) -> None:
    try:
        text = path.read_text(encoding="utf-8")
    except (OSError, UnicodeDecodeError) as exc:
        raise ValueError(f"lecture impossible ({exc})") from exc

    lines = text.splitlines(keepends=True)
    if not lines or lines[0].rstrip("\r\n") != "+++":
        raise ValueError("front matter TOML initial absent")

    try:
        closing_index = next(
            index
            for index, line in enumerate(lines[1:], start=1)
            if line.rstrip("\r\n") == "+++"
        )
    except StopIteration as exc:
        raise ValueError("délimiteur final +++ absent") from exc

    try:
        tomllib.loads("".join(lines[1:closing_index]))
    except tomllib.TOMLDecodeError as exc:
        raise ValueError(f"front matter TOML invalide ({exc})") from exc

    updated_indexes = [
        index
        for index in range(1, closing_index)
        if UPDATED_RE.fullmatch(lines[index].rstrip("\r\n")) is not None
    ]
    if len(updated_indexes) > 1:
        raise ValueError("plusieurs champs updated sont présents dans le front matter")

    newline = "\r\n" if lines[0].endswith("\r\n") else "\n"
    updated_line = f"updated = {timestamp}{newline}"

    if updated_indexes:
        lines[updated_indexes[0]] = updated_line
    else:
        try:
            date_index = next(
                index
                for index in range(1, closing_index)
                if DATE_RE.fullmatch(lines[index].rstrip("\r\n")) is not None
            )
        except StopIteration as exc:
            raise ValueError("champ date absent du front matter") from exc
        lines.insert(date_index + 1, updated_line)

    try:
        path.write_text("".join(lines), encoding="utf-8")
    except OSError as exc:
        raise ValueError(f"écriture impossible ({exc})") from exc


def main() -> int:
    args = parse_args()
    try:
        path = resolve_content_path(args.fiche)
        timestamp = current_timestamp()
        update_front_matter(path, timestamp)
    except (ValueError, RuntimeError) as exc:
        print(f"Erreur : {exc}", file=sys.stderr)
        return 1

    print(f"Fiche mise à jour : {path.relative_to(ROOT_DIR)}")
    print(f"updated = {timestamp}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
