"""
validate-markdown.py — Validador de sintaxis Markdown para skills del sistema SWL

Uso:
  python scripts/validate-markdown.py                                          # valida todos los .md
  python scripts/validate-markdown.py --path habilidades/django-experto/SKILL.md
  python scripts/validate-markdown.py --check                                  # modo CI: exit 1 si hay errores

Valida SKILL.md y recursos/*.md de todas las habilidades en ../habilidades.
Exit code 0 = éxito o solo advertencias; 1 = hay errores.
"""

import argparse
import re
import sys
from pathlib import Path


# ---------------------------------------------------------------------------
# Constantes
# ---------------------------------------------------------------------------
REGEX_APERTURA_BLOQUE = re.compile(r"^```(\w*)\s*$")
REGEX_CIERRE_BLOQUE = re.compile(r"^```\s*$")
REGEX_FILA_TABLA = re.compile(r"^\s*\|.*\|\s*$")
REGEX_SEPARADOR_TABLA = re.compile(r"^\s*\|[\s|:\-]+\|\s*$")
REGEX_COMENTARIO_HTML = re.compile(r"<!--.*?-->", re.DOTALL)
REGEX_ENCABEZADO = re.compile(r"^(#{1,6})\s+\S")


# ---------------------------------------------------------------------------
# Tipos de resultado
# ---------------------------------------------------------------------------
def _problema(tipo: str, codigo: str, linea: int | None, mensaje: str) -> dict:
    return {"type": tipo, "code": codigo, "line": linea, "message": mensaje}


def _error(codigo: str, linea: int | None, mensaje: str) -> dict:
    return _problema("error", codigo, linea, mensaje)


def _advertencia(codigo: str, linea: int | None, mensaje: str) -> dict:
    return _problema("warning", codigo, linea, mensaje)


# ---------------------------------------------------------------------------
# Validación de bloques de código
# ---------------------------------------------------------------------------
def _validar_bloques_codigo(lineas: list[str]) -> list[dict]:
    issues: list[dict] = []
    dentro_bloque = False
    linea_apertura = 0

    for i, linea in enumerate(lineas, start=1):
        if not dentro_bloque:
            if REGEX_APERTURA_BLOQUE.match(linea):
                dentro_bloque = True
                linea_apertura = i
        else:
            if REGEX_CIERRE_BLOQUE.match(linea):
                dentro_bloque = False

    if dentro_bloque:
        issues.append(_error(
            "UNCLOSED_CODE_BLOCK",
            linea_apertura,
            f"Bloque de codigo abierto en linea {linea_apertura} nunca se cierra"
        ))

    return issues


# ---------------------------------------------------------------------------
# Validación de tablas
# ---------------------------------------------------------------------------
def _contar_columnas(fila: str) -> int:
    """Cuenta el número de columnas en una fila de tabla Markdown."""
    fila = fila.strip()
    if fila.startswith("|"):
        fila = fila[1:]
    if fila.endswith("|"):
        fila = fila[:-1]
    return len(fila.split("|"))


