from pathlib import Path

import pytest

from abcforge.commands.abc2abci import _interleave

FIXTURES = Path(__file__).parent / "fixtures"


# ---------------------------------------------------------------------------
# Smoke tests: all fixture files must interleave without crashing
# ---------------------------------------------------------------------------

@pytest.mark.parametrize("abc_file", sorted(FIXTURES.glob("*.abc")))
def test_interleave_does_not_crash(abc_file):
    lines = abc_file.read_text(encoding="utf-8").splitlines(keepends=True)
    rotated, err = _interleave(lines)
    assert isinstance(err, (str, type(None)))
    if rotated is not None:
        assert len(rotated) > 0
        assert "[V:" in "".join(rotated), "output has no interleaved voice markers"
    else:
        pytest.skip(f"{abc_file.name} did not interleave: {err}")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def interleave(abc_text):
    """Convenience wrapper: text → rotated lines, raises on failure."""
    lines = abc_text.splitlines(keepends=True)
    rotated, err = _interleave(lines)
    assert rotated is not None, f"_interleave failed: {err}"
    return rotated


def body_lines(rotated):
    """Return only lines that carry interleaved voice content (contain [V:)."""
    return [l for l in rotated if "[V:" in l]


# ---------------------------------------------------------------------------
# [V:N] duplication fix
# Unrotated ABC from Guitar Pro / xml2abc prefixes every body line with
# [V:N]. rotate_abc then re-adds [V:N], producing "[V:1][V:1]…" in output.
# ---------------------------------------------------------------------------

INLINE_V_MARKERS = """\
X:1
T:Inline markers test
M:4/4
L:1/8
K:C
V:1
[V:1] C2 D2 E2 F2 | G4 z4 |]
V:2
[V:2] E2 F2 G2 A2 | B4 z4 |]
"""

def test_no_duplicate_voice_markers():
    rotated = interleave(INLINE_V_MARKERS)
    body = "".join(body_lines(rotated))
    assert "[V:1][V:1]" not in body, "duplicate [V:1] markers in output"
    assert "[V:2][V:2]" not in body, "duplicate [V:2] markers in output"


def test_inline_markers_body_has_correct_voice_count():
    rotated = interleave(INLINE_V_MARKERS)
    for line in body_lines(rotated):
        assert line.count("[V:") == 2, f"expected 2 voices per line, got: {line!r}"


# ---------------------------------------------------------------------------
# Correct interleaved output format
# A clean 2-voice piece must produce one interleaved line per bar group.
# ---------------------------------------------------------------------------

SIMPLE_TWO_VOICE = """\
X:1
T:Simple two voice
M:4/4
L:1/8
K:C
V:1
C2 D2 E2 F2 | G2 A2 B2 c2 |]
V:2
G,2 A,2 B,2 C2 | D2 E2 F2 G2 |]
"""

def test_simple_two_voice_structure():
    rotated = interleave(SIMPLE_TWO_VOICE)
    lines = body_lines(rotated)
    # Both voices must appear on every interleaved line
    for line in lines:
        assert "[V:1]" in line
        assert "[V:2]" in line


def test_simple_two_voice_notes_preserved():
    rotated = interleave(SIMPLE_TWO_VOICE)
    body = "".join(rotated)
    # Spot-check that notes from both voices are present
    assert "C2 D2 E2 F2" in body or "C2D2E2F2" in body.replace(" ", "")
    assert "G,2 A,2 B,2 C2" in body or "G,2A,2B,2C2" in body.replace(" ", "")


# ---------------------------------------------------------------------------
# V:N with trailing attributes ("V:1 clef=treble nm=Piano")
# extract_metadata_and_tunebody must find the tunebody start even when the
# V:1 header line has extra attributes.
# ---------------------------------------------------------------------------

VOICE_WITH_ATTRS = """\
X:1
T:Voice attrs test
M:4/4
L:1/8
K:C
V:1 clef=treble nm="Piano"
C2 D2 E2 F2 | G4 z4 |]
V:2 clef=bass
E,2 F,2 G,2 A,2 | B,4 z4 |]
"""

def test_voice_attrs_interleave():
    rotated = interleave(VOICE_WITH_ATTRS)
    body = "".join(body_lines(rotated))
    assert "[V:1]" in body
    assert "[V:2]" in body


# ---------------------------------------------------------------------------
# Inline [V:N] markers with whitespace after them
# The strip regex is r'\[V:N\]\s*' — verify whitespace is consumed too.
# ---------------------------------------------------------------------------

INLINE_V_WITH_SPACE = """\
X:1
T:Inline markers with space
M:4/4
L:1/8
K:C
V:1
[V:1]  C2 D2 E2 F2 | G4 z4 |]
V:2
[V:2]  E2 F2 G2 A2 | B4 z4 |]
"""

def test_inline_markers_with_trailing_space():
    rotated = interleave(INLINE_V_WITH_SPACE)
    body = "".join(body_lines(rotated))
    assert "[V:1][V:1]" not in body
    assert "[V:2][V:2]" not in body
