package de.tsschulz.tt_tagebuch.shared.state

import de.tsschulz.tt_tagebuch.shared.api.AccidentApi
import de.tsschulz.tt_tagebuch.shared.api.DiaryApi
import de.tsschulz.tt_tagebuch.shared.api.DiaryMemberActivitiesApi
import de.tsschulz.tt_tagebuch.shared.api.DiaryMemberApi
import de.tsschulz.tt_tagebuch.shared.api.GroupApi
import de.tsschulz.tt_tagebuch.shared.api.MemberGroupPhotosApi
import de.tsschulz.tt_tagebuch.shared.api.ParticipantsApi
import de.tsschulz.tt_tagebuch.shared.api.PredefinedActivitiesApi
import de.tsschulz.tt_tagebuch.shared.api.models.AccidentReportDto
import de.tsschulz.tt_tagebuch.shared.api.models.AddDiaryMemberNoteBody
import de.tsschulz.tt_tagebuch.shared.api.models.CreateAccidentBody
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberNoteDto
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberTagMutationBody
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryTag
import de.tsschulz.tt_tagebuch.shared.api.models.PredefinedActivityDto
import de.tsschulz.tt_tagebuch.shared.api.models.PredefinedActivityUpsertBody
import de.tsschulz.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.CreateTrainingGroupBody
import de.tsschulz.tt_tagebuch.shared.api.models.DeleteTrainingGroupBody
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryDateActivityItem
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryDate
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryFreeformActivity
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberActivityLink
import de.tsschulz.tt_tagebuch.shared.api.models.MemberGroupPhotoDto
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryPlanGroup
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryTrainingParticipant
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateNestedPlanGroupActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTrainingGroupBody
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class DiaryManager(
    private val diaryApi: DiaryApi,
    private val participantsApi: ParticipantsApi,
    private val groupApi: GroupApi,
    private val diaryMemberActivitiesApi: DiaryMemberActivitiesApi,
    private val diaryMemberApi: DiaryMemberApi,
    private val predefinedActivitiesApi: PredefinedActivitiesApi,
    private val accidentApi: AccidentApi,
    private val memberGroupPhotosApi: MemberGroupPhotosApi,
) {
    private val _state = MutableStateFlow(DiaryState())
    val state: StateFlow<DiaryState> = _state.asStateFlow()

    suspend fun listDates(clubId: Int): List<DiaryDate> = diaryApi.listDates(clubId)

    suspend fun fetchDateActivities(clubId: Int, diaryDateId: Int): List<DiaryDateActivityItem> {
        return diaryApi.listDateActivities(clubId, diaryDateId)
    }

    suspend fun listFreeformActivities(diaryDateId: Int): List<DiaryFreeformActivity> {
        return diaryApi.listFreeformActivities(diaryDateId)
    }

    suspend fun addFreeformActivity(diaryDateId: Int, description: String): DiaryFreeformActivity {
        return diaryApi.addFreeformActivity(diaryDateId, description)
    }

    suspend fun listMemberActivityLinks(clubId: Int, planOrGroupActivityId: Int): List<DiaryMemberActivityLink> {
        return diaryMemberActivitiesApi.list(clubId, planOrGroupActivityId)
    }

    suspend fun addParticipantsToMemberActivity(clubId: Int, planOrGroupActivityId: Int, participantRowIds: List<Int>) {
        diaryMemberActivitiesApi.add(clubId, planOrGroupActivityId, participantRowIds)
    }

    suspend fun removeParticipantFromMemberActivity(clubId: Int, planOrGroupActivityId: Int, participantRowId: Int) {
        diaryMemberActivitiesApi.remove(clubId, planOrGroupActivityId, participantRowId)
    }

    suspend fun listAllDiaryTags(): List<DiaryTag> {
        return diaryApi.listTags()
    }

    suspend fun listMemberNotes(clubId: Int, diaryDateId: Int, memberId: Int): List<DiaryMemberNoteDto> {
        return diaryMemberApi.listNotes(clubId, diaryDateId, memberId)
    }

    suspend fun addMemberNote(clubId: Int, diaryDateId: Int, memberId: Int, content: String): List<DiaryMemberNoteDto> {
        return diaryMemberApi.addNote(
            clubId,
            AddDiaryMemberNoteBody(memberId = memberId, diaryDateId = diaryDateId, content = content.trim()),
        )
    }

    suspend fun deleteMemberNote(clubId: Int, diaryDateId: Int, memberId: Int, noteId: Int): List<DiaryMemberNoteDto> {
        return diaryMemberApi.deleteNote(clubId, noteId, diaryDateId, memberId)
    }

    suspend fun listMemberTags(clubId: Int, diaryDateId: Int, memberId: Int): List<DiaryMemberTagLinkDto> {
        return diaryMemberApi.listTags(clubId, diaryDateId, memberId)
    }

    suspend fun addMemberTag(clubId: Int, diaryDateId: Int, memberId: Int, tagId: Int): List<DiaryMemberTagLinkDto> {
        return diaryMemberApi.addTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tagId))
    }

    suspend fun removeMemberTag(clubId: Int, diaryDateId: Int, memberId: Int, tagId: Int): List<DiaryMemberTagLinkDto> {
        return diaryMemberApi.removeTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tagId))
    }

    suspend fun createMemberTagAndLink(clubId: Int, diaryDateId: Int, memberId: Int, name: String): List<DiaryMemberTagLinkDto> {
        val tag = diaryApi.createTag(name.trim())
        return diaryMemberApi.addTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tag.id))
    }

    suspend fun searchPredefinedActivities(query: String, limit: Int = 20): List<PredefinedActivityDto> {
        return predefinedActivitiesApi.search(query, limit)
    }

    suspend fun listPredefinedActivities(scope: String? = null): List<PredefinedActivityDto> {
        return predefinedActivitiesApi.list(scope)
    }

    suspend fun getPredefinedActivity(id: Int): PredefinedActivityDto {
        return predefinedActivitiesApi.getById(id)
    }

    suspend fun createPredefinedActivity(body: PredefinedActivityUpsertBody): PredefinedActivityDto {
        return predefinedActivitiesApi.create(body)
    }

    suspend fun updatePredefinedActivity(id: Int, body: PredefinedActivityUpsertBody): PredefinedActivityDto {
        return predefinedActivitiesApi.update(id, body)
    }

    suspend fun listAccidents(clubId: Int, diaryDateId: Int): List<AccidentReportDto> {
        return accidentApi.list(clubId, diaryDateId)
    }

    suspend fun addAccident(clubId: Int, diaryDateId: Int, memberId: Int, description: String) {
        accidentApi.create(
            CreateAccidentBody(
                clubId = clubId,
                memberId = memberId,
                diaryDateId = diaryDateId,
                accident = description.trim(),
            ),
        )
    }

    suspend fun listMemberGroupPhotos(clubId: Int): List<MemberGroupPhotoDto> {
        return memberGroupPhotosApi.list(clubId)
    }

    suspend fun uploadMemberGroupPhoto(clubId: Int, imageBytes: ByteArray, title: String, description: String) {
        memberGroupPhotosApi.upload(clubId, imageBytes, title, description)
    }

    suspend fun deleteMemberGroupPhoto(clubId: Int, photoId: Int) {
        memberGroupPhotosApi.delete(clubId, photoId)
    }

    /** Liefert die `Participant`-Zeilen-ID für das Mitglied am Tag, legt bei Bedarf „anwesend“ an. */
    suspend fun ensureParticipantRowId(diaryDateId: Int, memberId: Int): Int {
        val list = participantsApi.listForDate(diaryDateId)
        list.find { it.memberId == memberId }?.id?.let { return it }
        return participantsApi.add(diaryDateId, memberId).id
    }

    suspend fun createPlanActivity(clubId: Int, body: CreateDiaryPlanActivityRequest) {
        diaryApi.createDateActivity(clubId, body)
    }

    suspend fun addPlanGroupActivity(body: AddDiaryPlanGroupActivityRequest) {
        diaryApi.addDateGroupActivity(body)
    }

    suspend fun updatePlanActivity(clubId: Int, activityId: Int, body: UpdateDiaryPlanActivityRequest) {
        diaryApi.updateDateActivity(clubId, activityId, body)
    }

    suspend fun updatePlanActivityOrder(clubId: Int, activityId: Int, orderId: Int) {
        diaryApi.updateDateActivityOrder(clubId, activityId, orderId)
    }

    suspend fun deletePlanActivity(clubId: Int, activityId: Int) {
        diaryApi.deleteDateActivity(clubId, activityId)
    }

    suspend fun updatePlanNestedGroupActivity(clubId: Int, groupActivityId: Int, body: UpdateNestedPlanGroupActivityRequest) {
        diaryApi.updateNestedGroupActivity(clubId, groupActivityId, body)
    }

    suspend fun deletePlanNestedGroupActivity(clubId: Int, groupActivityId: Int) {
        diaryApi.deleteNestedGroupActivity(clubId, groupActivityId)
    }

    suspend fun listTrainingGroups(clubId: Int, diaryDateId: Int): List<DiaryPlanGroup> {
        return groupApi.listForDiaryDate(clubId, diaryDateId)
    }

    suspend fun createTrainingGroup(clubId: Int, diaryDateId: Int, name: String, lead: String?) {
        groupApi.create(CreateTrainingGroupBody(clubid = clubId, dateid = diaryDateId, name = name, lead = lead))
    }

    suspend fun updateTrainingGroup(groupId: Int, clubId: Int, diaryDateId: Int, name: String, lead: String?) {
        groupApi.update(groupId, UpdateTrainingGroupBody(clubid = clubId, dateid = diaryDateId, name = name, lead = lead))
    }

    suspend fun deleteTrainingGroup(groupId: Int, clubId: Int, diaryDateId: Int) {
        groupApi.delete(groupId, DeleteTrainingGroupBody(clubid = clubId, dateid = diaryDateId))
    }

    suspend fun listTrainingParticipants(diaryDateId: Int): List<DiaryTrainingParticipant> {
        return participantsApi.listForDate(diaryDateId)
    }

    suspend fun addTrainingParticipant(diaryDateId: Int, memberId: Int) {
        participantsApi.add(diaryDateId, memberId)
    }

    suspend fun removeTrainingParticipant(diaryDateId: Int, memberId: Int) {
        participantsApi.remove(diaryDateId, memberId)
    }

    suspend fun setTrainingParticipantAttendanceStatus(diaryDateId: Int, memberId: Int, attendanceStatus: String) {
        participantsApi.updateAttendanceStatus(diaryDateId, memberId, attendanceStatus)
    }

    suspend fun setTrainingParticipantGroup(diaryDateId: Int, memberId: Int, groupId: Int?) {
        participantsApi.updateParticipantGroup(diaryDateId, memberId, groupId)
    }

    fun clearError() {
        _state.value = _state.value.copy(error = null)
    }

    suspend fun loadDates(clubId: Int) {
        val prev = _state.value
        val base = if (prev.lastLoadedClubId != null && prev.lastLoadedClubId != clubId) {
            prev.copy(dates = emptyList(), lastLoadedClubId = null, fromOfflineCache = false, error = null)
        } else {
            prev
        }
        _state.value = base.copy(isLoading = true, error = null)
        try {
            val dates = diaryApi.listDates(clubId)
            _state.value = base.copy(
                isLoading = false,
                dates = dates,
                lastLoadedClubId = clubId,
                fromOfflineCache = false,
                error = null,
            )
        } catch (t: Throwable) {
            if (base.lastLoadedClubId == clubId && base.dates.isNotEmpty()) {
                _state.value = base.copy(isLoading = false, fromOfflineCache = true, error = null)
            } else {
                _state.value = base.copy(
                    isLoading = false,
                    error = t.toUserMessage("Fehler beim Laden des Tagebuchs"),
                    fromOfflineCache = false,
                )
            }
        }
    }

    /** @return neue `diaryDateId` bei Erfolg, sonst `null` */
    suspend fun createDate(
        clubId: Int,
        date: String,
        trainingStart: String?,
        trainingEnd: String?,
        excludeFromBilling: Boolean = false,
    ): Int? {
        _state.value = _state.value.copy(isLoading = true, error = null)
        return try {
            val created = diaryApi.createDate(clubId, date, trainingStart, trainingEnd, excludeFromBilling)
            loadDates(clubId)
            created.id
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Eintrag konnte nicht erstellt werden"))
            null
        }
    }

    suspend fun updateTimes(
        clubId: Int,
        dateId: Int,
        trainingStart: String?,
        trainingEnd: String?,
        excludeFromBilling: Boolean? = null,
    ) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            diaryApi.updateTimes(clubId, dateId, trainingStart, trainingEnd, excludeFromBilling)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Zeiten konnten nicht gespeichert werden"))
        }
    }

    suspend fun deleteDate(clubId: Int, dateId: Int) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            diaryApi.deleteDate(clubId, dateId)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Eintrag konnte nicht gelöscht werden"))
        }
    }

    suspend fun addNote(clubId: Int, diaryDateId: Int, content: String) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            diaryApi.addNote(diaryDateId, content)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Notiz konnte nicht gespeichert werden"))
        }
    }

    suspend fun deleteNote(clubId: Int, noteId: Int) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            diaryApi.deleteNote(noteId)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Notiz konnte nicht gelöscht werden"))
        }
    }

    suspend fun createAndLinkTag(clubId: Int, diaryDateId: Int, name: String) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            val tag = diaryApi.createTag(name)
            diaryApi.linkTag(clubId, diaryDateId, tag.id)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Tag konnte nicht gespeichert werden"))
        }
    }

    suspend fun removeTag(clubId: Int, tagId: Int) {
        _state.value = _state.value.copy(isLoading = true, error = null)
        try {
            diaryApi.removeTag(clubId, tagId)
            loadDates(clubId)
        } catch (t: Throwable) {
            _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Tag konnte nicht entfernt werden"))
        }
    }

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