def _validar_tablas(lineas: list[str]) -> list[dict]:
    issues: list[dict] = []
    dentro_bloque = False
    i = 0

    while i < len(lineas):
        linea = lineas[i]
        num_linea = i + 1

        # No validar tablas dentro de bloques de código
        if not dentro_bloque and REGEX_APERTURA_BLOQUE.match(linea):
            dentro_bloque = True
        elif dentro_bloque and REGEX_CIERRE_BLOQUE.match(linea):
            dentro_bloque = False
        elif not dentro_bloque and REGEX_FILA_TABLA.match(linea):
            # Inicio potencial de tabla: fila de encabezado
            fila_encabezado = linea
            num_cols_encabezado = _contar_columnas(fila_encabezado)
            num_linea_encabezado = num_linea

            # Verificar si la siguiente línea es el separador
            if i + 1 < len(lineas):
                linea_siguiente = lineas[i + 1]
                if not REGEX_SEPARADOR_TABLA.match(linea_siguiente):
                    # Puede ser tabla sin separador solo si la línea siguiente también es fila de tabla
                    if REGEX_FILA_TABLA.match(linea_siguiente):
                        issues.append(_error(
                            "TABLE_MISSING_SEPARATOR",
                            num_linea,
                            f"Tabla en linea {num_linea} no tiene separador (segunda fila debe tener |---|)"
                        ))
                else:
                    # Tiene separador: avanzar y validar columnas del resto
                    i += 2  # saltar encabezado y separador
                    while i < len(lineas) and REGEX_FILA_TABLA.match(lineas[i]):
                        num_cols_fila = _contar_columnas(lineas[i])
                        if num_cols_fila != num_cols_encabezado:
                            issues.append(_error(
                                "TABLE_INCONSISTENT_COLUMNS",
                                i + 1,
                                f"Fila {i + 1} de tabla tiene {num_cols_fila} columna(s) "
                                f"pero el encabezado (linea {num_linea_encabezado}) define {num_cols_encabezado}"
                            ))
                        # Advertencia: comentarios HTML dentro de celdas de tabla
                        if REGEX_COMENTARIO_HTML.search(lineas[i]):
                            issues.append(_advertencia(
                                "HTML_COMMENT_IN_TABLE",
                                i + 1,
                                f"Comentario HTML en fila {i + 1} de tabla; puede romper el parsing en algunas herramientas"
                            ))
                        i += 1
                    continue  # ya avanzamos manualmente

        i += 1

    return issues


# ---------------------------------------------------------------------------
# Validación de encabezados (salto de nivel)
# ---------------------------------------------------------------------------
def _validar_encabezados(lineas: list[str]) -> list[dict]:
    issues: list[dict] = []
    dentro_bloque = False
    nivel_anterior = 0

    for i, linea in enumerate(lineas, start=1):
        if not dentro_bloque and REGEX_APERTURA_BLOQUE.match(linea):
            dentro_bloque = True
            continue
        if dentro_bloque and REGEX_CIERRE_BLOQUE.match(linea):
            dentro_bloque = False
            continue
        if dentro_bloque:
            continue

        m = REGEX_ENCABEZADO.match(linea)
        if m:
            nivel_actual = len(m.group(1))
            if nivel_anterior > 0 and nivel_actual > nivel_anterior + 1:
                issues.append(_advertencia(
                    "HEADING_LEVEL_SKIP",
                    i,
                    f"Encabezado salta de H{nivel_anterior} a H{nivel_actual} en linea {i} "
                    f"(se omite H{nivel_anterior + 1})"
                ))
            nivel_anterior = nivel_actual

    return issues


# ---------------------------------------------------------------------------
# Validación de un archivo individual
# ---------------------------------------------------------------------------
def validar_archivo(ruta: Path) -> dict:
    """
    Valida un archivo Markdown.
    Devuelve dict con claves: archivo, status, issues.
    """
    issues: list[dict] = []

    if not ruta.exists():
        return {
            "archivo": str(ruta),
            "status": "error",
            "issues": [_error("FILE_NOT_FOUND", None, f"Archivo no encontrado: {ruta}")]
        }

    contenido = ruta.read_text(encoding="utf-8", errors="replace")
    lineas = contenido.splitlines()

    issues.extend(_validar_bloques_codigo(lineas))
    issues.extend(_validar_tablas(lineas))
    issues.extend(_validar_encabezados(lineas))

    tiene_error = any(i["type"] == "error" for i in issues)
    tiene_advertencia = any(i["type"] == "warning" for i in issues)

    if tiene_error:
        estado = "error"
    elif tiene_advertencia:
        estado = "warning"
    else:
        estado = "ok"

    return {"archivo": str(ruta), "status": estado, "issues": issues}


