package com.thinkbiganalytics.feedmgr.service.feed;

/*-
 * #%L
 * thinkbig-feed-manager-controller
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.thinkbiganalytics.feedmgr.rest.model.FeedCategory;
import com.thinkbiganalytics.feedmgr.rest.model.FeedMetadata;
import com.thinkbiganalytics.feedmgr.rest.model.FeedSummary;
import com.thinkbiganalytics.feedmgr.rest.model.NifiFeed;
import com.thinkbiganalytics.feedmgr.rest.model.RegisteredTemplate;
import com.thinkbiganalytics.feedmgr.rest.model.UIFeed;
import com.thinkbiganalytics.feedmgr.rest.model.UserField;
import com.thinkbiganalytics.feedmgr.rest.model.UserProperty;
import com.thinkbiganalytics.feedmgr.service.FileObjectPersistence;
import com.thinkbiganalytics.feedmgr.service.category.FeedManagerCategoryService;
import com.thinkbiganalytics.feedmgr.service.template.FeedManagerTemplateService;
import com.thinkbiganalytics.metadata.api.feed.Feed;
import com.thinkbiganalytics.nifi.rest.client.LegacyNifiRestClient;
import com.thinkbiganalytics.policy.rest.model.FieldRuleProperty;
import com.thinkbiganalytics.rest.model.LabelValue;
import com.thinkbiganalytics.security.action.Action;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import javax.inject.Inject;

/**
 * In memory implementation
 */
public class InMemoryFeedManagerFeedService implements FeedManagerFeedService {

    @Inject
    private LegacyNifiRestClient nifiRestClient;

    @Inject
    private FeedManagerCategoryService categoryProvider;

    @Inject
    private FeedManagerTemplateService templateProvider;

    private Map<String, FeedMetadata> feeds = new HashMap<>();

    @PostConstruct
    private void postConstruct() {
        Collection<FeedMetadata> savedFeeds = FileObjectPersistence.getInstance().getFeedsFromFile();
        if (savedFeeds != null) {
            Long maxId = 0L;
            for (FeedMetadata feed : savedFeeds) {

                //update the category mappings
                String categoryId = feed.getCategory().getId();
                FeedCategory category = categoryProvider.getCategoryById(categoryId);
                feed.setCategory(category);
                category.addRelatedFeed(new FeedSummary(feed));
                //add it to the map
                feeds.put(feed.getId(), feed);
            }

            loadSavedFeedsToMetaClientStore();
        }
    }

    @Override
    public boolean checkFeedPermission(String id, Action action, Action... more) {
        // Permission checking not currently implemented for the in-memory implementation
        return true;
    }

    public Collection<FeedMetadata> getFeeds() {
        return feeds.values();
    }

    public Collection<? extends UIFeed> getFeeds(boolean verbose) {
        if (verbose) {
            return getFeeds();
        } else {
            return getFeedSummaryData();
        }

    }

    public List<FeedSummary> getFeedSummaryData() {
        List<FeedSummary> summaryList = new ArrayList<>();
        if (feeds != null && !feeds.isEmpty()) {
            for (FeedMetadata feed : feeds.values()) {
                summaryList.add(new FeedSummary(feed));
            }
        }
        return summaryList;
    }


    public List<FeedSummary> getFeedSummaryForCategory(String categoryId) {
        List<FeedSummary> summaryList = new ArrayList<>();
        FeedCategory category = categoryProvider.getCategoryById(categoryId);
        if (category != null && category.getFeeds() != null) {
            summaryList.addAll(category.getFeeds());
        }
        return summaryList;
    }


    @Override
    public FeedMetadata getFeedByName(final String categoryName, final String feedName) {

        if (feeds != null && !feeds.isEmpty()) {
            return Iterables.tryFind(feeds.values(), new Predicate<FeedMetadata>() {
                @Override
                public boolean apply(FeedMetadata metadata) {

                    return metadata.getFeedName().equalsIgnoreCase(feedName) && metadata.getCategoryName().equalsIgnoreCase(categoryName);
                }
            }).orNull();
        }
        return feeds.get(feedName);
    }

    @Override
    public FeedMetadata getFeedById(String id) {
        return getFeedById(id, false);
    }

