/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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.
 */

package org.elasticsearch.rest.action.cat;

import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.indices.stats.CommonStats;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsTests;
import org.elasticsearch.action.admin.indices.stats.ShardStats;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.RecoverySource.PeerRecoverySource;
import org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.UnassignedInfo;
import org.elasticsearch.common.Table;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.cache.query.QueryCacheStats;
import org.elasticsearch.index.cache.request.RequestCacheStats;
import org.elasticsearch.index.engine.SegmentsStats;
import org.elasticsearch.index.fielddata.FieldDataStats;
import org.elasticsearch.index.flush.FlushStats;
import org.elasticsearch.index.get.GetStats;
import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.index.shard.IndexingStats;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardPath;
import org.elasticsearch.index.store.StoreStats;
import org.elasticsearch.index.warmer.WarmerStats;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.search.suggest.completion.CompletionStats;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestRequest;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo;

/**
 * Tests for {@link RestIndicesAction}
 */
public class RestIndicesActionTests extends ESTestCase {

    public void testBuildTable() {
        final Settings settings = Settings.EMPTY;
        final RestController restController = new RestController(settings, Collections.emptySet(), null, null, null);
        final RestIndicesAction action = new RestIndicesAction(settings, restController, new IndexNameExpressionResolver(settings));

        // build a (semi-)random table
        final int numIndices = randomIntBetween(0, 5);
        Index[] indices = new Index[numIndices];
        for (int i = 0; i < numIndices; i++) {
            indices[i] = new Index(randomAlphaOfLength(5), UUIDs.randomBase64UUID());
        }

        final MetaData.Builder metaDataBuilder = MetaData.builder();
        for (final Index index : indices) {
            metaDataBuilder.put(IndexMetaData.builder(index.getName())
                                    .settings(Settings.builder()
                                                  .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
                                                  .put(IndexMetaData.SETTING_INDEX_UUID, index.getUUID()))
                                    .creationDate(System.currentTimeMillis())
                                    .numberOfShards(1)
                                    .numberOfReplicas(1)
                                    .state(IndexMetaData.State.OPEN));
        }
        final MetaData metaData = metaDataBuilder.build();

        final ClusterState clusterState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY))
                                              .metaData(metaData)
                                              .build();
        final String[] indicesStr = new String[indices.length];
        for (int i = 0; i < indices.length; i++) {
            indicesStr[i] = indices[i].getName();
        }
        final ClusterHealthResponse clusterHealth = new ClusterHealthResponse(
            clusterState.getClusterName().value(), indicesStr, clusterState, 0, 0, 0, TimeValue.timeValueMillis(1000L)
        );

        final Table table = action.buildTable(new FakeRestRequest(), indices, clusterHealth, randomIndicesStatsResponse(indices), metaData);

        // now, verify the table is correct
        int count = 0;
        List<Table.Cell> headers = table.getHeaders();
        assertThat(headers.get(count++).value, equalTo("health"));
        assertThat(headers.get(count++).value, equalTo("status"));
        assertThat(headers.get(count++).value, equalTo("index"));
        assertThat(headers.get(count++).value, equalTo("uuid"));

        List<List<Table.Cell>> rows = table.getRows();
        assertThat(rows.size(), equalTo(indices.length));
        // TODO: more to verify (e.g. randomize cluster health, num primaries, num replicas, etc)
        for (int i = 0; i < rows.size(); i++) {
            count = 0;
            final List<Table.Cell> row = rows.get(i);
            assertThat(row.get(count++).value, equalTo("red*")); // all are red because cluster state doesn't have routing entries
            assertThat(row.get(count++).value, equalTo("open")); // all are OPEN for now
            assertThat(row.get(count++).value, equalTo(indices[i].getName()));
            assertThat(row.get(count++).value, equalTo(indices[i].getUUID()));
        }
    }

    private IndicesStatsResponse randomIndicesStatsResponse(final Index[] indices) {
        List<ShardStats> shardStats = new ArrayList<>();
        for (final Index index : indices) {
            for (int i = 0; i < 2; i++) {
                ShardId shardId = new ShardId(index, i);
                Path path = createTempDir().resolve("indices").resolve(index.getUUID()).resolve(String.valueOf(i));
                ShardRouting shardRouting = ShardRouting.newUnassigned(shardId, i == 0,
                    i == 0 ? StoreRecoverySource.EMPTY_STORE_INSTANCE : PeerRecoverySource.INSTANCE,
                    new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, null)
                    );
                shardRouting = shardRouting.initialize("node-0", null, ShardRouting.UNAVAILABLE_EXPECTED_SHARD_SIZE);
                shardRouting = shardRouting.moveToStarted();
                CommonStats stats = new CommonStats();
                stats.fieldData = new FieldDataStats();
                stats.queryCache = new QueryCacheStats();
                stats.docs = new DocsStats();
                stats.store = new StoreStats();
                stats.indexing = new IndexingStats();
                stats.search = new SearchStats();
                stats.segments = new SegmentsStats();
                stats.merge = new MergeStats();
                stats.refresh = new RefreshStats();
                stats.completion = new CompletionStats();
                stats.requestCache = new RequestCacheStats();
                stats.get = new GetStats();
                stats.flush = new FlushStats();
                stats.warmer = new WarmerStats();
                shardStats.add(new ShardStats(shardRouting, new ShardPath(false, path, path, shardId), stats, null, null));
            }
        }
        return IndicesStatsTests.newIndicesStatsResponse(
            shardStats.toArray(new ShardStats[shardStats.size()]), shardStats.size(), shardStats.size(), 0, emptyList()
        );
    }
}
