package org.hswebframework.web.system.authorization.defaults.service;

import org.apache.commons.codec.digest.DigestUtils;
import org.hswebframework.ezorm.rdb.mapping.ReactiveDelete;
import org.hswebframework.ezorm.rdb.mapping.ReactiveUpdate;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.authorization.DimensionProvider;
import org.hswebframework.web.authorization.DimensionType;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
import org.hswebframework.web.system.authorization.api.event.ClearUserAuthorizationCacheEvent;
import org.hswebframework.web.system.authorization.defaults.configuration.PermissionProperties;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.util.*;
import java.util.stream.Collectors;

public class DefaultAuthorizationSettingService extends GenericReactiveCrudService<AuthorizationSettingEntity, String> {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Autowired
    private List<DimensionProvider> providers;


    protected AuthorizationSettingEntity generateId(AuthorizationSettingEntity entity) {
        if (StringUtils.isEmpty(entity.getId())) {
            entity.setId(DigestUtils.md5Hex(entity.getPermission() + entity.getDimensionType() + entity.getDimensionTarget()));
        }
        return entity;
    }

    @Override
    public Mono<SaveResult> save(Publisher<AuthorizationSettingEntity> entityPublisher) {
        return Flux.from(entityPublisher)
                   .map(this::generateId)
                   .collectList()
                   .flatMap(autz -> super.save(Flux.fromIterable(autz)).doOnSuccess(r -> clearUserAuthCache(autz)));
    }

    @Override
    public Mono<Integer> updateById(String id, Mono<AuthorizationSettingEntity> entityPublisher) {
        return entityPublisher
                .flatMap(autz -> super.updateById(id, Mono.just(autz))
                                      .doOnSuccess((r) -> clearUserAuthCache(Collections.singletonList(autz))));
    }

    @Override
    public Mono<Integer> deleteById(Publisher<String> idPublisher) {
        Flux<String> cache = Flux.from(idPublisher);

        return this
                .findById(cache)
                .collectList()
                .flatMap(list -> this
                        .deleteById(cache)
                        .doOnSuccess((i) -> clearUserAuthCache(list))
                );
    }

    @Override
    public Mono<Integer> insert(Publisher<AuthorizationSettingEntity> entityPublisher) {

        return Flux.from(entityPublisher)
                   .map(this::generateId)
                   .collectList()
                   .flatMap(list -> super
                           .insert(Flux.fromIterable(list))
                           .doOnSuccess(i -> clearUserAuthCache(list)));
    }

    @Override
    public Mono<Integer> insertBatch(Publisher<? extends Collection<AuthorizationSettingEntity>> entityPublisher) {
        return Flux
                .from(entityPublisher)
                .collectList()
                .flatMap(list -> super
                        .insertBatch(Flux.fromStream(list.stream()
                                                         .map(lst -> lst.stream()
                                                                        .map(this::generateId)
                                                                        .collect(Collectors.toList()))))
                        .doOnSuccess(i -> clearUserAuthCache(list
                                                                     .stream()
                                                                     .flatMap(Collection::stream)
                                                                     .collect(Collectors.toList()))));
    }

    @Override
    public ReactiveUpdate<AuthorizationSettingEntity> createUpdate() {

        return super
                .createUpdate()
                .onExecute((update, r) -> r
                        .doOnSuccess(i -> this
                                .createQuery()
                                .setParam(update.toQueryParam())
                                .fetch()
                                .collectList()
                                .subscribe(this::clearUserAuthCache)));
    }

    @Override
    public ReactiveDelete createDelete() {
        return super.createDelete()
                    .onExecute((delete, r) -> r
                            .doOnSuccess(i -> this
                                    .createQuery()
                                    .setParam(delete.toQueryParam())
                                    .fetch()
                                    .collectList()
                                    .subscribe(this::clearUserAuthCache)));
    }

    protected void clearUserAuthCache(List<AuthorizationSettingEntity> settings) {
        Flux.fromIterable(providers)
            .flatMap(provider ->
                             //按维度类型进行映射
                             provider.getAllType()
                                     .map(DimensionType::getId)
                                     .map(t -> Tuples.of(t, provider)))
            .collect(Collectors.toMap(Tuple2::getT1, Tuple2::getT2))
            .flatMapMany(typeProviderMapping -> Flux
                    .fromIterable(settings)//根据维度获取所有userId
                    .flatMap(setting -> Mono.justOrEmpty(typeProviderMapping.get(setting.getDimensionType()))
                                            .flatMapMany(provider -> provider.getUserIdByDimensionId(setting.getDimensionTarget()))))
            .collectList()
            .map(ClearUserAuthorizationCacheEvent::of)
            .subscribe(eventPublisher::publishEvent);
    }
}
