package de.tsschulz.tt_tagebuch.shared.state

import de.tsschulz.tt_tagebuch.shared.api.MemberActivitiesApi
import de.tsschulz.tt_tagebuch.shared.api.MembersApi
import de.tsschulz.tt_tagebuch.shared.api.TrainingGroupsApi
import de.tsschulz.tt_tagebuch.shared.api.TrainingStatsApi
import de.tsschulz.tt_tagebuch.shared.api.TrainingTimesApi
import de.tsschulz.tt_tagebuch.shared.api.models.CreateTrainingTimeBody
import de.tsschulz.tt_tagebuch.shared.api.models.Member
import de.tsschulz.tt_tagebuch.shared.api.models.MemberActivityStatDto
import de.tsschulz.tt_tagebuch.shared.api.models.MemberQuickMutationResponse
import de.tsschulz.tt_tagebuch.shared.api.models.MemberLastParticipationDto
import de.tsschulz.tt_tagebuch.shared.api.models.MemberSetBody
import de.tsschulz.tt_tagebuch.shared.api.models.MemberTransferRunBody
import de.tsschulz.tt_tagebuch.shared.api.models.TrainingGroupDto
import de.tsschulz.tt_tagebuch.shared.api.models.TrainingStatsMember
import de.tsschulz.tt_tagebuch.shared.api.models.TrainingTimeDto
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTrainingTimeBody
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.json.JsonObject

