# core/crbx_sia.py
import zipfile
import re
from pathlib import Path
from typing import Dict, Tuple, Optional, Any

def _decode_bytes(data: bytes) -> str:
    for enc in ("cp1252", "latin-1", "utf-8"):
        try:
            return data.decode(enc)
        except UnicodeDecodeError:
            continue
    return data.decode("utf-8", errors="ignore")

def detect_record_file_in_crbx(crbx_path: Path) -> Tuple[str, str]:
    with zipfile.ZipFile(crbx_path, "r") as zf:
        names = zf.namelist()
        candidates = [n for n in names if n.lower().endswith((".e1s", ".01s"))]
        if not candidates:
            raise ValueError("CRBX enthält weder .e1s noch .01s.")
        candidates.sort(key=lambda s: (not s.lower().endswith(".e1s"), s.lower()))
        name = candidates[0]
        ext = ".e1s" if name.lower().endswith(".e1s") else ".01s"
        return name, ext

def _read_record_text(crbx_path: Path, member_name: str) -> str:
    with zipfile.ZipFile(crbx_path, "r") as zf:
        return _decode_bytes(zf.read(member_name))

# --- Universal Parser ---

G_RECORD_RE = re.compile(r"^\s*G(?P<chap>\w{1,3})\s+(?P<pos>\d{6})\s+(?P<t>\d)\s*(?P<rest>.*)$")
B_RECORD_RE = re.compile(r"^\s*B(?P<chap>\w{1,3})\s+(?P<pos>\d{6})\s+(?P<ep_raw>[A-Z][+-]\d{13})\s+(?P<gp_raw>[A-Z][+-]\d{13})")

def _parse_records_universal(text: str) -> Dict[str, Dict[str, Any]]:
    out: Dict[str, Dict[str, Any]] = {}
    for line in text.splitlines():
        g_match = G_RECORD_RE.match(line)
        if g_match:
            groups = g_match.groupdict()
            pos6, t, rest = groups['pos'], groups['t'], groups['rest'].rstrip()
            base_key = f"{groups['chap']}.{pos6[:3]}.{pos6[3:]}".replace(" ", "")
            key = base_key
            
            if t == '6':
                parts = rest.strip().split()
                mengenart, qty_str, ep_str = None, None, None

                # Logische Zuordnung der Teile basierend auf Mustern
                for i, part in enumerate(parts):
                    if re.fullmatch(r"[AWQ]\+[0-9]{13}", part):
                        qty_str = part
                        # Alles davor ist die Mengenart
                        if i > 0:
                            mengenart = " ".join(parts[:i])
                        break # Wichtig: Nach der Menge aufhören, weiter zu suchen
                
                # Finde den Preis, der nicht die Menge ist
                for part in parts:
                    if re.fullmatch(r"[+-][0-9]+", part):
                        ep_str = part
                        break

                if mengenart:
                    key = f"{base_key}.{mengenart}"
                
                slot = out.setdefault(key, {})
                
                if qty_str: slot["qty"] = int(qty_str[2:]) / 1000.0
                if ep_str:
                    slot["ep"] = float(ep_str) / 100.0
                    if slot.get("qty"): slot["gp"] = slot.get("qty") * slot.get("ep")

            else:
                slot = out.setdefault(key, {})
                if t == "2":
                    current_text = slot.get("text", "") or ""
                    slot["text"] = (current_text + " " + rest.strip()).strip()
                elif t == "5":
                    slot["unit"] = (rest.strip().split()[-1] if rest.strip() else None)
            continue

        b_match = B_RECORD_RE.match(line)
        if b_match:
            pos6 = b_match.group("pos")
            key = f"{b_match.group('chap')}.{pos6[:3]}.{pos6[3:]}".replace(" ", "")
            slot = out.setdefault(key, {})
            ep_raw, gp_raw = b_match.group("ep_raw"), b_match.group("gp_raw")
            if ep_raw: slot["ep"] = int(ep_raw[2:]) / 100.0
            if gp_raw: slot["gp"] = int(gp_raw[2:]) / 100.0
            continue
            
    return {k: v for k, v in out.items() if any(v.values()) and not k.endswith("000.000")}

def parse_lv_crbx(crbx_path: Path) -> Dict[str, Dict[str, Any]]:
    member, _ = detect_record_file_in_crbx(crbx_path)
    txt = _read_record_text(crbx_path, member)
    return _parse_records_universal(txt)

def parse_offer_crbx(crbx_path: Path) -> Tuple[Optional[str], Dict[str, Dict[str, Any]]]:
    member, _ = detect_record_file_in_crbx(crbx_path)
    txt = _read_record_text(crbx_path, member)
    data = _parse_records_universal(txt)
    return None, data