#!/usr/bin/env python3
"""Crée un brouillon Markdown adapté à une section du site."""

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

from _schema import SECTION_ALIASES, VOCABULARY_FIELDS

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

MZL_RE = re.compile(r"(?:mzl-)?([0-9]{1,3})")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Créer un brouillon Markdown horodaté et prêt à compléter."
    )
    parser.add_argument(
        "section",
        choices=sorted(SECTION_ALIASES),
        help="section de destination",
    )
    parser.add_argument(
        "identifiant",
        help="nom du fichier sans .md ; pour un signe, numéro MZL ou mzl-XXX",
    )
    parser.add_argument(
        "--title",
        help="titre initial ; déduit de l’identifiant par défaut",
    )
    parser.add_argument(
        "--nature",
        choices=tuple(VOCABULARY_FIELDS),
        help="nature grammaticale d’une fiche de vocabulaire",
    )
    return parser.parse_args()


def normalize(value: str) -> str:
    return unicodedata.normalize("NFC", value.strip())


def toml_string(value: str) -> str:
    return json.dumps(value, ensure_ascii=False)


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 validate_identifier(identifier: str, *, allow_leading_hyphen: bool = False) -> str:
    """Valide un identifiant déjà écrit sous sa forme de nom de fichier."""
    value = normalize(identifier)
    if value.startswith("-") and not allow_leading_hyphen:
        raise ValueError("seul le vocabulaire accepte un identifiant commençant par un tiret")

    body = value[1:] if value.startswith("-") else value
    if not body or value in {".", "..", "_index"}:
        raise ValueError("identifiant de fichier invalide")
    if value != value.lower():
        raise ValueError("l’identifiant doit être écrit en minuscules")
    if value.endswith("-") or "--" in value:
        raise ValueError(
            "l’identifiant ne doit pas finir par un tiret ni en contenir deux de suite"
        )
    if not all(character.isalnum() or character == "-" for character in body):
        raise ValueError(
            "l’identifiant ne doit contenir que des lettres, des chiffres et des tirets"
        )
    return value


def parse_mzl(identifier: str) -> tuple[str, str]:
    value = normalize(identifier)
    match = MZL_RE.fullmatch(value)
    if match is None:
        raise ValueError(
            "un signe doit être identifié par un numéro MZL de un à trois chiffres ASCII"
        )

    number = int(match.group(1))
    if not 1 <= number <= 999:
        raise ValueError("le numéro MZL doit être compris entre 1 et 999")

    mzl = f"{number:03d}"
    return f"mzl-{mzl}", mzl


def choose_nature(nature: str | None) -> str:
    if nature:
        return nature
    if not sys.stdin.isatty():
        raise ValueError("--nature est requis pour une fiche de vocabulaire")

    choices = tuple(VOCABULARY_FIELDS)
    print("Nature grammaticale :")
    for index, choice in enumerate(choices, start=1):
        print(f"  {index}. {choice}")

    try:
        answer = input("Choix : ").strip()
    except (EOFError, KeyboardInterrupt) as exc:
        raise ValueError("sélection interrompue") from exc

    if not answer.isdigit() or not 1 <= int(answer) <= len(choices):
        raise ValueError("nature grammaticale invalide")
    return choices[int(answer) - 1]


def title_from_identifier(identifier: str) -> str:
    title = identifier.replace("-", " ")
    return title[:1].upper() + title[1:]


def build_vocabulary(title: str, timestamp: str, nature: str) -> str:
    fields = "\n".join(
        f"{field} = []" if field == "logograms" else f'{field} = ""'
        for field in VOCABULARY_FIELDS[nature]
    )
    return f"""+++
title = {toml_string(title)}
date = {timestamp}
# updated = YYYY-MM-DD
description = ""
draft = true

[extra]
{fields}

[taxonomies]
natures = [{toml_string(nature)}]
+++
"""


def build_sign(title: str, timestamp: str, mzl: str) -> str:
    return f"""+++
title = {toml_string(title)}
date = {timestamp}
# updated = YYYY-MM-DD
description = ""
draft = true

[extra]
sign = ""
mzl = "{mzl}"

[taxonomies]
lectures = []
+++
"""


def build_page(section: str, title: str, timestamp: str) -> str:
    if section == "articles":
        return f"""+++
title = {toml_string(title)}
date = {timestamp}
# updated = YYYY-MM-DD
description = ""
draft = true

[extra]
banner = ""
banner_alt = ""
banner_credit = ""
+++
"""

    if section == "grammaire":
        return f"""+++
title = {toml_string(title)}
date = {timestamp}
# updated = YYYY-MM-DD
description = ""
draft = true

[taxonomies]
themes = []
+++
"""

    if section == "textes":
        return f"""+++
title = {toml_string(title)}
date = {timestamp}
# updated = YYYY-MM-DD
description = ""
draft = true

[taxonomies]
genres = []
+++
"""

    raise ValueError(f"section non prise en charge : {section}")


def main() -> int:
    args = parse_args()
    section = SECTION_ALIASES[args.section]

    try:
        if section != "vocabulaire" and args.nature is not None:
            raise ValueError("--nature est réservé aux fiches de vocabulaire")

        timestamp = current_timestamp()
        custom_title = normalize(args.title) if args.title is not None else None

        if section == "signes":
            filename, mzl = parse_mzl(args.identifiant)
            title = custom_title if custom_title is not None else ""
            content = build_sign(title, timestamp, mzl)
        else:
            filename = validate_identifier(
                args.identifiant,
                allow_leading_hyphen=section == "vocabulaire",
            )
            if section == "vocabulaire":
                nature = choose_nature(args.nature)
                title = custom_title if custom_title is not None else filename
                content = build_vocabulary(title, timestamp, nature)
            else:
                title = (
                    custom_title
                    if custom_title is not None
                    else title_from_identifier(filename)
                )
                content = build_page(section, title, timestamp)
    except (ValueError, RuntimeError) as exc:
        print(f"Erreur : {exc}", file=sys.stderr)
        return 2

    destination = CONTENT_DIR / section / f"{filename}.md"
    try:
        destination.parent.mkdir(parents=True, exist_ok=True)
        with destination.open("x", encoding="utf-8") as stream:
            stream.write(content)
    except FileExistsError:
        print(
            f"Erreur : le fichier existe déjà : {destination.relative_to(ROOT_DIR)}",
            file=sys.stderr,
        )
        return 1
    except OSError as exc:
        print(
            f"Erreur : impossible de créer {destination.relative_to(ROOT_DIR)} ({exc})",
            file=sys.stderr,
        )
        return 1

    print(f"Brouillon créé : {destination.relative_to(ROOT_DIR)}")
    print("Compléter la fiche, passer draft à false, puis lancer :")
    print("  ./scripts/check-content.py")
    return 0


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