package com.example.elva.data.repository

import com.example.elva.data.local.HealthDataStore
import com.example.elva.data.models.health.*
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class HealthRepository @Inject constructor(
    private val dataStore: HealthDataStore
) {

    // Period tracking
    fun getPeriods(): List<Period> = dataStore.getPeriods()

    fun addPeriod(period: Period) = dataStore.addPeriod(period)

    fun updatePeriod(period: Period) = dataStore.updatePeriod(period)

    fun deletePeriod(periodId: Long) = dataStore.deletePeriod(periodId)

    fun calculateCycleStats(): CycleStats {
        val periods = getPeriods().sortedBy { it.startDate }

        if (periods.size < 2) {
            return CycleStats(
                averageCycleLength = 28,
                averagePeriodLength = 5,
                nextPeriodStart = periods.lastOrNull()?.startDate?.plusDays(28),
                nextOvulation = periods.lastOrNull()?.startDate?.plusDays(14),
                fertileWindowStart = periods.lastOrNull()?.startDate?.plusDays(12),
                fertileWindowEnd = periods.lastOrNull()?.startDate?.plusDays(16)
            )
        }

        // Calculate average cycle length
        val cycleLengths = mutableListOf<Int>()
        for (i in 0 until periods.size - 1) {
            val days = ChronoUnit.DAYS.between(
                periods[i].startDate,
                periods[i + 1].startDate
            ).toInt()
            cycleLengths.add(days)
        }
        val avgCycleLength = if (cycleLengths.isNotEmpty()) {
            cycleLengths.average().toInt()
        } else 28

        // Calculate average period length
        val periodLengths = periods.map {
            ChronoUnit.DAYS.between(it.startDate, it.endDate).toInt() + 1
        }
        val avgPeriodLength = periodLengths.average().toInt()

        // Predict next period
        val lastPeriod = periods.last()
        val nextPeriodStart = lastPeriod.startDate.plusDays(avgCycleLength.toLong())

        // Calculate ovulation (typically 14 days before next period)
        val nextOvulation = nextPeriodStart.minusDays(14)

        // Fertile window (5 days before ovulation to 1 day after)
        val fertileWindowStart = nextOvulation.minusDays(5)
        val fertileWindowEnd = nextOvulation.plusDays(1)

        return CycleStats(
            averageCycleLength = avgCycleLength,
            averagePeriodLength = avgPeriodLength,
            nextPeriodStart = nextPeriodStart,
            nextOvulation = nextOvulation,
            fertileWindowStart = fertileWindowStart,
            fertileWindowEnd = fertileWindowEnd
        )
    }

    fun isPeriodDay(date: LocalDate): Boolean {
        return getPeriods().any { period ->
            !date.isBefore(period.startDate) && !date.isAfter(period.endDate)
        }
    }

    fun isFertileDay(date: LocalDate): Boolean {
        val stats = calculateCycleStats()
        return stats.fertileWindowStart?.let { start ->
            stats.fertileWindowEnd?.let { end ->
                !date.isBefore(start) && !date.isAfter(end)
            }
        } ?: false
    }

    // Water intake
    fun getWaterIntakes(): List<WaterIntake> = dataStore.getWaterIntakes()

    fun getWaterIntakeForDate(date: LocalDate): WaterIntake? {
        return getWaterIntakes().find { it.date == date }
    }

    fun addWater(date: LocalDate, amountMl: Int) = dataStore.addWater(date, amountMl)

    fun getWaterGoal(): Int = dataStore.getWaterGoal()

    fun setWaterGoal(goalMl: Int) = dataStore.setWaterGoal(goalMl)

    // Calories
    fun getCalorieEntries(): List<CalorieEntry> = dataStore.getCalorieEntries()

    fun getCalorieEntriesForDate(date: LocalDate): List<CalorieEntry> {
        return getCalorieEntries().filter { it.date == date }
    }

    fun addCalorieEntry(entry: CalorieEntry) = dataStore.addCalorieEntry(entry)

    fun deleteCalorieEntry(entryId: Long) = dataStore.deleteCalorieEntry(entryId)

    fun getCalorieGoal(): CalorieGoal = dataStore.getCalorieGoal()

    fun setCalorieGoal(goal: CalorieGoal) = dataStore.setCalorieGoal(goal)

    fun getTotalCaloriesForDate(date: LocalDate): Int {
        return getCalorieEntriesForDate(date).sumOf { it.calories }
    }

    // Weight
    fun getWeightEntries(): List<WeightEntry> = dataStore.getWeightEntries()

    fun addWeightEntry(entry: WeightEntry) = dataStore.addWeightEntry(entry)

    fun deleteWeightEntry(entryId: Long) = dataStore.deleteWeightEntry(entryId)

    fun getLatestWeight(): WeightEntry? {
        return getWeightEntries().maxByOrNull { it.date }
    }

    // Sleep
    fun getSleepEntries(): List<SleepEntry> = dataStore.getSleepEntries()

    fun getSleepEntryForDate(date: LocalDate): SleepEntry? {
        return getSleepEntries().find { it.date == date }
    }

    fun addSleepEntry(entry: SleepEntry) = dataStore.addSleepEntry(entry)

    fun deleteSleepEntry(entryId: Long) = dataStore.deleteSleepEntry(entryId)

    fun getSleepGoal(): Float = dataStore.getSleepGoal()

    fun setSleepGoal(hours: Float) = dataStore.setSleepGoal(hours)

    fun getAverageSleepHours(days: Int = 7): Float {
        val entries = getSleepEntries()
            .sortedByDescending { it.date }
            .take(days)

        return if (entries.isEmpty()) 0f
        else entries.map { it.durationHours }.average().toFloat()
    }

    // Additional helper methods for ViewModel
    fun getCycleInfo(): CycleInfo {
        val periods = getPeriods().sortedBy { it.startDate }
        val lastPeriod = periods.lastOrNull()
        val stats = calculateCycleStats()

        return CycleInfo(
            averageCycleLength = stats.averageCycleLength,
            averagePeriodLength = stats.averagePeriodLength,
            lastPeriodStart = lastPeriod?.startDate,
            nextPeriodPrediction = stats.nextPeriodStart,
            ovulationPrediction = stats.nextOvulation,
            currentCycleDay = lastPeriod?.let {
                java.time.temporal.ChronoUnit.DAYS.between(it.startDate, LocalDate.now()).toInt() + 1
            } ?: 1
        )
    }

    // Journal
    fun getJournalEntries(): List<HealthJournalEntry> = dataStore.getJournalEntries()

    fun getJournalEntriesForDate(date: LocalDate): List<HealthJournalEntry> {
        return getJournalEntries().filter { it.date == date }
    }

    fun addJournalEntry(entry: HealthJournalEntry) = dataStore.addJournalEntry(entry)

    fun deleteJournalEntry(entryId: Long) = dataStore.deleteJournalEntry(entryId)
}

