package ru.yandex.direct.api.v5.entity.agencyclients.service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import com.google.common.collect.ImmutableMap;
import com.yandex.direct.api.v5.agencyclients.AgencyClientUpdateItem;
import com.yandex.direct.api.v5.agencyclients.UpdateRequest;
import com.yandex.direct.api.v5.general.YesNoEnum;
import com.yandex.direct.api.v5.generalclients.GrantItem;
import com.yandex.direct.api.v5.generalclients.PrivilegeEnum;
import one.util.streamex.StreamEx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.direct.api.v5.converter.PropertyConverter;
import ru.yandex.direct.api.v5.converter.PropertyConverterFactory;
import ru.yandex.direct.api.v5.entity.clients.service.UpdateConverter;
import ru.yandex.direct.core.entity.client.model.Client;
import ru.yandex.direct.core.entity.grants.model.Grants;
import ru.yandex.direct.core.entity.user.model.User;
import ru.yandex.direct.dbutil.model.ClientId;
import ru.yandex.direct.model.ModelChanges;
import ru.yandex.direct.rbac.RbacService;

import static com.yandex.direct.api.v5.generalclients.PrivilegeEnum.EDIT_CAMPAIGNS;
import static com.yandex.direct.api.v5.generalclients.PrivilegeEnum.IMPORT_XLS;
import static com.yandex.direct.api.v5.generalclients.PrivilegeEnum.TRANSFER_MONEY;
import static ru.yandex.direct.utils.FunctionalUtils.mapList;

@Component
public class UpdateAgencyClientConverter {
    private static final Map<PrivilegeEnum, PropertyConverter<Grants, YesNoEnum, Boolean>> GRANTS_PROPS =
            ImmutableMap.of(
                    EDIT_CAMPAIGNS, PropertyConverterFactory.yesToTrue(Grants.ALLOW_EDIT_CAMPAIGN),
                    IMPORT_XLS, PropertyConverterFactory.yesToTrue(Grants.ALLOW_IMPORT_XLS),
                    TRANSFER_MONEY, PropertyConverterFactory.yesToTrue(Grants.ALLOW_TRANSFER_MONEY)
            );

    private final RbacService rbacService;

    @Autowired
    public UpdateAgencyClientConverter(RbacService rbacService) {
        this.rbacService = rbacService;
    }

    public List<UserAgencyClientChanges> convertToChanges(UpdateRequest request) {
        List<ClientId> clientIds = request.getClients().stream()
                .map(AgencyClientUpdateItem::getClientId)
                .map(ClientId::fromLong)
                .collect(Collectors.toList());
        Map<ClientId, Long> ids = rbacService.getChiefsByClientIds(clientIds);
        return mapList(request.getClients(),
                item -> convertToChanges(item, ids.get(ClientId.fromLong(item.getClientId()))));
    }

    public static UserAgencyClientChanges convertToChanges(AgencyClientUpdateItem updateItem, @Nullable Long uid) {
        // Значение uid не получаем из запроса пользователя, а определяем по clientId. Если clientId неверный, то uid
        // может отсутствовать. В этом случае заменяем его 0, чтобы не сломать ModelChanges. Валидация ModelChanges
        // должна пометить ошибкой некорректный uid, но эту ошибку не стоит выдавать пользователю,
        // т.к. значение uid вычисляемое.
        ModelChanges<User> userChanges = new ModelChanges<>(uid != null ? uid : 0, User.class);
        UpdateConverter.processUserChanges(userChanges, updateItem);

        Long clientId = updateItem.getClientId();
        ModelChanges<Client> clientChanges = new ModelChanges<>(clientId, Client.class);
        UpdateConverter.processClientChanges(clientChanges, updateItem);

        ModelChanges<Grants> grantsChanges = new ModelChanges<>(clientId, Grants.class);
        StreamEx.of(updateItem.getGrants())
                .mapToEntry(GrantItem::getPrivilege, GrantItem::getValue)
                .forKeyValue((privilege, value) -> {
                    PropertyConverter<Grants, YesNoEnum, Boolean> propertyConverter = GRANTS_PROPS.get(privilege);
                    grantsChanges.process(propertyConverter.apply(value), propertyConverter.getProperty());
                });

        return new UserAgencyClientChanges(userChanges, clientChanges, grantsChanges);
    }
}
