package fr.titionfire.ffsaf.domain.service;

import fr.titionfire.ffsaf.data.model.CheckoutModel;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.repository.CheckoutRepository;
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
import fr.titionfire.ffsaf.rest.client.HelloAssoService;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutMetadata;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.scheduler.Scheduled;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.hibernate.reactive.mutiny.Mutiny;

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

@WithSession
@ApplicationScoped
public class CheckoutService {

    @Inject
    CheckoutRepository repository;

    @Inject
    LicenceRepository licenceRepository;

    @Inject
    MembreService membreService;

    @Inject
    LicenceService licenceService;

    @Inject
    LoggerService ls;

    @RestClient
    HelloAssoService helloAssoService;

    @ConfigProperty(name = "frontRootUrl")
    String frontRootUrl;

    @ConfigProperty(name = "unitLicencePrice")
    int unitLicencePrice;

    @ConfigProperty(name = "helloasso.organizationSlug")
    String organizationSlug;

    public Uni<Boolean> canDeleteLicence(long id) {
        return repository.countByLicenseId(id).invoke(c -> System.out.println(c)).map(count -> count == 0);
    }

    public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) {
        return membreService.getByAccountId(securityCtx.getSubject())
                .call(membreModel -> Mutiny.fetch(membreModel.getClub()))
                .chain(membreModel -> {
                    CheckoutModel model = new CheckoutModel();
                    model.setMembre(membreModel);
                    model.setLicenseIds(ids);
                    model.setPaymentStatus(CheckoutModel.PaymentStatus.UNKNOW);

                    return Panache.withTransaction(() -> repository.persist(model));
                })
                .chain(checkoutModel -> {
                    CheckoutIntentsRequest request = new CheckoutIntentsRequest();
                    request.setTotalAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
                    request.setInitialAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
                    request.setItemName("%d licences %d-%d pour %s".formatted(checkoutModel.getLicenseIds().size(),
                            Utils.getSaison(), Utils.getSaison() + 1, checkoutModel.getMembre().getClub().getName()));
                    request.setBackUrl(frontRootUrl + "/club/member/pay");
                    request.setErrorUrl(frontRootUrl + "/club/member/pay/error");
                    request.setReturnUrl(frontRootUrl + "/club/member/pay/return");
                    request.setContainsDonation(false);
                    request.setPayer(new CheckoutIntentsRequest.Payer(checkoutModel.getMembre().getFname(),
                            checkoutModel.getMembre().getLname(), checkoutModel.getMembre().getEmail()));
                    request.setMetadata(new CheckoutMetadata(checkoutModel.getId()));

                    return helloAssoService.checkout(organizationSlug, request)
                            .call(response -> {
                                checkoutModel.setCheckoutId(response.getId());
                                return Panache.withTransaction(() -> repository.persist(checkoutModel));
                            });
                })
                .onFailure().transform(t -> new DInternalError(t.getMessage()))
                .map(CheckoutIntentsResponse::getRedirectUrl);
    }

    public Uni<Response> paymentStatusChange(String state, CheckoutMetadata metadata) {
        return repository.findById(metadata.getCheckoutDBId())
                .chain(checkoutModel -> {
                    CheckoutModel.PaymentStatus newStatus = CheckoutModel.PaymentStatus.valueOf(state.toUpperCase());

                    Uni<?> uni = Uni.createFrom().nullItem();

                    if (checkoutModel.getPaymentStatus().equals(newStatus))
                        return uni;

                    if (newStatus.equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
                        for (Long id : checkoutModel.getLicenseIds()) {
                            uni = uni.chain(__ -> licenceRepository.findById(id)
                                    .onFailure().recoverWithNull()
                                    .call(licenceModel -> {
                                        if (licenceModel == null) {
                                            ls.logAnonymous(LogModel.ActionType.UPDATE, LogModel.ObjectType.Licence,
                                                    "Fail to save payment for licence (checkout n°" + checkoutModel.getCheckoutId() + ")",
                                                    "", id);
                                            return Uni.createFrom().nullItem();
                                        }

                                        ls.logUpdateAnonymous("Paiement de la licence", licenceModel);
                                        licenceModel.setPay(true);

                                        if (licenceModel.getCertificate() != null && licenceModel.getCertificate()
                                                .length() > 3) {
                                            if (!licenceModel.isValidate())
                                                ls.logUpdateAnonymous("Validation automatique de la licence",
                                                        licenceModel);
                                            return licenceService.validateLicences(licenceModel);
                                        } else {
                                            return Panache.withTransaction(
                                                    () -> licenceRepository.persist(licenceModel));
                                        }
                                    }));
                        }
                    } else if (checkoutModel.getPaymentStatus().equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
                        for (Long id : checkoutModel.getLicenseIds()) {
                            uni = uni.chain(__ -> licenceRepository.findById(id)
                                    .onFailure().recoverWithNull()
                                    .call(licenceModel -> {
                                        if (licenceModel == null)
                                            return Uni.createFrom().nullItem();

                                        ls.logUpdateAnonymous("Annulation automatique du paiement de la licence",
                                                licenceModel);
                                        licenceModel.setPay(false);
                                        if (licenceModel.isValidate())
                                            ls.logUpdateAnonymous(
                                                    "Annulation automatique de la validation de la licence",
                                                    licenceModel);
                                        licenceModel.setValidate(false);
                                        return Panache.withTransaction(() -> licenceRepository.persist(licenceModel));
                                    }));
                        }
                    }
                    uni = uni.call(__ -> ls.append());

                    checkoutModel.setPaymentStatus(newStatus);
                    return uni.chain(__ -> Panache.withTransaction(() -> repository.persist(checkoutModel)));
                })
                .onFailure().invoke(Throwable::printStackTrace)
                .map(__ -> Response.ok().build());
    }

    @Scheduled(cron = "0 0 * * * ?")
    Uni<Void> everyHours() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, -1);
        Date dateLimit = calendar.getTime();

        return repository.delete("creationDate < ?1 AND (checkoutId IS NULL OR paymentStatus = ?2)", dateLimit,
                CheckoutModel.PaymentStatus.UNKNOW)
                .map(__ -> null);
    }
}
