from __future__ import annotations

import sys
from pathlib import Path


ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "plugins" / "1c"))

from parser.cas_payload import (  # noqa: E402
    classify_payload,
    decode_declared_utf8_bsl_prefix,
    extract_stream_blocks,
    replace_declared_utf8_bsl_prefix_same_width,
    stream_header,
    structural_stream_blocks_with_data,
)


def test_structural_stream_parser_ignores_header_signature_inside_member() -> None:
    fake_header = stream_header(1)
    first = b"prefix" + fake_header + b"tail"
    payload = b"lead" + stream_header(len(first)) + first + stream_header(3) + b"end"

    # The old discovery scanner sees the signature embedded in `first`.
    assert len(extract_stream_blocks(payload, include_text=True)) == 3

    blocks = structural_stream_blocks_with_data(payload)
    assert len(blocks) == 2
    assert blocks[0]["data"] == first
    assert blocks[1]["data"] == b"end"


def test_declared_utf8_bsl_prefix_excludes_opaque_member_tail() -> None:
    bsl = b"\xef\xbb\xbf// marker\r\n"
    opaque = b"\x00\x04\x10metadata"
    member = bsl + opaque
    header = f"\r\n{len(bsl):08x} {len(member):08x} 7fffffff \r\n".encode("ascii")
    payload = b"lead" + header + member

    decoded = decode_declared_utf8_bsl_prefix(payload, 0)
    assert decoded["status"] == "ok"
    assert decoded["text"] == "// marker\r\n"
    assert decoded["bsl_prefix_bytes"] == len(bsl)
    assert decoded["opaque_tail_bytes"] == len(opaque)


def test_classification_uses_declared_utf8_prefix_not_opaque_member_tail() -> None:
    bsl = b"\xef\xbb\xbf// source-only\r\n"
    opaque = b"\x00metadata-tail-that-is-not-bsl"
    member = bsl + opaque
    header = f"\r\n{len(bsl):08x} {len(member):08x} 7fffffff \r\n".encode("ascii")

    classified = classify_payload(b"lead" + header + member, include_text=True)

    stream = classified["stream_blocks"][0]
    assert stream["text"] == "// source-only\r\n"
    assert stream["declared_utf8_bsl_prefix"]["opaque_tail_bytes"] == len(opaque)


def test_classification_does_not_treat_declared_brace_prefix_as_bsl() -> None:
    descriptor = b"\xef\xbb\xbf{3,1,0,\"\",0}"
    member = descriptor + b"\x00opaque"
    header = f"\r\n{len(descriptor):08x} {len(member):08x} 7fffffff \r\n".encode("ascii")

    classified = classify_payload(b"lead" + header + member, include_text=True)

    assert "declared_utf8_bsl_prefix" not in classified["stream_blocks"][0]


def test_fixed_width_prefix_replacement_preserves_tail_and_all_other_members() -> None:
    first = b"first-member"
    old_text = "//Пример - 3\r\n//Пример - 4     "
    old_prefix = b"\xef\xbb\xbf" + old_text.encode("utf-8")
    opaque = b"\x00opaque-platform-tail\x10\x20"
    member = old_prefix + opaque
    payload = (
        b"lead"
        + stream_header(len(first))
        + first
        + f"\r\n{len(old_prefix):08x} {len(member):08x} 7fffffff \r\n".encode("ascii")
        + member
        + stream_header(3)
        + b"end"
    )

    replacement, evidence = replace_declared_utf8_bsl_prefix_same_width(
        payload,
        1,
        text="//Тест - 3\r\n//Пример - 4     ",
    )

    before = structural_stream_blocks_with_data(payload)
    after = structural_stream_blocks_with_data(replacement)
    assert evidence["padding_bytes"] == len("Пример".encode("utf-8")) - len("Тест".encode("utf-8"))
    assert before[0]["data"] == after[0]["data"]
    assert before[1]["data"][len(old_prefix) :] == after[1]["data"][len(old_prefix) :]
    assert before[2]["data"] == after[2]["data"]
    assert decode_declared_utf8_bsl_prefix(replacement, 1)["text"].startswith("//Тест - 3")


def test_fixed_width_prefix_replacement_rejects_growth() -> None:
    old_prefix = b"\xef\xbb\xbf// one"
    member = old_prefix + b"opaque"
    payload = b"lead" + f"\r\n{len(old_prefix):08x} {len(member):08x} 7fffffff \r\n".encode("ascii") + member

    try:
        replace_declared_utf8_bsl_prefix_same_width(payload, 0, text="// replacement that is longer")
    except ValueError as exc:
        assert str(exc) == "replacement_declared_bsl_prefix_exceeds_fixed_width"
    else:
        raise AssertionError("fixed-width codec accepted growth")
