"""Tests for icon modifier codes (SIDC positions 17-20).

These tests verify the m1/m2 modifier overlay support added to handle
the sector 1 and sector 2 modifier codes in 20-digit number SIDCs.
"""

import pytest

from milsymbol import Symbol

# ── JS reference SVGs for exact-match tests ──
# Generated by milsymbol 3.0.4 JS library at size=80.

JS_REFS = {
    # SS 27 dismounted individual — both modifiers (scale 0.45)
    "10032700001103010527": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="94.4" height="94.4" viewBox="41 41 118 118"><path d='
        '"m 100,45 55,25 0,60 -55,25 -55,-25 0,-60 z" stroke-width="4" '
        'stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path>'
        '<g transform="translate(55,55)" ><g transform="scale(0.45)" ><path '
        'd="m 100,60 0,80 M 85,75 100,60 115,75" stroke-width='
        '"6.666666666666667" stroke="black" fill="none" ></path></g></g>'
        '<path d="m 75,62 h 20  m 5,16 V 62.1  M 125,62 h -20" '
        'stroke-width="3" stroke="black" fill="none" ></path><text x="100" '
        'y="134" text-anchor="middle" font-size="25" font-family="Arial" '
        'font-weight="bold" dominant-baseline="middle" stroke-width="3" '
        'stroke="none" fill="black" >E-3</text></svg>'
    ),
    # SS 27 — m1 only (scale 0.7, shifted down)
    "10032700001103050500": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="94.4" height="94.4" viewBox="41 41 118 118"><path d='
        '"m 100,45 55,25 0,60 -55,25 -55,-25 0,-60 z" stroke-width="4" '
        'stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path>'
        '<g transform="translate(0,10)" ><g transform="translate(30,30)" >'
        '<g transform="scale(0.7)" ><path d="m 100,60 0,80 M 85,75 100,60 '
        '115,75 M 80,140 120,140" stroke-width="4.285714285714286" '
        'stroke="black" fill="none" ></path></g></g></g><path d="m 75,62 h '
        '20  m 5,16 V 62.1  M 125,62 h -20" stroke-width="3" '
        'stroke="black" fill="none" ></path></svg>'
    ),
    # SS 27 — m2 only (scale 0.7, shifted up)
    "10032700001103010027": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="94.4" height="94.4" viewBox="41 41 118 118"><path d='
        '"m 100,45 55,25 0,60 -55,25 -55,-25 0,-60 z" stroke-width="4" '
        'stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path>'
        '<g transform="translate(0,-10)" ><g transform="translate(30,30)" >'
        '<g transform="scale(0.7)" ><path d="m 100,60 0,80 M 85,75 100,60 '
        '115,75" stroke-width="4.285714285714286" stroke="black" '
        'fill="none" ></path></g></g></g><text x="100" y="134" '
        'text-anchor="middle" font-size="25" font-family="Arial" '
        'font-weight="bold" dominant-baseline="middle" stroke-width="3" '
        'stroke="none" fill="black" >E-3</text></svg>'
    ),
    # SS 15 land equipment — both modifiers (scale 0.45)
    "10031500001201000102": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="102.4" height="102.4" viewBox="36 36 128 128">'
        '<circle cx="100" cy="100" r="60" stroke-width="4" stroke="black" '
        'fill="rgb(128,224,255)" fill-opacity="1" ></circle>'
        '<g transform="translate(55,55)" ><g transform="scale(0.45)" >'
        '<path d="m 60,120 80,0 M 120,80 c 25,0.2 25,40 0,40 l -40,0 C '
        '55,120 55,80 80,80 Z" stroke-width="6.666666666666667" '
        'stroke="black" fill="none" ></path><circle cx="80" cy="130" r="7" '
        'stroke-width="6.666666666666667" stroke="black" fill="none" '
        '></circle><circle cx="120" cy="130" r="7" stroke-width='
        '"6.666666666666667" stroke="black" fill="none" ></circle><text '
        'x="100" y="103" text-anchor="middle" font-size="45" '
        'font-family="Arial" font-weight="bold" dominant-baseline="middle" '
        'stroke-width="6.666666666666667" stroke="none" fill="black" '
        '>A</text></g></g><text x="100" y="71" text-anchor="middle" '
        'font-size="28" font-family="Arial" font-weight="bold" '
        'dominant-baseline="middle" stroke-width="3" stroke="none" '
        'fill="black" >B</text><text x="100" y="134" text-anchor="middle" '
        'font-size="28" font-family="Arial" font-weight="bold" '
        'dominant-baseline="middle" stroke-width="3" stroke="none" '
        'fill="black" >L</text></svg>'
    ),
    # Hostile affiliation — same modifiers
    "10062700001103010527": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="121.6" height="121.6" viewBox="24 24 152 152">'
        '<path d="M 100,28 L172,100 100,172 28,100 100,28 Z" '
        'stroke-width="4" stroke="black" fill="rgb(255,128,128)" '
        'fill-opacity="1" ></path><g transform="translate(55,55)" >'
        '<g transform="scale(0.45)" ><path d="m 100,60 0,80 M 85,75 100,60 '
        '115,75" stroke-width="6.666666666666667" stroke="black" '
        'fill="none" ></path></g></g><path d="m 75,62 h 20  m 5,16 V 62.1  '
        'M 125,62 h -20" stroke-width="3" stroke="black" fill="none" '
        '></path><text x="100" y="134" text-anchor="middle" font-size="25" '
        'font-family="Arial" font-weight="bold" dominant-baseline="middle" '
        'stroke-width="3" stroke="none" fill="black" >E-3</text></svg>'
    ),
    # Base symbol (0000) — no modifiers
    "10032700001103010000": (
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile='
        '"tiny" width="94.4" height="94.4" viewBox="41 41 118 118"><path d='
        '"m 100,45 55,25 0,60 -55,25 -55,-25 0,-60 z" stroke-width="4" '
        'stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path>'
        '<g transform="translate(0,0)" ><g transform="scale(1)" ><path d='
        '"m 100,60 0,80 M 85,75 100,60 115,75" stroke-width="3" '
        'stroke="black" fill="none" ></path></g></g></svg>'
    ),
}


