// 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.doris.statistics.query;

import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.TableIf.TableType;
import org.apache.doris.common.util.Util;

import com.google.common.collect.ImmutableMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * This class is used to store the query statistics of materialized views.
 */
public class IndexStats {
    private ConcurrentHashMap<String, AtomicLong> columnQueryStats;
    private ConcurrentHashMap<String, AtomicLong> columnFilterStats;
    private AtomicLong queryHit;
    private TableIf table;
    private long indexId;

    /**
     * The statistics of the column in the materialized view.
     */
    public IndexStats(TableIf table, long indexId) {
        this.indexId = indexId;
        columnQueryStats = new ConcurrentHashMap<>();
        columnFilterStats = new ConcurrentHashMap<>();
        queryHit = new AtomicLong(0L);
        this.table = table;
    }

    public TableIf getTable() {
        return table;
    }

    public ConcurrentHashMap<String, AtomicLong> getColumnQueryStats() {
        return columnQueryStats;
    }

    public ConcurrentHashMap<String, AtomicLong> getColumnFilterStats() {
        return columnFilterStats;
    }

    private void addStats(String column, ConcurrentHashMap<String, AtomicLong> stats) {
        AtomicLong l = stats.get(column);
        if (l == null) {
            l = new AtomicLong(0);
            AtomicLong old = stats.putIfAbsent(column, l);
            if (old == null) {
                l.updateAndGet(Util.overflowSafeIncrement());
            } else {
                old.updateAndGet(Util.overflowSafeIncrement());
            }
        } else {
            l.updateAndGet(Util.overflowSafeIncrement());
        }
    }

    /**
     * Add the query statistics of the column to the materialized view.
     */
    public void addStats(Map<String, ColumnStatsDelta> columnStats) {
        queryHit.updateAndGet(Util.overflowSafeIncrement());
        for (Map.Entry<String, ColumnStatsDelta> columnStat : columnStats.entrySet()) {
            if (columnStat.getValue().filterHit) {
                addFilterStats(columnStat.getKey());
            }
            if (columnStat.getValue().queryHit) {
                addQueryStats(columnStat.getKey());
            }
        }
    }

    public void addQueryStats(String column) {
        addStats(column, columnQueryStats);
    }

    public void addFilterStats(String column) {
        addStats(column, columnFilterStats);
    }

    /**
     * Get the query statistics of the column in the materialized view.
     */
    public long getQueryStats(String column) {
        AtomicLong l = columnQueryStats.get(column);
        if (l == null) {
            return 0;
        } else {
            return l.get();
        }
    }

    /**
     * Get the query statistics of the materialized view.
     */
    public Long getQueryStats() {
        return queryHit.get();
    }

    /**
     * Get the filter statistics of all columns in the materialized view.
     */
    public long getFilterStats(String column) {
        AtomicLong l = columnFilterStats.get(column);
        if (l == null) {
            return 0;
        } else {
            return l.get();
        }
    }

    /**
     * Get the maximum filter statistics of all columns in the materialized view.
     */
    public Map<String, Map> getStats(boolean summary) {
        List<Column> indexColumns;
        if (table.getType() == TableType.OLAP) {
            OlapTable olapTable = (OlapTable) table;
            indexColumns = olapTable.getSchemaByIndexId(indexId);
        } else {
            indexColumns = table.getBaseSchema();
        }
        Map<String, Map> stat = new HashMap<>();
        stat.put("summary", ImmutableMap.of("query", getQueryStats()));
        if (!summary) {
            Map<String, Map> columnStats = new HashMap<>();

            for (Column column : indexColumns) {
                Map<String, Long> dstat = new HashMap<>();
                dstat.put("query", getQueryStats(column.getName()));
                dstat.put("filter", getFilterStats(column.getName()));
                columnStats.put(column.getName(), dstat);
            }
            stat.put("detail", columnStats);
        }
        return stat;
    }

    public void rename(String column, String newName) {
        AtomicLong queryStata = columnQueryStats.get(column);
        if (queryStata != null) {
            columnQueryStats.put(newName, queryStata);
            columnQueryStats.remove(column);
        }
        AtomicLong filterStats = columnFilterStats.get(column);
        if (filterStats != null) {
            columnFilterStats.put(newName, filterStats);
            columnFilterStats.remove(column);
        }
    }
}
