import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import ru.alfabank.mobile.contract.header.Headers
import ru.alfabank.mobile.mvno.roaming.api.dto.AdditionalServiceDto
import ru.alfabank.mobile.mvno.roaming.api.dto.CountryDto
import ru.alfabank.mobile.mvno.roaming.api.dto.IdentTypeDto
import ru.alfabank.mobile.mvno.roaming.api.dto.ProgressBarSegmentsWeightDto
import ru.alfabank.mobile.mvno.roaming.multistep.configuration.properties.EconomyGuideProperties
import ru.alfabank.mobile.mvno.roaming.multistep.configuration.properties.SduiProperties
import ru.alfabank.mobile.mvno.roaming.multistep.service.RoamingService
import ru.alfabank.mobile.mvno.roaming.multistep.service.context.CountryRoamingServicesContextService
import ru.alfabank.mobile.mvno.roaming.multistep.service.model.CountryRoamingServicesContext.RoamingAvailablePackages
import ru.alfabank.mobile.mvno.roaming.multistep.service.model.CountryRoamingServicesContext.RoamingAvailableServices
import ru.alfabank.mobile.mvno.roaming.multistep.utils.Constants.ROAMING_GROUP_SIZE_TEMPLATE
import ru.alfabank.mobile.mvno.roaming.multistep.utils.DeepLinkUtils
import ru.alfabank.mobile.mvno.roaming.multistep.utils.getCountriesEnding
import ru.alfabank.mobile.mvno.service.api.dto.AvailableServiceDto
import ru.alfabank.mobile.mvno.service.api.dto.GroupDto
import ru.alfabank.mobile.mvno.service.api.dto.SubGroupDto
import utils.TestData.businessProperties
import java.math.BigDecimal
import java.time.Instant
import java.time.InstantSource

class CountryRoamingServicesContextServiceTest {

    private val deepLinkUtils: DeepLinkUtils = mock()
    private val sduiProperties: SduiProperties = SduiProperties(
        "",
        mock {
            on { economyGuide }.doReturn(EconomyGuideProperties("", "host/", ""))
        },
        mock()
    )
    private val instantSource: InstantSource = InstantSource.fixed(
        Instant.parse("2025-01-01T00:00:00Z"),
    )
    private val roamingService = mock<RoamingService>()

    private val service = CountryRoamingServicesContextService(
        deepLinkUtils = deepLinkUtils,
        sduiProperties = sduiProperties,
        businessProperties = businessProperties,
        instantSource = instantSource,
        roamingService = roamingService,
    )

    @BeforeEach
    fun init() {
        whenever(roamingService.getRoamingMinimumGroupOrEmpty(any())).thenReturn(emptySet())
    }

    @Test
    fun `should throw exception when countries list is empty`() {
        val headers = mock<Headers>()

        val exception = assertThrows<IllegalArgumentException> {
            service.buildContext(
                headers = headers,
                stepNumber = 1,
                mcc = "250",
                contractId = "contractId",
                availableServices = emptyList(),
                additionalServices = emptyList(),
                commonServices = emptyList(),
                countries = emptyList(),
                identTypes = emptyList()
            )
        }

        assertEquals("Country not found by mcc code", exception.message)
    }

    @Test
    fun `should handle null subGroups in available services`() {
        val headers = mock<Headers>()

        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "Russia"
            on { icon } doReturn "iconPath/"
        }

        val group = mock<GroupDto> {
            on { id } doReturn "ROAMING"
            on { subGroups } doReturn null
        }

        whenever(sduiProperties.content.extension).thenReturn(".svg")
        whenever(sduiProperties.content.urlIconHost).thenReturn("host/")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("deeplink")

        val context = service.buildContext(
            headers = headers,
            stepNumber = 1,
            mcc = "250",
            contractId = "contractId",
            availableServices = listOf(group),
            additionalServices = emptyList(),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = emptyList()
        )