class TestIconModifierExactMatch:
    """SVG exact match against JS reference for modifier combinations."""

    @pytest.mark.parametrize("sidc", JS_REFS.keys())
    def test_svg_exact_match(self, sidc):
        sym = Symbol(sidc, size=80)
        assert sym.is_valid()
        assert sym.as_svg() == JS_REFS[sidc]


class TestIconModifierValidity:
    """Modifier codes should resolve as valid when base entity exists."""

    def test_both_modifiers_valid(self):
        s = Symbol("10032700001103010527", size=80)
        assert s.is_valid()

    def test_m1_only_valid(self):
        s = Symbol("10032700001103050500", size=80)
        assert s.is_valid()

    def test_m2_only_valid(self):
        s = Symbol("10032700001103010027", size=80)
        assert s.is_valid()

    def test_base_0000_still_valid(self):
        s = Symbol("10032700001103010000", size=80)
        assert s.is_valid()

    def test_nonexistent_base_entity_invalid(self):
        """Modifier codes can't save a bad entity."""
        s = Symbol("10032700009999990527", size=80)
        assert not s.is_valid()

    def test_unknown_modifier_code_still_valid(self):
        """Unknown m1/m2 codes don't crash — base entity still resolves."""
        s = Symbol("10032700001103019999", size=80)
        assert s.is_valid()


