# PregnancyInfoDomainModel.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2025 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */
    package dk.nsp.mingraviditet.core.data.domain.pregnancyInfo

    import dk.nsp.mingraviditet.core.data.domain.pregnancyInfo.PregnancyState.Companion.SOFA_VIEW_THRESHOLD_WEEKS_AFTER_BIRTH
    import dk.nsp.mingraviditet.core.data.domain.pregnancyInfo.PregnancyState.Companion.SOFA_VIEW_THRESHOLD_WEEKS_AFTER_DUE_DATE
    import dk.nsp.mingraviditet.core.data.domain.pregnancyInfo.PregnancyState.Companion.TERMINATION_CUTOFF_WEEK
    import dk.nsp.mingraviditet.core.network.pregnancyInfo.model.FetusSizeDTO
    import dk.nsp.mingraviditet.core.network.pregnancyInfo.model.PregnancyInfoDTO
    import java.time.LocalDate
    import dk.nsp.mingraviditet.core.shared.R as sharedR

    data class PregnancyInfoDomainModel(
        val name: String? = null,
        val pregnancyState: PregnancyState,
    )

    sealed class PregnancyState(val fetusSize: FetusSize? = null) {
        fun showSofaView() = this is ShowSofaView
        fun canCreateNewPregnancy() = this is GaveBirth && this.canCreateNewPregnancy
        fun gaveBirth() = this is GaveBirth
        fun mustSetDueDate() = this is SetDueDate
        fun showFetusProgress() = this is WaitingForBirth || this is GaveBirth
        fun noPregnancyDataAvailable() = this is NoPregnancyDataAvailable
        fun getLastMenstruationPeriod(): LocalDate? {
            return when (this) {
                is WaitingForBirth -> dueDate.minusDays(PREGNANCY_DURATION_DAYS)
                is GaveBirth -> dateOfBirth.minusDays(PREGNANCY_DURATION_DAYS)
                else -> null
            }
        }

        fun weekOrNull(): Int? {
            return when (this) {
                is WaitingForBirth -> weekNumber
                is GaveBirth -> weekNumber
                NoPregnancyDataAvailable, SetDueDate -> null
                ShowSofaView -> null
            }
        }

        fun dueDateOrNull(): LocalDate? {
            return when (this) {
                is WaitingForBirth -> dueDate
                else -> null
            }
        }

        fun birthDateOrNull(): LocalDate? {
            return when (this) {
                is GaveBirth -> dateOfBirth
                else -> null
            }
        }

        data object SetDueDate : PregnancyState()

        data object ShowSofaView : PregnancyState()

        data class WaitingForBirth(
            val dueDate: LocalDate,
            val week: String,
            val weekNumber: Int,
            val trimester: String,
            val gestationAge: String,
            val fetusSizeModel: FetusSize,
            val canEditBirthDate: Boolean,
            val canTerminatePregnancy: Boolean,
        ) : PregnancyState(
            fetusSize = fetusSizeModel,
        )

        data class GaveBirth(
            val dateOfBirth: LocalDate,
            val weekNumber: Int,
            val canCreateNewPregnancy: Boolean,
        ) : PregnancyState(FetusSize.Size18(100))

        data object NoPregnancyDataAvailable : PregnancyState()

        companion object {
            const val PREGNANCY_DURATION_DAYS = 280L
            const val SOFA_VIEW_THRESHOLD_WEEKS_AFTER_DUE_DATE = 11L
            const val SOFA_VIEW_THRESHOLD_WEEKS_AFTER_BIRTH = 8L
            const val TERMINATION_CUTOFF_WEEK = 40
        }
    }

    fun PregnancyInfoDTO.toDomain(
        dateOfBirth: LocalDate?,
        userIsAnonymous: Boolean,
        dateOfPregnancyTermination: LocalDate?,
    ): PregnancyInfoDomainModel {
        val userName = this.user?.userName
        val pregnancyData = this.pregnancyInfo

        when {
            looseDocument != null -> {
                return PregnancyInfoDomainModel(
                    name = userName,
                    pregnancyState = PregnancyState.ShowSofaView,
                )
            }

            pregnancyData == null -> {
                return PregnancyInfoDomainModel(
                    name = userName,
                    pregnancyState = PregnancyState.NoPregnancyDataAvailable,
                )
            }

            dateOfPregnancyTermination != null && !userIsAnonymous -> {
                return PregnancyInfoDomainModel(
                    name = userName,
                    pregnancyState = PregnancyState.ShowSofaView,
                )
            }

            dateOfBirth != null -> {
                if (dateOfBirth <= LocalDate.now().minusWeeks(SOFA_VIEW_THRESHOLD_WEEKS_AFTER_BIRTH) && !userIsAnonymous) {
                    return PregnancyInfoDomainModel(
                        name = userName,
                        pregnancyState = PregnancyState.ShowSofaView,
                    )
                }
                return PregnancyInfoDomainModel(
                    name = userName,
                    pregnancyState = PregnancyState.GaveBirth(
                        dateOfBirth = dateOfBirth,
                        weekNumber = pregnancyData.weekNumber,
                        canCreateNewPregnancy = userIsAnonymous,
                    ),
                )
            }

            pregnancyData.dueDate <= LocalDate.now().minusWeeks(SOFA_VIEW_THRESHOLD_WEEKS_AFTER_DUE_DATE) -> {
                return PregnancyInfoDomainModel(
                    name = this.user?.userName,
                    pregnancyState = PregnancyState.ShowSofaView,
                )
            }

            else -> return PregnancyInfoDomainModel(
                name = this.user?.userName,
                pregnancyState = PregnancyState.WaitingForBirth(
                    dueDate = pregnancyData.dueDate,
                    week = pregnancyData.week,
                    trimester = pregnancyData.trimester,
                    gestationAge = pregnancyData.gestationalAge,
                    fetusSizeModel = pregnancyData.fetusSize.toDomain(pregnancyData.progressInPercent),
                    weekNumber = pregnancyData.weekNumber,
                    canEditBirthDate = userIsAnonymous,
                    canTerminatePregnancy = !userIsAnonymous && pregnancyData.weekNumber < TERMINATION_CUTOFF_WEEK,
                ),
            )
        }
    }

    fun FetusSizeDTO.toDomain(progressInPercent: Int): FetusSize =
        when (this) {
            FetusSizeDTO.SIZE_0_0_TO_1_0 -> FetusSize.Size1(progressInPercent)
            FetusSizeDTO.SIZE_1_0_TO_1_5 -> FetusSize.Size2(progressInPercent)
            FetusSizeDTO.SIZE_1_5_TO_2_0 -> FetusSize.Size3(progressInPercent)
            FetusSizeDTO.SIZE_2_0_TO_2_5 -> FetusSize.Size4(progressInPercent)
            FetusSizeDTO.SIZE_2_5_TO_3_0 -> FetusSize.Size5(progressInPercent)
            FetusSizeDTO.SIZE_3_0_TO_3_5 -> FetusSize.Size6(progressInPercent)
            FetusSizeDTO.SIZE_3_5_TO_4_0 -> FetusSize.Size7(progressInPercent)
            FetusSizeDTO.SIZE_4_0_TO_4_5 -> FetusSize.Size8(progressInPercent)
            FetusSizeDTO.SIZE_4_5_TO_5_0 -> FetusSize.Size9(progressInPercent)
            FetusSizeDTO.SIZE_5_0_TO_5_5 -> FetusSize.Size10(progressInPercent)
            FetusSizeDTO.SIZE_5_5_TO_6_0 -> FetusSize.Size11(progressInPercent)
            FetusSizeDTO.SIZE_6_0_TO_6_5 -> FetusSize.Size12(progressInPercent)
            FetusSizeDTO.SIZE_6_5_TO_7_0 -> FetusSize.Size13(progressInPercent)
            FetusSizeDTO.SIZE_7_0_TO_7_5 -> FetusSize.Size14(progressInPercent)
            FetusSizeDTO.SIZE_7_5_TO_8_0 -> FetusSize.Size15(progressInPercent)
            FetusSizeDTO.SIZE_8_0_TO_8_5 -> FetusSize.Size16(progressInPercent)
            FetusSizeDTO.SIZE_8_5_TO_9_0 -> FetusSize.Size17(progressInPercent)
            FetusSizeDTO.SIZE_9_0 -> FetusSize.Size18(progressInPercent)
        }

    sealed interface FetusSize {

        val iconRes: Int
        val progressInPercent: Int

        data class Size1(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_0_5_month
            override val progressInPercent: Int = progress
        }

        data class Size2(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_1_0_month
            override val progressInPercent: Int = progress
        }

        data class Size3(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_1_5_months
            override val progressInPercent: Int = progress
        }

        data class Size4(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_2_0_months
            override val progressInPercent: Int = progress
        }

        data class Size5(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_2_5_months
            override val progressInPercent: Int = progress
        }

        data class Size6(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_3_0_months
            override val progressInPercent: Int = progress
        }

        data class Size7(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_3_5_months
            override val progressInPercent: Int = progress
        }

        data class Size8(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_4_0_months
            override val progressInPercent: Int = progress
        }

        data class Size9(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_4_5_months
            override val progressInPercent: Int = progress
        }

        data class Size10(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_5_0_months
            override val progressInPercent: Int = progress
        }

        data class Size11(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_5_5_months
            override val progressInPercent: Int = progress
        }

        data class Size12(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_6_0_months
            override val progressInPercent: Int = progress
        }

        data class Size13(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_6_5_months
            override val progressInPercent: Int = progress
        }

        data class Size14(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_7_0_months
            override val progressInPercent: Int = progress
        }

        data class Size15(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_7_5_months
            override val progressInPercent: Int = progress
        }

        data class Size16(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_8_0_months
            override val progressInPercent: Int = progress
        }

        data class Size17(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_8_5_months
            override val progressInPercent: Int = progress
        }

        data class Size18(val progress: Int) : FetusSize {
            override val iconRes: Int = sharedR.drawable.ic_9_0_months
            override val progressInPercent: Int = progress
        }
    }