package de.tsschulz.tt_tagebuch.shared.state

import de.tsschulz.tt_tagebuch.shared.api.ClubTeamsApi
import de.tsschulz.tt_tagebuch.shared.api.MatchesApi
import de.tsschulz.tt_tagebuch.shared.api.ScheduleLogic
import de.tsschulz.tt_tagebuch.shared.api.models.ClubTeamDto
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchInvitationCreateBody
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchInvitationDto
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchSaveBody
import de.tsschulz.tt_tagebuch.shared.api.models.LeagueTableRowDto
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchDto
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchScope
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleViewMode
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateMatchPlayersBody
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

data class ScheduleState(
    val viewMode: ScheduleViewMode = ScheduleViewMode.Team,
    val teams: List<ClubTeamDto> = emptyList(),
    val selectedTeamId: Int? = null,
    val ownMatches: List<ScheduleMatchDto> = emptyList(),
    val allMatches: List<ScheduleMatchDto> = emptyList(),
    val overallMatches: List<ScheduleMatchDto> = emptyList(),
    val friendlyMatches: List<ScheduleMatchDto> = emptyList(),
    val incomingFriendlyInvitations: List<FriendlyMatchInvitationDto> = emptyList(),
    val outgoingFriendlyInvitations: List<FriendlyMatchInvitationDto> = emptyList(),
    val leagueTable: List<LeagueTableRowDto> = emptyList(),
    val matchScope: ScheduleMatchScope = ScheduleMatchScope.Own,
    val otherTeamName: String = "",
    val isLoading: Boolean = false,
    val error: String? = null,
    val seasonId: Int? = null,
) {
    val selectedTeam: ClubTeamDto?
        get() = teams.find { it.id == selectedTeamId }

    val displayedMatches: List<ScheduleMatchDto>
        get() = when (viewMode) {
            ScheduleViewMode.Overall -> ScheduleLogic.sortMatches(overallMatches)
            ScheduleViewMode.Adult ->
                ScheduleLogic.sortMatches(ScheduleLogic.filterAdultLeagues(overallMatches))
            ScheduleViewMode.Friendly -> ScheduleLogic.sortMatches(friendlyMatches)
            ScheduleViewMode.Team -> {
                val t = selectedTeam ?: return emptyList()
                ScheduleLogic.applyTeamMatchScope(
                    ownMatches = ownMatches,
                    allMatches = allMatches,
                    scope = matchScope,
                    ownTeamName = t.name,
                    otherTeamName = otherTeamName,
                )
            }
        }

    val leagueTeamOptions: List<String>
        get() = ScheduleLogic.leagueTeamNames(ScheduleLogic.mergeUniqueMatches(allMatches, ownMatches))
}

private fun List<ScheduleMatchDto>.withoutFriendly(): List<ScheduleMatchDto> = filterNot { it.isFriendly }

