#!/usr/bin/env python3
import re
import sys
from pathlib import Path
from typing import List, Tuple, Optional

ENTRY_START_RE = re.compile(r'\[(\d+)\]\s*=\s*\{', re.MULTILINE)

PAIR_RE = re.compile(
    r'([A-Za-z_]\w*)\s*=\s*"((?:\\.|[^"\\])*)"',
    re.MULTILINE
)

def read_text_with_fallback(p: Path) -> str:
    for enc in ("utf-8", "utf-8-sig", "cp950", "big5", "gbk", "gb18030"):
        try:
            return p.read_text(encoding=enc, errors="strict")
        except UnicodeDecodeError:
            continue
    return p.read_text(encoding="utf-8", errors="ignore")

def unescape_lua_double_quoted(s: str) -> str:
    s = s.replace(r'\\', '\\').replace(r'\"', '"')
    s = s.replace(r'\n', ' ')
    s = s.replace(r'\r', ' ')
    s = s.replace(r'\t', ' ')
    return " ".join(s.split())

def csv_escape(token: str) -> str:
    if any(c in token for c in (',', '"')):
        token = token.replace('"', '""')
        return f'"{token}"'
    return token

def usage(code: int = 2) -> None:
    prog = Path(sys.argv[0]).name
    sys.stderr.write(
        "Usage:\n"
        f"  {prog} <lua_file> [--out <output_file>] [--vals-only]\n\n"
        "Output formats:\n"
        "  default     : id,key1,val1,key2,val2,...\n"
        "  --vals-only : id,val1,val2,...\n"
    )
    sys.exit(code)

def parse_args(argv: List[str]) -> Tuple[Path, Optional[Path], bool]:
    args = list(argv)
    if not args:
        usage()

    out_path: Optional[Path] = None
    if "--out" in args:
        i = args.index("--out")
        if i + 1 >= len(args):
            usage()
        out_path = Path(args[i + 1])
        args = args[:i] + args[i + 2:]

    vals_only = False
    if "--vals-only" in args:
        vals_only = True
        args.remove("--vals-only")

    if len(args) != 1:
        usage()

    return Path(args[0]), out_path, vals_only

def iter_entries(content: str) -> List[Tuple[str, str]]:
    starts = list(ENTRY_START_RE.finditer(content))
    entries: List[Tuple[str, str]] = []
    for i, m in enumerate(starts):
        entry_id = m.group(1)
        start = m.end()
        end = starts[i + 1].start() if i + 1 < len(starts) else len(content)
        entries.append((entry_id, content[start:end]))
    return entries

def main() -> None:
    lua_file, out_path, vals_only = parse_args(sys.argv[1:])

    if not lua_file.is_file():
        sys.stderr.write(f"File not found: {lua_file}\n")
        sys.exit(1)

    content = read_text_with_fallback(lua_file)
    lines: List[str] = []

    for entry_id, segment in iter_entries(content):
        pairs = [
            (k, unescape_lua_double_quoted(v))
            for k, v in PAIR_RE.findall(segment)
        ]
        if not pairs:
            continue

        row: List[str] = [entry_id]
        if vals_only:
            row.extend(v for _, v in pairs)
        else:
            for k, v in pairs:
                row.append(k)
                row.append(v)

        lines.append(",".join(csv_escape(x) for x in row))

    output = "\n".join(lines)
    if out_path:
        out_path.write_text(output + ("\n" if output else ""), encoding="utf-8")
    else:
        sys.stdout.write(output + ("\n" if output else ""))

if __name__ == "__main__":
    main()