# ---------------------------------------------------------------------------
# Recolección de archivos a validar
# ---------------------------------------------------------------------------
def recolectar_archivos_markdown(dir_habilidades: Path) -> list[Path]:
    """Devuelve todos los SKILL.md y recursos/*.md bajo dir_habilidades."""
    archivos: list[Path] = []

    for skill_dir in sorted(dir_habilidades.iterdir()):
        if not skill_dir.is_dir():
            continue

        skill_md = skill_dir / "SKILL.md"
        if skill_md.exists():
            archivos.append(skill_md)

        recursos_dir = skill_dir / "recursos"
        if recursos_dir.is_dir():
            for archivo in sorted(recursos_dir.iterdir()):
                if archivo.suffix.lower() == ".md":
                    archivos.append(archivo)

    return archivos


# ---------------------------------------------------------------------------
# Presentación de resultados
# ---------------------------------------------------------------------------
def _iconos() -> dict[str, str]:
    """Devuelve iconos Unicode si el terminal los soporta, ASCII en caso contrario."""
    enc = getattr(sys.stdout, "encoding", "") or ""
    if enc.lower().replace("-", "") in ("utf8", "utf16", "utf32"):
        return {"ok": "\u2713", "warning": "\u26a0", "error": "\u2717"}
    return {"ok": "[OK]", "warning": "[WARN]", "error": "[ERR]"}


def imprimir_resultado_normal(resultado: dict, raiz: Path, iconos: dict[str, str]) -> None:
    """Imprime el resultado de un archivo, mostrando la ruta relativa a raiz."""
    ruta_abs = Path(resultado["archivo"])
    try:
        ruta_mostrar = ruta_abs.relative_to(raiz)
    except ValueError:
        ruta_mostrar = ruta_abs

    estado = resultado["status"]
    icono = iconos.get(estado, "?")
    issues = resultado["issues"]

    if estado == "ok":
        print(f"{icono} {ruta_mostrar} - OK")
    else:
        for issue in issues:
            prefijo = "ERROR" if issue["type"] == "error" else "ADVERTENCIA"
            ref_linea = f" (linea {issue['line']})" if issue.get("line") else ""
            print(f"{icono} {ruta_mostrar}{ref_linea} - {prefijo}: {issue['message']}")


# ---------------------------------------------------------------------------
# Punto de entrada
# ---------------------------------------------------------------------------
def main() -> int:
    parser = argparse.ArgumentParser(
        description="Validador de sintaxis Markdown para skills del sistema SWL"
    )
    parser.add_argument(
        "--path",
        metavar="RUTA",
        help="Validar un archivo Markdown específico"
    )
    parser.add_argument(
        "--check",
        action="store_true",
        help="Modo CI: exit 1 si hay errores"
    )
    args = parser.parse_args()

    raiz = Path(__file__).parent.parent
    dir_habilidades = raiz / "habilidades"

    if args.path:
        # Ruta puede ser relativa a la raíz del proyecto o absoluta
        ruta_archivo = Path(args.path)
        if not ruta_archivo.is_absolute():
            ruta_archivo = raiz / ruta_archivo
        archivos = [ruta_archivo]
    else:
        if not dir_habilidades.is_dir():
            print(
                f"ERROR: Directorio de habilidades no encontrado: {dir_habilidades}",
                file=sys.stderr
            )
            return 1
        archivos = recolectar_archivos_markdown(dir_habilidades)

    if not archivos:
        print("No se encontraron archivos Markdown para validar.")
        return 0

    resultados: list[dict] = []
    for ruta in archivos:
        resultado = validar_archivo(ruta)
        resultados.append(resultado)

    # Contadores
    n_ok = sum(1 for r in resultados if r["status"] == "ok")
    n_warnings = sum(1 for r in resultados if r["status"] == "warning")
    n_errors = sum(1 for r in resultados if r["status"] == "error")

    iconos = _iconos()
    for resultado in resultados:
        imprimir_resultado_normal(resultado, raiz, iconos)

    total = len(resultados)
    partes = [f"{total} archivo(s) validado(s)", f"{n_ok} OK"]
    if n_warnings:
        partes.append(f"{n_warnings} advertencia(s)")
    if n_errors:
        partes.append(f"{n_errors} error(es)")
    print(f"\nResumen: {', '.join(partes)}")

    if args.check and n_errors > 0:
        return 1

    return 0


if __name__ == "__main__":
    sys.exit(main())