        assertEquals("Russia", context.countryName)
        assertEquals("host/iconPath/.svg", context.countryIcon)
        assertEquals("0 странах", context.roamingGroupSize)
        assertEquals(emptyList<RoamingAvailablePackages>(), context.roamingAvailablePackages)
        assertEquals(emptyList<RoamingAvailableServices>(), context.roamingAvailableServices)
    }

    @Test
    fun `should handle empty identTypes when building roamingGroupSize`() {
        val headers = mock<Headers>()
        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "Spain"
            on { icon } doReturn "spain/"
        }

        whenever(sduiProperties.content.extension).thenReturn(".png")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("link")

        val context = service.buildContext(
            headers = headers,
            stepNumber = 2,
            mcc = "250",
            contractId = "cid",
            availableServices = emptyList(),
            additionalServices = emptyList(),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = emptyList()
        )

        assertEquals("0 странах", context.roamingGroupSize)
    }

    @Test
    fun `should handle subGroup with empty services when building available services`() {
        val headers = mock<Headers>()
        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "Italy"
            on { icon } doReturn "it/"
        }

        val subGroup = mock<SubGroupDto> {
            on { id } doReturn "id1"
            on { icon } doReturn "icon"
            on { name } doReturn "Package A"
            on { services } doReturn emptyList()
            on { validityDays } doReturn null
            on { roamingGroup } doReturn "GB"
        }

        val group = mock<GroupDto> {
            on { id } doReturn "ROAMING"
            on { subGroups } doReturn listOf(subGroup)
        }

        whenever(sduiProperties.content.extension).thenReturn(".svg")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("link")
        whenever(deepLinkUtils.getEnableServiceMultistepDeeplink(any(), any(), any(), any(), any()))
            .thenReturn("deep_link")

        val context = service.buildContext(
            headers = headers,
            stepNumber = 3,
            mcc = "250",
            contractId = "cid",
            availableServices = listOf(group),
            additionalServices = emptyList(),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = emptyList()
        )

        assertEquals(emptyList<RoamingAvailablePackages>(), context.roamingAvailablePackages)
    }

    @Test
    fun `should return null segments when progressBarSegmentsWeight is null`() {
        val headers = mock<Headers>()
        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "TestCountry"
            on { icon } doReturn "flag/"
        }

        val subgroup = mock<SubGroupDto> {
            on { id } doReturn "match_id"
            on { icon } doReturn "sub_icon"
            on { catalogOrder } doReturn 0
        }

        val group = mock<GroupDto> {
            on { subGroups } doReturn listOf(subgroup)
        }

        val additionalService = mock<AdditionalServiceDto> {
            on { mcc } doReturn listOf("250")
            on { identType } doReturn "match_id"
            on { name } doReturn "Test Package"
            on { currentVolume } doReturn 100.toBigDecimal()
            on { roamingGroup } doReturn "GB"
            on { dateEnd } doReturn Instant.parse("2025-01-10T00:00:00Z")
            on { progressBarSegmentsWeight } doReturn null
        }

        whenever(sduiProperties.content.extension).thenReturn(".svg")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("link")

        val context = service.buildContext(
            headers = headers,
            stepNumber = 1,
            mcc = "250",
            contractId = "cid",
            availableServices = listOf(group),
            additionalServices = listOf(additionalService),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = emptyList()
        )

        val pkg = context.roamingPackages.firstOrNull()
        assertNotNull(pkg, "Expected a roaming package to be created")
        assertNull(pkg!!.leftSegment, "Left segment should be null")
        assertNull(pkg.rightSegment, "Right segment should be null")
    }

    @Test
    fun `should map left and right segment weights when not null`() {
        val headers = mock<Headers>()
        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "SegmentLand"
            on { icon } doReturn "seg/"
        }

        val subgroup = mock<SubGroupDto> {
            on { id } doReturn "seg_id"
            on { icon } doReturn "seg_icon"
            on { name } doReturn "name"
            on { catalogOrder } doReturn 0
            on { roamingGroup } doReturn "additionalSms"
            on { services } doReturn listOf(AvailableServiceDto(
				id = "1",
				offerIdent = "1",
				name = "1",
				price = BigDecimal.ZERO,
				statusCode = AvailableServiceDto.StatusCode.ON,
				isExtension = false,
				instanceId = 0,
				priceBase = BigDecimal.ZERO,
				description = "1",
			))
        }

        val weight = mock<ProgressBarSegmentsWeightDto> {
            on { left } doReturn 40.toBigDecimal()
            on { right } doReturn 60.toBigDecimal()
        }

        val additionalService = mock<AdditionalServiceDto> {
            on { mcc } doReturn listOf("250")
            on { identType } doReturn "seg_id"
            on { name } doReturn "Segmented Service"
            on { currentVolume } doReturn 200.toBigDecimal()
            on { roamingGroup } doReturn "NOT_A_ROAMING_GROUP"
            on { dateEnd } doReturn Instant.parse("2025-01-05T00:00:00Z")
            on { progressBarSegmentsWeight } doReturn weight
        }

        whenever(sduiProperties.content.extension).thenReturn(".svg")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("link")
        whenever(deepLinkUtils.getEnableServiceMultistepDeeplink(any(), any(), any(), anyOrNull(), any()))
            .thenReturn("link")

        val context = service.buildContext(
            headers = headers,
            stepNumber = 1,
            mcc = "250",
            contractId = "cid",
            availableServices = listOf(
                mock {
                    on { subGroups } doReturn listOf(subgroup)
                    on { id } doReturn "ROAMING"
                }
            ),
            additionalServices = listOf(additionalService),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = emptyList()
        )

        val pkg = context.roamingPackages.first()
        assertEquals(40.toBigDecimal(), pkg.leftSegment?.weight)
        assertEquals(60.toBigDecimal(), pkg.rightSegment?.weight)
    }

    @Test
    fun `should correctly calculate minimum roaming group size from identTypes`() {
        val headers = mock<Headers>()
        val country = mock<CountryDto> {
            on { mccCode } doReturn "250"
            on { name } doReturn "IdentLand"
            on { icon } doReturn "flag/"
        }

        val identType1 = mock<IdentTypeDto> {
            on { mccGroup } doReturn listOf("a", "b", "c")
        }

        val identType2 = mock<IdentTypeDto> {
            on { mccGroup } doReturn listOf("x", "y")
        }

        val identType3 = mock<IdentTypeDto> {
            on { mccGroup } doReturn listOf("1", "2", "3", "4")
        }

        whenever(sduiProperties.content.extension).thenReturn(".svg")
        whenever(deepLinkUtils.getCountryGroupDeepLink(any(), any(), any(), any())).thenReturn("link")
        whenever(roamingService.getRoamingMinimumGroupOrEmpty(any())).thenReturn(setOf("x", "y"))

        val context = service.buildContext(
            headers = headers,
            stepNumber = 1,
            mcc = "250",
            contractId = "cid",
            availableServices = emptyList(),
            additionalServices = emptyList(),
            commonServices = emptyList(),
            countries = listOf(country),
            identTypes = listOf(identType1, identType2, identType3) // sizes: 3, 2, 4 → min = 2
        )

        val expected = String.format(ROAMING_GROUP_SIZE_TEMPLATE, 2, getCountriesEnding(2))
        assertEquals(expected, context.roamingGroupSize)
    }
}

given this test, cover toValidityPeriod (write only new test function)