package fr.titionfire.ffsaf.domain.service;

import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionGuestRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.rest.data.MatchData;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import java.util.List;

@WithSession
@ApplicationScoped
public class MatchService {

    @Inject
    MatchRepository repository;

    @Inject
    CategoryRepository categoryRepository;

    @Inject
    CombRepository combRepository;

    @Inject
    CompetPermService permService;

    @Inject
    CompetitionGuestRepository competitionGuestRepository;

    public Uni<MatchData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
        return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
                .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
                .call(data -> permService.hasAdminViewPerm(securityCtx, data.getCategory().getCompet()))
                .map(MatchData::fromModel);
    }

    public Uni<List<MatchData>> getAllByPouleAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
        return categoryRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
                .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
                .call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet()))
                .chain(data -> repository.list("poule = ?1", data.getId())
                        .map(o -> o.stream().map(MatchData::fromModel).toList()));
    }

    public Uni<MatchData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, MatchData data) {
        return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
                .chain(o -> {
                            if (o == null) {
                                return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
                                        .firstResult()
                                        .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
                                        .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
                                        .map(categoryModel -> {
                                            MatchModel model = new MatchModel();

                                            model.setId(null);
                                            model.setSystem(system);
                                            model.setSystemId(data.getId());
                                            model.setCategory(categoryModel);
                                            return model;
                                        });
                            } else {
                                return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
                                        .firstResult()
                                        .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
                                        .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
                                        .map(__ -> o);
                            }
                        }
                )
                .chain(o -> {
                    o.setCategory_ord(data.getCategory_ord());
                    o.getScores().clear();
                    o.getScores().addAll(data.getScores());

                    return Uni.createFrom().nullItem()
                            .chain(() -> (data.getC1_id() == null) ?
                                    Uni.createFrom().nullItem() : combRepository.findById(data.getC1_id()))
                            .invoke(o::setC1_id)
                            .chain(() -> (data.getC1_id() == null) ?
                                    Uni.createFrom().nullItem() : combRepository.findById(data.getC2_id()))
                            .invoke(o::setC2_id)
                            .chain(() -> (data.getC1_str() == null) ?
                                    Uni.createFrom()
                                            .item((CompetitionGuestModel) null) : competitionGuestRepository.find(
                                    "fname = ?1 AND lname = ?2",
                                    data.getC1_str().substring(0, data.getC1_str().indexOf(" ")),
                                    data.getC1_str().substring(data.getC1_str().indexOf(" ") + 1)).firstResult())
                            .invoke(o::setC1_guest)
                            .chain(() -> (data.getC2_str() == null) ?
                                    Uni.createFrom()
                                            .item((CompetitionGuestModel) null) : competitionGuestRepository.find(
                                    "fname = ?1 AND lname = ?2",
                                    data.getC2_str().substring(0, data.getC2_str().indexOf(" ")),
                                    data.getC2_str().substring(data.getC2_str().indexOf(" ") + 1)).firstResult())
                            .invoke(o::setC2_guest)
                            .chain(() -> Panache.withTransaction(() -> repository.persist(o)));
                })
                .map(MatchData::fromModel);
    }

    public Uni<?> updateScore(SecurityCtx securityCtx, CompetitionSystem system, Long id,
                              List<ScoreEmbeddable> scores) {
        return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
                .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
                .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet()))
                .invoke(data -> {
                    data.getScores().clear();
                    data.getScores().addAll(scores);
                })
                .chain(data -> Panache.withTransaction(() -> repository.persist(data)))
                .map(o -> "OK");
    }

    public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
        return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
                .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
                .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet()))
                .chain(data -> Panache.withTransaction(() -> repository.delete(data)));
    }
}