class ScheduleManager(
    private val clubTeamsApi: ClubTeamsApi,
    private val matchesApi: MatchesApi,
) {
    private val _state = MutableStateFlow(ScheduleState())
    val state: StateFlow<ScheduleState> = _state.asStateFlow()

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

    suspend fun refresh(clubId: Int) {
        when (_state.value.viewMode) {
            ScheduleViewMode.Team -> {
                val team = _state.value.selectedTeam
                if (team != null && (team.league?.id ?: 0) > 0) {
                    loadMatchesForTeam(clubId, team)
                } else {
                    loadClubTeams(clubId)
                }
            }
            ScheduleViewMode.Overall -> loadOverallSchedule(clubId)
            ScheduleViewMode.Adult -> loadAdultSchedule(clubId)
            ScheduleViewMode.Friendly -> {
                loadFriendlyMatches(clubId)
                loadFriendlyInvitations(clubId)
            }
        }
    }

    suspend fun loadClubTeams(clubId: Int) {
        _state.update { it.copy(isLoading = true, error = null) }
        try {
            val raw = clubTeamsApi.listClubTeams(clubId, _state.value.seasonId)
            val sorted = ScheduleLogic.sortClubTeams(raw)
            val withLeague = ScheduleLogic.teamsWithLeague(sorted)
            val pick = withLeague.firstOrNull() ?: sorted.firstOrNull()
            _state.update {
                it.copy(
                    teams = sorted,
                    selectedTeamId = pick?.id,
                    isLoading = false,
                    error = null,
                )
            }
            if (pick != null && (pick.league?.id ?: 0) > 0) {
                loadMatchesForTeam(clubId, pick)
            } else {
                _state.update {
                    it.copy(
                        viewMode = ScheduleViewMode.Team,
                        ownMatches = emptyList(),
                        allMatches = emptyList(),
                        leagueTable = emptyList(),
                        overallMatches = emptyList(),
                    )
                }
            }
        } catch (t: Throwable) {
            _state.update {
                it.copy(
                    isLoading = false,
                    error = t.toUserMessage("Mannschaften konnten nicht geladen werden"),
                )
            }
        }
    }

    suspend fun selectTeam(clubId: Int, teamId: Int) {
        val team = _state.value.teams.find { it.id == teamId } ?: return
        _state.update {
            it.copy(
                selectedTeamId = teamId,
                matchScope = ScheduleMatchScope.Own,
                otherTeamName = "",
            )
        }
        if ((team.league?.id ?: 0) > 0) {
            loadMatchesForTeam(clubId, team)
        } else {
            _state.update {
                it.copy(
                    ownMatches = emptyList(),
                    allMatches = emptyList(),
                    leagueTable = emptyList(),
                    overallMatches = emptyList(),
                    viewMode = ScheduleViewMode.Team,
                )
            }
        }
    }

    private suspend fun loadMatchesForTeam(clubId: Int, team: ClubTeamDto) {
        val leagueId = team.league?.id ?: return
        _state.update { it.copy(isLoading = true, error = null, viewMode = ScheduleViewMode.Team) }
        try {
            val own = matchesApi.listMatchesForLeague(clubId, leagueId, "own").withoutFriendly()
            val all = matchesApi.listMatchesForLeague(clubId, leagueId, "all").withoutFriendly()
            val table = runCatching { matchesApi.leagueTable(clubId, leagueId) }.getOrElse { emptyList() }
            _state.update {
                it.copy(
                    selectedTeamId = team.id,
                    ownMatches = own,
                    allMatches = all,
                    overallMatches = emptyList(),
                    leagueTable = table,
                    isLoading = false,
                    error = null,
                )
            }
            ensureOtherTeamDefault()
        } catch (t: Throwable) {
            _state.update {
                it.copy(
                    isLoading = false,
                    ownMatches = emptyList(),
                    allMatches = emptyList(),
                    leagueTable = emptyList(),
                    error = t.toUserMessage("Spiele konnten nicht geladen werden"),
                )
            }
        }
    }

    suspend fun loadOverallSchedule(clubId: Int) {
        _state.update {
            it.copy(
                isLoading = true,
                error = null,
                viewMode = ScheduleViewMode.Overall,
                matchScope = ScheduleMatchScope.Own,
                otherTeamName = "",
                selectedTeamId = null,
                ownMatches = emptyList(),
                allMatches = emptyList(),
                leagueTable = emptyList(),
            )
        }
        try {
            val matches = matchesApi.listMatchesForLeagues(clubId, _state.value.seasonId).withoutFriendly()
            _state.update { it.copy(overallMatches = matches, isLoading = false, error = null) }
        } catch (t: Throwable) {
            _state.update {
                it.copy(
                    isLoading = false,
                    overallMatches = emptyList(),
                    error = t.toUserMessage("Gesamtspielplan konnte nicht geladen werden"),
                )
            }
        }
    }

    suspend fun loadAdultSchedule(clubId: Int) {
        _state.update {
            it.copy(
                isLoading = true,
                error = null,
                viewMode = ScheduleViewMode.Adult,
                matchScope = ScheduleMatchScope.Own,
                otherTeamName = "",
                selectedTeamId = null,
                ownMatches = emptyList(),
                allMatches = emptyList(),
                leagueTable = emptyList(),
            )
        }
        try {
            val matches = matchesApi.listMatchesForLeagues(clubId, _state.value.seasonId).withoutFriendly()
            _state.update { it.copy(overallMatches = matches, isLoading = false, error = null) }
        } catch (t: Throwable) {
            _state.update {
                it.copy(
                    isLoading = false,
                    overallMatches = emptyList(),
                    error = t.toUserMessage("Erwachsenen-Spielplan konnte nicht geladen werden"),
                )
            }
        }
    }

    suspend fun loadFriendlyMatches(clubId: Int) {
        _state.update {
            it.copy(
                isLoading = true,
                error = null,
                viewMode = ScheduleViewMode.Friendly,
                matchScope = ScheduleMatchScope.Own,
                otherTeamName = "",
                selectedTeamId = null,
                ownMatches = emptyList(),
                allMatches = emptyList(),
                overallMatches = emptyList(),
                leagueTable = emptyList(),
            )
        }
        try {
            val localMatches = matchesApi.listFriendlyMatches(clubId)
            val sharedMatches = matchesApi.listSharedFriendlyMatches(clubId)
            val combined = ScheduleLogic.sortMatches(localMatches + sharedMatches)
            _state.update { it.copy(friendlyMatches = combined, isLoading = false, error = null) }
            loadFriendlyInvitations(clubId)
        } catch (t: Throwable) {
            _state.update {
                it.copy(
                    isLoading = false,
                    friendlyMatches = emptyList(),
                    error = t.toUserMessage("Freundschaftsspiele konnten nicht geladen werden"),
                )
            }
        }
    }

    fun setMatchScope(scope: ScheduleMatchScope) {
        _state.update { it.copy(matchScope = scope) }
        if (scope == ScheduleMatchScope.Other) {
            ensureOtherTeamDefault()
        } else {
            _state.update { it.copy(otherTeamName = "") }
        }
    }

    fun setOtherTeamName(name: String) {
        _state.update { it.copy(otherTeamName = name) }
    }

    private fun ensureOtherTeamDefault() {
        val options = _state.value.leagueTeamOptions
        if (_state.value.otherTeamName.isBlank() && options.isNotEmpty()) {
            _state.update { it.copy(otherTeamName = options.first()) }
        }
    }

    suspend fun updateMatchPlayers(clubId: Int, matchId: Int, ready: List<Int>, planned: List<Int>, played: List<Int>) {
        matchesApi.updateMatchPlayers(
            matchId,
            UpdateMatchPlayersBody(clubId = clubId, playersReady = ready, playersPlanned = planned, playersPlayed = played),
        )
        refresh(clubId)
    }

    suspend fun updateMatchPlayersForMatch(
        clubId: Int,
        match: ScheduleMatchDto,
        ready: List<Int>,
        planned: List<Int>,
        played: List<Int>,
    ) {
        val body = UpdateMatchPlayersBody(
            clubId = clubId,
            playersReady = ready,
            playersPlanned = planned,
            playersPlayed = played,
        )
        when {
            match.isFriendly && match.isSharedFriendly -> matchesApi.updateSharedFriendlyMatchPlayers(clubId, match.id, body)
            match.isFriendly -> matchesApi.updateFriendlyMatchPlayers(clubId, match.id, body)
            else -> matchesApi.updateMatchPlayers(match.id, body)
        }
        refresh(clubId)
    }

    suspend fun createFriendlyMatch(clubId: Int, body: FriendlyMatchSaveBody) {
        val saved = matchesApi.createFriendlyMatch(clubId, body)
        _state.update { it.copy(friendlyMatches = ScheduleLogic.sortMatches(it.friendlyMatches + saved)) }
    }

    suspend fun loadFriendlyInvitations(clubId: Int) {
        try {
            val incoming = matchesApi.listIncomingFriendlyInvitations(clubId)
            val outgoing = matchesApi.listOutgoingFriendlyInvitations(clubId)
            _state.update {
                it.copy(
                    incomingFriendlyInvitations = incoming,
                    outgoingFriendlyInvitations = outgoing,
                )
            }
        } catch (_: Throwable) {
            // Invitations sind ein Zusatzbereich; Fehler sollen den Hauptscreen nicht blockieren.
        }
    }

    suspend fun createFriendlyInvitation(clubId: Int, body: FriendlyMatchInvitationCreateBody) {
        matchesApi.createFriendlyInvitation(clubId, body)
        loadFriendlyInvitations(clubId)
    }

    suspend fun acceptFriendlyInvitation(clubId: Int, invitationId: Int) {
        val sharedMatch = matchesApi.acceptFriendlyInvitation(clubId, invitationId)
        _state.update { it.copy(friendlyMatches = ScheduleLogic.sortMatches(it.friendlyMatches + sharedMatch)) }
        loadFriendlyInvitations(clubId)
    }

    suspend fun declineFriendlyInvitation(clubId: Int, invitationId: Int) {
        matchesApi.declineFriendlyInvitation(clubId, invitationId)
        loadFriendlyInvitations(clubId)
    }

    suspend fun updateFriendlyMatch(clubId: Int, matchId: Int, body: FriendlyMatchSaveBody) {
        val existing = _state.value.friendlyMatches.find { it.id == matchId }
        val saved = if (existing?.isSharedFriendly == true) {
            matchesApi.updateSharedFriendlyMatch(clubId, matchId, body)
        } else {
            matchesApi.updateFriendlyMatch(clubId, matchId, body)
        }
        _state.update {
            it.copy(
                friendlyMatches = ScheduleLogic.sortMatches(
                    it.friendlyMatches.filterNot { match -> match.id == matchId } + saved,
                ),
            )
        }
    }

    suspend fun deleteFriendlyMatch(clubId: Int, matchId: Int) {
        val existing = _state.value.friendlyMatches.find { it.id == matchId }
        if (existing?.isSharedFriendly == true) {
            matchesApi.deleteSharedFriendlyMatch(clubId, matchId)
        } else {
            matchesApi.deleteFriendlyMatch(clubId, matchId)
        }
        _state.update { it.copy(friendlyMatches = it.friendlyMatches.filterNot { match -> match.id == matchId }) }
    }
}
