package fr.titionfire.ffsaf.ws.recv;

import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CardRepository;
import fr.titionfire.ffsaf.data.repository.ClubCardRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSCard;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import java.util.Date;
import java.util.List;

@WithSession
@ApplicationScoped
@RegisterForReflection
public class RCard {

    @Inject
    MatchRepository matchRepository;

    @Inject
    ClubCardRepository clubCardRepository;

    @Inject
    CardRepository cardRepository;

    @Inject
    CardService cardService;

    @Inject
    CompetitionRepository competitionRepository;

    @Inject
    TradService trad;

    private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
        return matchRepository.findById(id)
                .invoke(Unchecked.consumer(o -> {
                    if (o == null)
                        throw new DNotFoundException(trad.t("matche.non.trouver", connection));
                    if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
                        throw new DForbiddenException(trad.t("permission.denied", connection));
                }));
    }

    @WSReceiver(code = "getCardForMatch", permission = PermLevel.VIEW)
    public Uni<List<CardModel>> getCardForMatch(WebSocketConnection connection, Long matchId) {
        if (matchId == null)
            return Uni.createFrom().nullItem();
        return getById(matchId, connection).chain(matchModel -> cardService.getForMatch(matchModel));
    }

    @WSReceiver(code = "getAllForTeamNoDetail", permission = PermLevel.VIEW)
    public Uni<List<SendTeamCards>> getAllForTeamNoDetail(WebSocketConnection connection, Object o) {
        return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
                .chain(c -> clubCardRepository.list("competition = ?1", c.getId()))
                .map(cards -> cards.stream()
                        .map(card -> new SendTeamCards(card.getTeamUuid(), card.getTeamName(), List.of(),
                                card.getType(), card.getReason(), card.getDate()))
                        .toList());
    }

    @WSReceiver(code = "sendCardAdd", permission = PermLevel.TABLE)
    public Uni<Void> sendCardAdd(WebSocketConnection connection, SendCardAdd card) {
        return getById(card.matchId(), connection)
                .chain(matchModel -> cardService.checkCanBeAdded(card, matchModel)
                        .chain(c -> {
                            CardModel model = new CardModel();
                            model.setComb(card.combId());
                            model.setMatch(card.matchId());
                            model.setCategory(matchModel.getCategory().getId());
                            model.setCompetition(matchModel.getCategory().getCompet());
                            model.setCompetitionId(matchModel.getCategory().getCompet().getId());
                            model.setType(card.type());
                            model.setReason(card.reason());

                            return Panache.withTransaction(() -> cardRepository.persist(model));
                        })
                )
                .invoke(cardModel -> SSCard.sendCards(connection, List.of(cardModel)))
                .replaceWithVoid();
    }

    @WSReceiver(code = "sendCardRm", permission = PermLevel.ADMIN)
    public Uni<Void> sendCardRm(WebSocketConnection connection, SendCardAdd card) {
        return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
                .chain(competition -> cardRepository.find(
                                "match IS NULL AND comb = ?1 AND type = ?2 AND competition = ?3 AND match " + (card.matchId() == null ? "IS NULL" : "= " + card.matchId()),
                                card.combId(), card.type(), competition)
                        .firstResult()
                        .invoke(Unchecked.consumer(o -> {
                            if (o == null)
                                throw new DNotFoundException(trad.t("carton.non.trouver", connection));
                            SSCard.sendRmCards(connection, List.of(o.getId()));
                        }))
                        .chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel)))
                )
                .replaceWithVoid();
    }

    @WSReceiver(code = "applyTeamCards", permission = PermLevel.TABLE)
    public Uni<Void> applyTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
        return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
                .chain(c -> cardService.addTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type,
                        teamCards.reason()))
                .invoke(cards -> SSCard.sendTeamCard(connection,
                        new SendTeamCards(teamCards.teamUuid(), teamCards.teamName(), cards, teamCards.type(),
                                teamCards.reason(), new Date())))
                .replaceWithVoid();
    }

    @WSReceiver(code = "sendTeamCardReturnState", permission = PermLevel.TABLE)
    public Uni<Void> sendTeamCardReturnState(WebSocketConnection connection, SendTeamCardReturnState state) {
        if (state.state <= 0 || state.state > 2)
            return Uni.createFrom().voidItem();

        return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
                .chain(c -> cardService.recvReturnState(c, state))
                .invoke(cards -> SSCard.sendCards(connection, cards))
                .replaceWithVoid();
    }

    @WSReceiver(code = "removeTeamCards", permission = PermLevel.TABLE)
    public Uni<Void> removeTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
        return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
                .chain(c -> cardService.rmTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type))
                .invoke(cards -> SSCard.sendRmCards(connection, cards))
                .invoke(__ -> SSCard.rmTeamCard(connection, teamCards))
                .replaceWithVoid();
    }

    @RegisterForReflection
    public record SendCardAdd(Long matchId, long combId, CardModel.CardType type, String reason) {
    }

    @RegisterForReflection
    public record SendTeamCards(String teamUuid, String teamName, List<CardModel> cards, CardModel.CardType type,
                                String reason, Date date) {
    }

    @RegisterForReflection
    public record SendTeamCardReturnState(String teamUuid, String teamName, CardModel.CardType type,
                                          int state, Long selectedCategory, Long selectedMatch) {
    }
}
