package com.fabledsword.minstrel.player.output.upnp.sonos

import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

class SonosTopologyTest {

    @Test
    fun `single zone group with one member`() {
        val xml = """
            <ZoneGroupState>
              <ZoneGroups>
                <ZoneGroup Coordinator="RINCON_A" ID="RINCON_A:1">
                  <ZoneGroupMember UUID="RINCON_A" ZoneName="Kitchen"
                    Location="http://192.168.1.10:1400/xml/device_description.xml"/>
                </ZoneGroup>
              </ZoneGroups>
            </ZoneGroupState>
        """.trimIndent()
        val groups = SonosTopology.parse(xml)
        assertEquals(1, groups.size)
        val g = groups.first()
        assertEquals("RINCON_A", g.coordinatorUdn)
        assertEquals("Kitchen", g.name)
        assertEquals(1, g.members.size)
    }

    @Test
    fun `stereo pair collapses to one group with two members`() {
        val xml = """
            <ZoneGroupState>
              <ZoneGroups>
                <ZoneGroup Coordinator="RINCON_L" ID="RINCON_L:2">
                  <ZoneGroupMember UUID="RINCON_L" ZoneName="Living Room"
                    Location="http://192.168.1.11:1400/xml/device_description.xml"
                    ChannelMapSet="RINCON_L:LF,LF;RINCON_R:RF,RF"/>
                  <ZoneGroupMember UUID="RINCON_R" ZoneName="Living Room"
                    Location="http://192.168.1.12:1400/xml/device_description.xml"
                    ChannelMapSet="RINCON_L:LF,LF;RINCON_R:RF,RF"/>
                </ZoneGroup>
              </ZoneGroups>
            </ZoneGroupState>
        """.trimIndent()
        val groups = SonosTopology.parse(xml)
        assertEquals(1, groups.size)
        val g = groups.first()
        assertEquals("RINCON_L", g.coordinatorUdn)
        assertEquals("Living Room", g.name)
        assertEquals(2, g.members.size)
        assertNotNull(g.members[0].channelMapSet)
    }

    @Test
    fun `multi-speaker group lists coordinator name`() {
        val xml = """
            <ZoneGroupState>
              <ZoneGroups>
                <ZoneGroup Coordinator="RINCON_X" ID="RINCON_X:3">
                  <ZoneGroupMember UUID="RINCON_X" ZoneName="Office"/>
                  <ZoneGroupMember UUID="RINCON_Y" ZoneName="Bedroom"/>
                </ZoneGroup>
              </ZoneGroups>
            </ZoneGroupState>
        """.trimIndent()
        val groups = SonosTopology.parse(xml)
        assertEquals("Office", groups.first().name)
    }

    @Test
    fun `malformed xml returns empty list`() {
        assertEquals(emptyList(), SonosTopology.parse("<garbage"))
    }

    @Test
    fun `parses inline non-escaped ZoneGroupState document`() {
        // Some Sonos firmware sends the topology as nested elements with
        // no escaping. After SoapClient.readUntilEndTag rebuilds a flat
        // string, parse should still find the groups.
        val xml = """
            <ZoneGroupState>
              <ZoneGroups>
                <ZoneGroup Coordinator="RINCON_A" ID="RINCON_A:1">
                  <ZoneGroupMember UUID="RINCON_A" ZoneName="Kitchen"
                    Location="http://192.168.1.10:1400/xml/device_description.xml"/>
                </ZoneGroup>
              </ZoneGroups>
            </ZoneGroupState>
        """.trimIndent()
        val groups = SonosTopology.parse(xml)
        assertEquals(1, groups.size)
    }

    @Test
    fun `parses escaped ZoneGroupState wrapped in entities`() {
        val xml = """
            &lt;ZoneGroupState&gt;
              &lt;ZoneGroups&gt;
                &lt;ZoneGroup Coordinator="RINCON_A" ID="RINCON_A:1"&gt;
                  &lt;ZoneGroupMember UUID="RINCON_A" ZoneName="Kitchen"
                    Location="http://192.168.1.10:1400/xml/device_description.xml"/&gt;
                &lt;/ZoneGroup&gt;
              &lt;/ZoneGroups&gt;
            &lt;/ZoneGroupState&gt;
        """.trimIndent()
        val groups = SonosTopology.parse(xml)
        assertEquals(1, groups.size)
    }
}
