package org.zaine.management.data

import android.util.Log
import org.zaine.management.data.api.EveningCheckinRequest
import org.zaine.management.data.api.MorningCheckinRequest
import org.zaine.management.data.api.ApiFactory
import org.zaine.management.data.api.LoginRequest
import org.zaine.management.data.api.WirdEntryRequest
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class BackendRepository @Inject constructor(
    private val apiFactory: ApiFactory,
    private val sessionStore: SessionStore
) {
    suspend fun login(baseUrl: String, username: String, password: String): NetworkResult<Unit> =
        runCatchingNetwork {
            val normalizedBaseUrl = baseUrl.trim()
            Log.d(TAG, "Login request starting: baseUrl=$normalizedBaseUrl username=${username.trim()}")
            sessionStore.baseUrl = normalizedBaseUrl
            val api = apiFactory.create(sessionStore.baseUrl)
            sessionStore.token = api.login(LoginRequest(username.trim(), password)).token
            Log.d(TAG, "Login request succeeded")
        }

    suspend fun today(): NetworkResult<TodayBundle> = runCatchingNetwork {
        val api = api()
        TodayBundle(
            events = retryNetwork { api.todayEvents() },
            status = retryNetwork { api.todayStatus() }
        )
    }

    suspend fun month(year: Int, month: Int) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.monthEvents(year, month) }
    }

    suspend fun calendarRange(from: String, to: String) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.rangeEvents(from, to) }
    }

    suspend fun event(id: Long) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.event(id) }
    }

    suspend fun submitMorning(request: MorningCheckinRequest) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.submitMorning(request) }
    }

    suspend fun submitEvening(request: EveningCheckinRequest) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.submitEvening(request) }
    }

    suspend fun checkinHistory(from: String, to: String) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.checkinHistory(from, to) }
    }

    suspend fun todayWirdEntries() = runCatchingNetwork {
        val api = api()
        retryNetwork { api.todayWirdEntries() }
    }

    suspend fun wirdEntries(from: String, to: String) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.wirdEntries(from, to) }
    }

    suspend fun createWirdEntry(request: WirdEntryRequest) = runCatchingNetwork {
        val api = api()
        retryNetwork { api.createWirdEntry(request) }
    }

    private fun api() = apiFactory.create(sessionStore.baseUrl)

    private suspend fun <T> runCatchingNetwork(block: suspend () -> T): NetworkResult<T> =
        try {
            NetworkResult.Success(block())
        } catch (ex: Exception) {
            Log.e(TAG, "Network request failed", ex)
            NetworkResult.Error(ex.message ?: "Network request failed")
        }

    private suspend fun <T> retryNetwork(block: suspend () -> T): T {
        return try {
            block()
        } catch (ex: IOException) {
            block()
        }
    }
}

data class TodayBundle(
    val events: List<org.zaine.management.data.api.CalendarEventDto>,
    val status: org.zaine.management.data.api.TodayStatusDto
)

private const val TAG = "ZManagement"
