package fr.titionfire.ffsaf.domain.service;

import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.model.LogModel.ActionType;
import fr.titionfire.ffsaf.data.model.LogModel.ObjectType;
import fr.titionfire.ffsaf.data.model.LoggableModel;
import fr.titionfire.ffsaf.data.repository.LogRepository;
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.RequestScoped;
import jakarta.inject.Inject;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@WithSession
@RequestScoped
public class LoggerService {

    @Inject
    LogRepository repository;

    @Inject
    SecurityCtx securityCtx;

    private final List<LogModel> buffer = new ArrayList<>();

    public Uni<?> logA(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
        return Panache.withTransaction(() -> repository.persist(
                new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
                        message)));
    }

    public Uni<?> logA(ActionType action, String message, LoggableModel model) {
        return logA(action, model.getObjectType(), message, model.getObjectName(), model.getId());
    }

    public Uni<?> logAAdd(LoggableModel model) {
        return logA(ActionType.ADD, "", model);
    }

    public Uni<?> logAUpdate(String message, LoggableModel model) {
        return logA(ActionType.UPDATE, message, model);
    }

    public Uni<?> logAChange(String champ, Object o1, Object o2, LoggableModel model) {
        if (Objects.equals(o1, o2))
            return Uni.createFrom().nullItem();
        return logA(ActionType.UPDATE, champ + ": " + o1.toString() + " -> " + o2.toString(), model);
    }

    public Uni<?> logADelete(LoggableModel model) {
        return logA(ActionType.REMOVE, "", model);
    }

    public Uni<?> append() {
        if (buffer.isEmpty())
            return Uni.createFrom().voidItem();
        return Panache.withTransaction(() -> repository.persist(buffer))
                .invoke(__ -> buffer.clear());
    }

    public void clear() {
        buffer.clear();
    }

    public void log(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
        buffer.add(new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
                message));
    }

    public void logAnonymous(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
        buffer.add(new LogModel(null, null, new Date(), action, object, target_id, target_name, message));
    }

    public void log(ActionType action, String message, LoggableModel model) {
        log(action, model.getObjectType(), message, model.getObjectName(), model.getId());
    }

    public void logAnonymous(ActionType action, String message, LoggableModel model) {
        logAnonymous(action, model.getObjectType(), message, model.getObjectName(), model.getId());
    }

    public void logAdd(LoggableModel model) {
        log(ActionType.ADD, "", model);
    }

    public void logUpdate(String message, LoggableModel model) {
        log(ActionType.UPDATE, message, model);
    }

    public void logUpdateAnonymous(String message, LoggableModel model) {
        logAnonymous(ActionType.UPDATE, message, model);
    }

    public void logChange(String champ, Object o1, Object o2, LoggableModel model) {
        if (Objects.equals(o1, o2))
            return;
        log(ActionType.UPDATE,
                champ + ": " + (o1 == null ? "null" : o1.toString()) + " -> " + (o2 == null ? "null" : o2.toString()),
                model);
    }

    public void logDelete(LoggableModel model) {
        log(ActionType.REMOVE, "", model);
    }
}
