"""从 SSMS“生成脚本”导出的 .sql（含数据）读取旧库行，作为没有 SQL Server 时的迁移数据源。

只支持该导出中出现的值形式：N'...'（'' 转义）、CAST(N'...' AS DateTime)、NULL、整数、小数、0x 二进制。"""
import datetime as dt
import re
from pathlib import Path

_INSERT = re.compile(r"INSERT \[dbo\]\.\[(?P<table>\w+)\] \((?P<cols>[^)]*)\) VALUES \(")
_CAST = re.compile(r"CAST\(N'([^']*)' AS (?:DateTime|DateTime2|Date)\)")
_NUMBER = re.compile(r"-?\d+(?:\.\d+)?")
_BINARY = re.compile(r"0x([0-9A-Fa-f]*)")


def _read_string(text: str, i: int) -> tuple[str, int]:
    """text[i] 是开头的单引号；返回解码后的字符串与结束引号之后的位置。"""
    out, i = [], i + 1
    while True:
        j = text.index("'", i)
        out.append(text[i:j])
        if text.startswith("''", j):
            out.append("'")
            i = j + 2
        else:
            return "".join(out), j + 1


def _read_values(text: str, i: int) -> tuple[list, int]:
    values = []
    while True:
        while text[i] in " \r\n\t,":
            i += 1
        if text[i] == ")":
            return values, i + 1
        if text.startswith("N'", i) or text[i] == "'":
            value, i = _read_string(text, i + (1 if text[i] == "N" else 0))
        elif text.startswith("NULL", i):
            value, i = None, i + 4
        elif m := _BINARY.match(text, i):
            value, i = bytes.fromhex(m.group(1)), m.end()
        elif m := _CAST.match(text, i):
            value, i = dt.datetime.fromisoformat(m.group(1)), m.end()
        elif m := _NUMBER.match(text, i):
            value, i = (float(m.group()) if "." in m.group() else int(m.group())), m.end()
        else:
            raise ValueError(f"unsupported value at {text[i:i + 40]!r}")
        values.append(value)


def parse_inserts(text: str) -> dict[str, list[dict]]:
    tables: dict[str, list[dict]] = {}
    pos = 0
    while m := _INSERT.search(text, pos):
        cols = [c.strip().strip("[]") for c in m["cols"].split(",")]
        values, pos = _read_values(text, m.end())
        tables.setdefault(m["table"], []).append(dict(zip(cols, values, strict=True)))
    return tables


class SqlScriptSource:
    def __init__(self, path: str | Path):
        raw = Path(path).read_bytes()
        encoding = "utf-16" if raw[:2] in (b"\xff\xfe", b"\xfe\xff") else "utf-8-sig"
        self.tables = parse_inserts(raw.decode(encoding))

    def rows(self, table: str) -> list[dict]:
        return [dict(r) for r in self.tables.get(table, [])]
