# PregnancyInfo.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2023 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.pregnancymodels

    import java.time.LocalDate
    import java.time.temporal.ChronoUnit

    sealed class DueDateMethod(open val date: LocalDate) {
        data class ExpectedDueDate(val edd: LocalDate) : DueDateMethod(edd)
        data class LastMenstruationPeriod(val lmp: LocalDate) : DueDateMethod(lmp)

        val expectedDueDate: LocalDate
            get() = when (this) {
                is ExpectedDueDate -> edd
                is LastMenstruationPeriod -> lmp.plusDays(280)
            }

        val lastMenstruationPeriod: LocalDate
            get() = when (this) {
                is ExpectedDueDate -> edd.minusDays(280)
                is LastMenstruationPeriod -> lmp
            }
    }

    const val MAX_PREGNANCY_WEEK = 42
    data class PregnancyInfo(
        val dueDateMethod: DueDateMethod,
        val dateOfBirth: LocalDate?,
    ) {

        val expectedDueDate: LocalDate
            get() = dueDateMethod.expectedDueDate

        private val lastMenstruationPeriod: LocalDate
            get() = dueDateMethod.lastMenstruationPeriod

        val pregnancyWeek: Int
            get() = ChronoUnit.WEEKS.between(lastMenstruationPeriod, LocalDate.now()).toInt().coerceAtMost(MAX_PREGNANCY_WEEK) + 1

        val gestationAge: GestationAge
            get() {
                val gestationWeeks = ChronoUnit.WEEKS.between(lastMenstruationPeriod, LocalDate.now())
                val gestationDays = if (gestationWeeks > MAX_PREGNANCY_WEEK) {
                    6
                } else {
                    ChronoUnit.DAYS.between(lastMenstruationPeriod.plusWeeks(gestationWeeks), LocalDate.now())
                }

                return GestationAge(gestationWeek = gestationWeeks.toInt().coerceAtMost(MAX_PREGNANCY_WEEK), gestationDays = gestationDays.toInt())
            }

        val trimester: Int
            get() {
                return when (pregnancyWeek - 1) {
                    in 0..11 -> 1
                    in 12..27 -> 2
                    else -> 3
                }
            }

        val monthsIntoPregnancy: Double
            get() {
                val daysIntoPregnancy = ChronoUnit.DAYS.between(lastMenstruationPeriod, LocalDate.now())
                val avgDaysPerMonth = 365.0 / 12
                return daysIntoPregnancy / avgDaysPerMonth
            }
    }