    @Override
    public FeedMetadata getFeedById(String id, boolean refreshTargetTableSchema) {

        if (feeds != null && !feeds.isEmpty()) {
            FeedMetadata feed = feeds.get(id);
            if (feed != null) {
                //get the latest category data
                FeedCategory category = categoryProvider.getCategoryById(feed.getCategory().getId());
                feed.setCategory(category);

                //set the template to the feed

                RegisteredTemplate registeredTemplate = templateProvider.getRegisteredTemplate(feed.getTemplateId());

                if (registeredTemplate != null) {
                    RegisteredTemplate copy = new RegisteredTemplate(registeredTemplate);
                    copy.getProperties().clear();
                    feed.setRegisteredTemplate(copy);
                    feed.setTemplateId(copy.getNifiTemplateId());
                }

                return feed;
            }
        }

        return null;
    }


    @Override
    public List<FeedMetadata> getFeedsWithTemplate(final String registeredTemplateId) {
        return Lists.newArrayList(Iterables.filter(feeds.values(), new Predicate<FeedMetadata>() {
            @Override
            public boolean apply(FeedMetadata feed) {
                return feed.getTemplateId().equalsIgnoreCase(registeredTemplateId);
            }
        }));
    }

    @Override
    public Feed.ID resolveFeed(@Nonnull Serializable fid) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void enableFeedCleanup(@Nonnull String feedId) {
        throw new UnsupportedOperationException();
    }

    /**
     * Needed to rewire feeds to ids in the server upon server start since the Feed Metadata store is in memory now
     */
    private void loadSavedFeedsToMetaClientStore() {
        for (FeedMetadata feedMetadata : feeds.values()) {
            feedMetadata.setFeedId(null);
            //  saveToMetadataStore(feedMetadata);
        }
    }


    public void saveFeed(FeedMetadata feed) {
        if (feed.getId() == null || !feeds.containsKey(feed.getId())) {
            feed.setId(UUID.randomUUID().toString());
            feed.setVersion(new Long(1));
        } else {
            FeedMetadata previousFeed = feeds.get(feed.getId());
            feed.setId(previousFeed.getId());
            feed.setVersion(previousFeed.getVersion() + 1L);
        }

        //match up the related category
        String categoryId = feed.getCategory().getId();
        FeedCategory category = null;
        if (categoryId != null) {
            category = categoryProvider.getCategoryById(categoryId);
        }
        if (category == null) {
            final String categoryName = feed.getCategory().getSystemName();
            category = categoryProvider.getCategoryBySystemName(categoryName);
            feed.setCategory(category);
        }
        if (category != null) {
            category.addRelatedFeed(new FeedSummary(feed));
        }

        //  saveToMetadataStore(feed);
        feeds.put(feed.getId(), feed);
        FileObjectPersistence.getInstance().writeFeedsToFile(feeds.values());
    }

    @Override
    public void deleteFeed(@Nonnull String feedId) {
        feeds.remove(feedId);
    }

    @Override
    public FeedSummary enableFeed(String feedId) {
        FeedMetadata feedMetadata = getFeedById(feedId);
        if (feedMetadata != null) {
            feedMetadata.setState("ENABLED");
            return new FeedSummary(feedMetadata);
        }
        return null;
    }

    @Override
    public FeedSummary disableFeed(String feedId) {
        FeedMetadata feedMetadata = getFeedById(feedId);
        if (feedMetadata != null) {
            feedMetadata.setState("DISABLED");
            return new FeedSummary(feedMetadata);
        }
        return null;
    }

    @Override
    public void applyFeedSelectOptions(List<FieldRuleProperty> properties) {
        if (properties != null && !properties.isEmpty()) {
            List<FeedSummary> feedSummaries = getFeedSummaryData();
            List<LabelValue> feedSelection = new ArrayList<>();
            for (FeedSummary feedSummary : feedSummaries) {
                feedSelection.add(new LabelValue(feedSummary.getCategoryAndFeedDisplayName(), feedSummary.getCategoryAndFeedSystemName()));
            }
            for (FieldRuleProperty property : properties) {
                property.setSelectableValues(feedSelection);
                if (property.getValues() == null) {
                    property.setValues(new ArrayList<>()); // reset the intial values to be an empty arraylist
                }
            }
        }
    }

    @Nonnull
    @Override
    public Set<UserField> getUserFields() {
        return Collections.emptySet();
    }

    @Override
    public void setUserFields(@Nonnull Set<UserField> userFields) {
    }

    @Nonnull
    @Override
    public Optional<Set<UserProperty>> getUserFields(@Nonnull String categoryId) {
        return Optional.of(Collections.emptySet());
    }

    @Override
    public NifiFeed createFeed(FeedMetadata feedMetadata) {
        saveFeed(feedMetadata);
        NifiFeed nifiFeed = new NifiFeed();
        nifiFeed.setFeedMetadata(feedMetadata);
        nifiFeed.setSuccess(true);
        return nifiFeed;
    }
}