class TestIconModifierScaling:
    """Verify the scaling logic for SS 27 and SS 15."""

    def test_both_mods_scale_045(self):
        """Both m1+m2 present → icon scaled to 0.45."""
        s = Symbol("10032700001103010527", size=80)
        svg = s.as_svg()
        assert "scale(0.45)" in svg
        assert "translate(55,55)" in svg

    def test_m1_only_scale_07_shift_down(self):
        """m1 only → icon scaled to 0.7, shifted down (+10)."""
        s = Symbol("10032700001103050500", size=80)
        svg = s.as_svg()
        assert "scale(0.7)" in svg
        assert "translate(0,10)" in svg
        assert "translate(30,30)" in svg

    def test_m2_only_scale_07_shift_up(self):
        """m2 only → icon scaled to 0.7, shifted up (-10)."""
        s = Symbol("10032700001103010027", size=80)
        svg = s.as_svg()
        assert "scale(0.7)" in svg
        assert "translate(0,-10)" in svg
        assert "translate(30,30)" in svg

    def test_base_0000_scale_1(self):
        """No modifiers → scale(1), identity transform."""
        s = Symbol("10032700001103010000", size=80)
        svg = s.as_svg()
        assert "scale(1)" in svg

    def test_ss15_excluded_entity_no_scaling(self):
        """SS 15 entity 130100 is in the exclusion list — no scaling."""
        s = Symbol("10031500001301000102", size=80)
        svg = s.as_svg()
        assert "scale(0.45)" not in svg
        assert "scale(0.7)" not in svg

    def test_ss15_non_excluded_entity_scales(self):
        """SS 15 entity 120100 (tank) is NOT excluded — should scale."""
        s = Symbol("10031500001201000102", size=80)
        svg = s.as_svg()
        assert "scale(0.45)" in svg

    def test_ss10_no_scaling(self):
        """SS 10 (land unit) has no scaling logic — modifiers just appended."""
        s = Symbol("10031000001211000102", size=80)
        svg = s.as_svg()
        assert "scale(0.45)" not in svg
        assert "scale(0.7)" not in svg

    def test_stroke_width_compensated(self):
        """Scaled icon paths should have compensated stroke-width."""
        s = Symbol("10032700001103010527", size=80)
        svg = s.as_svg()
        # scale(0.45) → stroke-width should be 3/0.45 = 6.667
        assert 'stroke-width="6.666666666666667"' in svg


class TestIconModifierEdgeCases:
    """Edge cases for modifier handling."""

    def test_empty_array_modifier_valid(self):
        """m1=11 on SS 15 is defined as empty array [] — valid but draws nothing."""
        s = Symbol("10031500001201001100", size=80)
        assert s.is_valid()
        # Should still produce SVG (frame + scaled base icon, no m1 overlay)
        assert len(s.as_svg()) > 100

    def test_hq_echelon_and_modifiers_combined(self):
        """Positions 6-9 (status/HQ/echelon) AND 17-20 (icon mods) both non-zero."""
        # status=2, HQ=1, echelon=20 (division), m1=05, m2=27
        s = Symbol("10032721201103010527", size=80)
        assert s.is_valid()
        svg = s.as_svg()
        # Should have scaling from icon mods
        assert "scale(0.45)" in svg

    def test_nonzero_status_field(self):
        """Position 6 (status) non-zero should still resolve via fallback."""
        # status=1 (present), rest normal
        s = Symbol("10032710001103010527", size=80)
        assert s.is_valid()
        assert "scale(0.45)" in s.as_svg()

    def test_different_affiliations_same_modifiers(self):
        """Same modifiers across affiliations produce different frames."""
        friendly = Symbol("10032700001103010527", size=80)
        hostile = Symbol("10062700001103010527", size=80)
        assert friendly.is_valid()
        assert hostile.is_valid()
        # Different SVGs (different frame shapes and colors)
        assert friendly.as_svg() != hostile.as_svg()
        # But both should have scaling
        assert "scale(0.45)" in friendly.as_svg()
        assert "scale(0.45)" in hostile.as_svg()

    def test_modifier_overlay_content_present(self):
        """Verify the m1/m2 overlay draw instructions appear in the SVG."""
        s = Symbol("10032700001103010527", size=80)
        svg = s.as_svg()
        # m1=05 is sniper crosshairs path
        assert "m 75,62" in svg
        # m2=27 is rank text "E-3"
        assert "E-3" in svg

    def test_ss27_non_weapon_entity_no_scaling(self):
        """SS 27 entities outside the 110301-110403 range don't scale."""
        # Entity 110101 = infantry dismounted (not in weapons range)
        s = Symbol("10032700001101010500", size=80)
        assert s.is_valid()
        svg = s.as_svg()
        assert "scale(0.45)" not in svg
        assert "scale(0.7)" not in svg

    def test_letter_sidc_unaffected(self):
        """Letter SIDCs have no m1/m2 modifier mechanism."""
        s = Symbol("SFG-UCI----", size=80)
        assert s.is_valid()
        # Should work exactly as before — no modifier logic involved
        assert len(s.as_svg()) > 100
