"""Mermaid block collection/restoration and xls→xlsx conversion.

Promoted from ``scripts/inject_headers_and_parse_ks.py``.
"""
from __future__ import annotations

import logging
import os
import re
import shutil
import subprocess
import tempfile
import xml.etree.ElementTree as ET
import zipfile
from pathlib import Path
from typing import List, Tuple

from src.extraction_v2.xlsx_header_injection import (
    NS,
    _load_shared_strings,
    _read_cell_text,
)

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# .xls -> .xlsx (LibreOffice headless)
# ---------------------------------------------------------------------------

def convert_xls_to_xlsx(xls_path: str, outdir: str) -> str:
    soffice = shutil.which("soffice") or shutil.which("libreoffice")
    if not soffice:
        raise RuntimeError("LibreOffice (soffice) not found; required to convert .xls")
    os.makedirs(outdir, exist_ok=True)
    proc = subprocess.run(
        [soffice, "--headless", "--convert-to", "xlsx", "--outdir", outdir, xls_path],
        capture_output=True, text=True, timeout=300,
    )
    if proc.returncode != 0:
        raise RuntimeError(f"soffice conversion failed:\n{proc.stderr}")
    converted = os.path.join(outdir, Path(xls_path).stem + ".xlsx")
    if not os.path.exists(converted):
        raise RuntimeError(f"Expected converted file not found: {converted}")
    return converted


# ---------------------------------------------------------------------------
# Mermaid block collection + restoration
# ---------------------------------------------------------------------------

def collect_mermaid_blocks(xlsx_path: str) -> List[str]:
    workdir = tempfile.mkdtemp(prefix="mermaid_collect_")
    try:
        with zipfile.ZipFile(xlsx_path) as z:
            z.extractall(workdir)

        ss_path = os.path.join(workdir, "xl/sharedStrings.xml")
        shared = _load_shared_strings(ET.parse(ss_path).getroot()) if os.path.exists(ss_path) else []

        out: List[str] = []
        ws_dir = os.path.join(workdir, "xl/worksheets")
        for fname in sorted(os.listdir(ws_dir)):
            if not fname.endswith(".xml"):
                continue
            root = ET.parse(os.path.join(ws_dir, fname)).getroot()
            sd = root.find(f"{{{NS}}}sheetData")
            if sd is None:
                continue
            for row in sd.findall(f"{{{NS}}}row"):
                for c in row.findall(f"{{{NS}}}c"):
                    text = _read_cell_text(c, shared)
                    if text and text.lstrip().startswith("```mermaid"):
                        out.append(text)
        return out
    finally:
        shutil.rmtree(workdir, ignore_errors=True)


def restore_mermaid_blocks(md: str, mermaid_texts: List[str]) -> Tuple[str, int]:
    restored = 0
    for original in mermaid_texts:
        tokens = original.split()
        if not tokens:
            continue
        flex = r"\s+".join(re.escape(t) for t in tokens)
        pattern = r"\|?\s*" + flex + r"\s*\|?"
        replacement = "\n\n" + original + "\n\n"
        new_md, n = re.subn(pattern, replacement, md, count=1)
        if n == 0:
            logger.warning("could not relocate mermaid block (%d chars)", len(original))
            continue
        md = new_md
        restored += 1
    return md, restored
