package io.sentry.internal.debugmeta

import io.sentry.ILogger
import io.sentry.SentryIntegrationPackageStorage
import io.sentry.SentryOptions
import io.sentry.protocol.SentryPackage
import io.sentry.util.DebugMetaPropertiesApplier
import java.net.URL
import java.nio.charset.Charset
import java.util.Collections
import kotlin.test.Test
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

class ResourcesDebugMetaLoaderTest {
  class Fixture {
    val logger = mock<ILogger>()
    val classLoader = mock<ClassLoader>()

    fun getSut(
      fileName: String = "sentry-debug-meta.properties",
      content: List<String>? = null,
    ): ResourcesDebugMetaLoader {
      if (content != null) {
        val mockUrlMap = content.map { content -> Pair(mock<URL>(), content) }
        whenever(classLoader.getResources(fileName))
          .thenReturn(Collections.enumeration(mockUrlMap.map { it.first }))

        mockUrlMap.forEach {
          whenever(it.first.openStream())
            .thenReturn(it.second.byteInputStream(Charset.defaultCharset()))
        }
      } else {
        whenever(classLoader.getResources(fileName)).thenReturn(Collections.emptyEnumeration())
      }
      return ResourcesDebugMetaLoader(logger, classLoader)
    }
  }

  private val fixture = Fixture()

  @Test
  fun `reads from assets into properties and can be applied to options`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=34077988-a0e5-4839-9618-7400e1616d1b
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68
            io.sentry.build-tool=maven
            """
              .trimIndent()
          )
      )

    val options = SentryOptions()

    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    assertEquals(options.bundleIds, setOf("88ba82db-cd26-4c09-8b31-21461d286b68"))
    assertEquals(options.proguardUuid, "34077988-a0e5-4839-9618-7400e1616d1b")
  }

  @Test
  fun `reads multiple bundle IDs from assets into properties and can be applied to options`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=34077988-a0e5-4839-9618-7400e1616d1b
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68,8d11a44a-facd-46c1-a49b-87d256227101
            io.sentry.build-tool=maven
            """
              .trimIndent()
          )
      )

    val options = SentryOptions()

    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    assertEquals(
      options.bundleIds,
      setOf("88ba82db-cd26-4c09-8b31-21461d286b68", "8d11a44a-facd-46c1-a49b-87d256227101"),
    )
    assertEquals(options.proguardUuid, "34077988-a0e5-4839-9618-7400e1616d1b")
  }

  @Test
  fun `reads multiple bundle IDs from multiple resource into properties and can be applied to options`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=7b1fae93-63fb-43ff-a70a-608dc5005970
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68,8d11a44a-facd-46c1-a49b-87d256227101
            io.sentry.build-tool=maven
            """
              .trimIndent(),
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=37c90685-32a1-40db-9019-a2f0b05674cb
            io.sentry.bundle-ids=13e16819-accf-48da-a82d-f6ec94af9948
            io.sentry.build-tool=maven
            """
              .trimIndent(),
          )
      )

    val options = SentryOptions()

    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    assertEquals(
      options.bundleIds,
      setOf(
        "88ba82db-cd26-4c09-8b31-21461d286b68",
        "8d11a44a-facd-46c1-a49b-87d256227101",
        "13e16819-accf-48da-a82d-f6ec94af9948",
      ),
    )
    assertEquals(options.proguardUuid, "7b1fae93-63fb-43ff-a70a-608dc5005970")
  }

  @Test
  fun `when file does not exist, returns null`() {
    val sut = fixture.getSut()

    assertNull(sut.loadDebugMeta())
  }

  @Test
  fun `reads build-tool and build-version and adds them to packages`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=34077988-a0e5-4839-9618-7400e1616d1b
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68
            io.sentry.build-tool=maven
            io.sentry.build-tool-version=1.0
            """
              .trimIndent()
          )
      )

    val options = SentryOptions()
    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    val expected = SentryPackage("maven", "1.0")
    assertContains(SentryIntegrationPackageStorage.getInstance().packages, expected)
  }

  @Test
  fun `reads build-tool and adds it to packages with unknown version if build-tool-version is absent`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated by sentry-maven-plugin
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=34077988-a0e5-4839-9618-7400e1616d1b
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68
            io.sentry.build-tool=maven
            """
              .trimIndent()
          )
      )

    val options = SentryOptions()
    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    val expected = SentryPackage("maven", "unknown")
    assertContains(SentryIntegrationPackageStorage.getInstance().packages, expected)
  }

  @Test
  fun `does not add build-tool to packages if absent`() {
    val sut =
      fixture.getSut(
        content =
          listOf(
            """
            #Generated manually
            #Wed May 17 15:33:34 CEST 2023
            io.sentry.ProguardUuids=34077988-a0e5-4839-9618-7400e1616d1b
            io.sentry.bundle-ids=88ba82db-cd26-4c09-8b31-21461d286b68
            """
              .trimIndent()
          )
      )

    val options = SentryOptions()
    assertNotNull(sut.loadDebugMeta()) { DebugMetaPropertiesApplier.apply(options, it) }

    assertFalse {
      SentryIntegrationPackageStorage.getInstance().packages.any {
        it.name.equals("io.sentry.build-tool")
      }
    }
  }
}
