/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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.apache.cassandra.tools.nodetool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.cassandra.tools.NodeProbe;
import org.apache.cassandra.tools.nodetool.layout.CassandraUsage;
import org.apache.cassandra.tools.nodetool.stats.StatsHolder;
import org.apache.cassandra.tools.nodetool.stats.StatsPrinter;
import org.apache.cassandra.tools.nodetool.stats.StatsTableComparator;
import org.apache.cassandra.tools.nodetool.stats.TableStatsHolder;
import org.apache.cassandra.tools.nodetool.stats.TableStatsPrinter;

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "tablestats", description = "Print statistics on tables")
public class TableStats extends AbstractCommand
{
    @CassandraUsage(usage = "[<keyspace.table>...]", description = "List of tables (or keyspace) names")
    @CommandLine.Parameters(paramLabel = "keyspace.table", index = "0..*", arity = "0..*", description = "List of tables (or keyspace) names")
    private List<String> tableNames = new ArrayList<>();

    @Option(names = { "-i" }, description = "Ignore the list of tables and display the remaining tables")
    private boolean ignore = false;

    @Option(paramLabel = "human_readable",
            names = { "-H", "--human-readable" },
            description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB")
    private boolean humanReadable = false;

    @Option(paramLabel = "format",
            names = { "-F", "--format" },
            description = "Output format (json, yaml)")
    private String outputFormat = "";

    @Option(paramLabel = "sort_key",
            names = { "-s", "--sort" },
            description = "Sort tables by specified sort key (average_live_cells_per_slice_last_five_minutes, "
                        + "average_tombstones_per_slice_last_five_minutes, bloom_filter_false_positives, "
                        + "bloom_filter_false_ratio, bloom_filter_off_heap_memory_used, bloom_filter_space_used, "
                        + "compacted_partition_maximum_bytes, compacted_partition_mean_bytes, "
                        + "compacted_partition_minimum_bytes, compression_dictionaries_memory_used, compression_metadata_off_heap_memory_used, "
                        + "full_name, index_summary_off_heap_memory_used, local_read_count, "
                        + "local_read_latency_ms, local_write_latency_ms, "
                        + "maximum_live_cells_per_slice_last_five_minutes, "
                        + "maximum_tombstones_per_slice_last_five_minutes, memtable_cell_count, memtable_data_size, "
                        + "memtable_off_heap_memory_used, memtable_switch_count, number_of_partitions_estimate, "
                        + "off_heap_memory_used_total, pending_flushes, percent_repaired, read_latency, reads, "
                        + "space_used_by_snapshots_total, space_used_live, space_used_total, "
                        + "sstable_compression_ratio, sstable_count, table_name, write_latency, writes, "
                        + "max_sstable_size, local_read_write_ratio, twcs_max_duration, sai_local_query_latency_ms, "
                        + "sai_post_filtering_read_latency, sai_disk_used_bytes, sai_sstable_indexes_hit, sai_index_segments_hit "
                        + "sai_rows_filtered, sai_total_query_timeouts, sai_total_queryable_index_ratio)")
    private String sortKey = "";

    @Option(paramLabel = "top",
            names = { "-t", "--top" },
            description = "Show only the top K tables for the sort key (specify the number K of tables to be shown")
    private int top = 0;

    @Option(paramLabel = "sstable_location_check",
            names = { "-l", "--sstable-location-check" },
            description = "Check whether or not the SSTables are in the correct location.")
    private boolean locationCheck = false;

    @Override
    public void execute(NodeProbe probe)
    {
        if (!outputFormat.isEmpty() && !"json".equals(outputFormat) && !"yaml".equals(outputFormat))
        {
            throw new IllegalArgumentException("arguments for -F are json,yaml only.");
        }

        if (!sortKey.isEmpty() && !Arrays.asList(StatsTableComparator.supportedSortKeys).contains(sortKey))
        {
            throw new IllegalArgumentException(String.format("argument for sort must be one of: %s",
                                               String.join(", ", StatsTableComparator.supportedSortKeys)));
        }

        if (top > 0 && sortKey.isEmpty())
        {
            throw new IllegalArgumentException("cannot filter top K tables without specifying a sort key.");
        }

        if (top < 0)
        {
            throw new IllegalArgumentException("argument for top must be a positive integer.");
        }

        StatsHolder holder = new TableStatsHolder(probe, humanReadable, ignore, tableNames, sortKey, top, locationCheck);
        // print out the keyspace and table statistics
        StatsPrinter printer = TableStatsPrinter.from(outputFormat, !sortKey.isEmpty());
        printer.print(holder, probe.output().out);
    }

}