class MembersManager(
    private val membersApi: MembersApi,
    private val trainingGroupsApi: TrainingGroupsApi,
    private val memberActivitiesApi: MemberActivitiesApi,
    private val trainingTimesApi: TrainingTimesApi,
    private val trainingStatsApi: TrainingStatsApi,
) {
    private val _state = MutableStateFlow(MembersState())
    val state: StateFlow<MembersState> = _state.asStateFlow()

    suspend fun loadMembers(clubId: Int) {
        val prev = _state.value
        val base = if (prev.lastLoadedClubId != null && prev.lastLoadedClubId != clubId) {
            prev.copy(members = emptyList(), lastLoadedClubId = null, fromOfflineCache = false, error = null)
        } else {
            prev
        }
        _state.value = base.copy(isLoading = true, error = null)
        try {
            val members = membersApi.listMembers(clubId)
            val merged = runCatching { trainingStatsApi.getStats(clubId) }
                .fold(
                    onSuccess = { mergeTrainingStatsIntoMembers(members, it.members) },
                    onFailure = { members },
                )
                .sortedWith(compareBy<Member> { it.lastName.lowercase() }.thenBy { it.firstName.lowercase() })
            _state.value = base.copy(
                members = merged,
                isLoading = false,
                lastLoadedClubId = clubId,
                fromOfflineCache = false,
                error = null,
            )
        } catch (t: Throwable) {
            if (base.lastLoadedClubId == clubId && base.members.isNotEmpty()) {
                _state.value = base.copy(isLoading = false, fromOfflineCache = true, error = null)
            } else {
                _state.value = base.copy(
                    isLoading = false,
                    error = t.toUserMessage("Mitglieder konnten nicht geladen werden"),
                    fromOfflineCache = false,
                )
            }
        }
    }

    suspend fun updateRatingsFromMyTischtennis(clubId: Int) {
        membersApi.updateRatingsFromMyTischtennis(clubId)
    }

    suspend fun quickUpdateTestMembership(clubId: Int, memberId: Int): MemberQuickMutationResponse =
        membersApi.quickUpdateTestMembership(clubId, memberId)

    suspend fun quickUpdateMemberFormHandedOver(clubId: Int, memberId: Int): MemberQuickMutationResponse =
        membersApi.quickUpdateMemberFormHandedOver(clubId, memberId)

    suspend fun quickDeactivateMember(clubId: Int, memberId: Int): MemberQuickMutationResponse =
        membersApi.quickDeactivateMember(clubId, memberId)

    suspend fun requestClickTtRegistration(clubId: Int, memberId: Int): MemberQuickMutationResponse =
        membersApi.requestClickTtRegistration(clubId, memberId)

    suspend fun listMemberNotes(clubId: Int, memberId: Int): List<de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberNoteDto> =
        membersApi.listMemberNotes(clubId, memberId)

    suspend fun addMemberNote(clubId: Int, memberId: Int, content: String): List<de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberNoteDto> =
        membersApi.addMemberNote(clubId, memberId, content)

    suspend fun deleteMemberNote(clubId: Int, noteId: Int): List<de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberNoteDto> =
        membersApi.deleteMemberNote(clubId, noteId)

    suspend fun transferMembers(clubId: Int, body: MemberTransferRunBody): JsonObject =
        membersApi.transferMembers(clubId, body)

    suspend fun saveMember(clubId: Int, body: MemberSetBody) {
        membersApi.setMember(clubId, body)
    }

    suspend fun uploadMemberPortrait(clubId: Int, memberId: Int, imageBytes: ByteArray, makePrimary: Boolean = true) {
        membersApi.uploadMemberImage(clubId, memberId, imageBytes, makePrimary = makePrimary)
    }

    suspend fun listTrainingGroups(clubId: Int): List<TrainingGroupDto> = trainingGroupsApi.listGroups(clubId)

    suspend fun listMembersForClub(clubId: Int, activeOnly: Boolean = true): List<Member> =
        membersApi.listMembers(clubId, showAll = !activeOnly)
            .sortedWith(compareBy<Member> { it.lastName.lowercase() }.thenBy { it.firstName.lowercase() })

    suspend fun createClubTrainingGroup(clubId: Int, name: String): TrainingGroupDto =
        trainingGroupsApi.createGroup(clubId, name)

    suspend fun updateClubTrainingGroup(clubId: Int, groupId: Int, name: String, sortOrder: Int?): TrainingGroupDto =
        trainingGroupsApi.updateGroup(clubId, groupId, name, sortOrder)

    suspend fun deleteClubTrainingGroup(clubId: Int, groupId: Int) {
        trainingGroupsApi.deleteGroup(clubId, groupId)
    }

    suspend fun listMemberTrainingGroups(clubId: Int, memberId: Int): List<TrainingGroupDto> =
        trainingGroupsApi.listMemberGroups(clubId, memberId)

    suspend fun addMemberToTrainingGroup(clubId: Int, groupId: Int, memberId: Int) {
        trainingGroupsApi.addMemberToGroup(clubId, groupId, memberId)
    }

    suspend fun removeMemberFromTrainingGroup(clubId: Int, groupId: Int, memberId: Int) {
        trainingGroupsApi.removeMemberFromGroup(clubId, groupId, memberId)
    }

    suspend fun createClubTrainingTime(clubId: Int, body: CreateTrainingTimeBody): TrainingTimeDto =
        trainingTimesApi.createTime(clubId, body)

    suspend fun updateClubTrainingTime(clubId: Int, timeId: Int, body: UpdateTrainingTimeBody): TrainingTimeDto =
        trainingTimesApi.updateTime(clubId, timeId, body)

    suspend fun deleteClubTrainingTime(clubId: Int, timeId: Int) {
        trainingTimesApi.deleteTime(clubId, timeId)
    }

    suspend fun memberActivityStats(clubId: Int, memberId: Int, period: String = "year"): List<MemberActivityStatDto> =
        memberActivitiesApi.listActivityStats(clubId, memberId, period)

    suspend fun memberLastParticipations(clubId: Int, memberId: Int, limit: Int = 12): List<MemberLastParticipationDto> =
        memberActivitiesApi.listLastParticipations(clubId, memberId, limit)

    suspend fun trainingScheduleGroups(clubId: Int): List<TrainingGroupDto> =
        trainingTimesApi.listGroupsWithTimes(clubId)

    fun clear() {
        _state.value = MembersState()
    }
}

private fun mergeTrainingStatsIntoMembers(members: List<Member>, stats: List<TrainingStatsMember>): List<Member> {
    val byId = stats.associateBy { it.id }
    return members.map { m ->
        val s = byId[m.id]
        if (s == null) {
            m.copy(
                trainingParticipations = 0,
                missedTrainingWeeks = 0,
                notInTraining = false,
                lastTraining = null,
                trainingGroups = emptyList(),
            )
        } else {
            m.copy(
                trainingParticipations = s.participationTotal,
                lastTraining = s.lastTraining,
                notInTraining = s.notInTraining,
                missedTrainingWeeks = s.missedTrainingWeeks,
                trainingGroups = s.trainingGroups,
            )
        }
    }
}
