// Copyright 2023 Greptime Team
//
// 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.

use std::sync::Arc;

use common_catalog::consts::{METRIC_ENGINE, MITO_ENGINE};
use datatypes::schema::{Schema, SchemaRef};
use datatypes::vectors::{Int64Vector, StringVector, VectorRef};

use crate::system_schema::information_schema::table_names::*;
use crate::system_schema::utils::tables::{
    bigint_column, string_column, string_columns, timestamp_micro_column,
};

const NO_VALUE: &str = "NO";

/// Find the schema and columns by the table_name, only valid for memory tables.
/// Safety: the user MUST ensure the table schema exists, panic otherwise.
pub(super) fn get_schema_columns(table_name: &str) -> (SchemaRef, Vec<VectorRef>) {
    let (column_schemas, columns): (_, Vec<VectorRef>) = match table_name {
        COLUMN_PRIVILEGES => (
            string_columns(&[
                "GRANTEE",
                "TABLE_CATALOG",
                "TABLE_SCHEMA",
                "TABLE_NAME",
                "COLUMN_NAME",
                "PRIVILEGE_TYPE",
                "IS_GRANTABLE",
            ]),
            vec![],
        ),

        COLUMN_STATISTICS => (
            string_columns(&[
                "SCHEMA_NAME",
                "TABLE_NAME",
                "COLUMN_NAME",
                // TODO(dennis): It must be a JSON type, but we don't support it yet
                "HISTOGRAM",
            ]),
            vec![],
        ),

        ENGINES => (
            string_columns(&[
                "ENGINE",
                "SUPPORT",
                "COMMENT",
                "TRANSACTIONS",
                "XA",
                "SAVEPOINTS",
            ]),
            vec![
                Arc::new(StringVector::from(vec![MITO_ENGINE, METRIC_ENGINE])),
                Arc::new(StringVector::from(vec!["DEFAULT", "YES"])),
                Arc::new(StringVector::from(vec![
                    "Storage engine for time-series data",
                    "Storage engine for observability scenarios, which is adept at handling a large number of small tables, making it particularly suitable for cloud-native monitoring",
                ])),
                Arc::new(StringVector::from(vec![NO_VALUE, NO_VALUE])),
                Arc::new(StringVector::from(vec![NO_VALUE, NO_VALUE])),
                Arc::new(StringVector::from(vec![NO_VALUE, NO_VALUE])),
            ],
        ),

        BUILD_INFO => {
            let build_info = common_version::build_info();
            (
                string_columns(&[
                    "GIT_BRANCH",
                    "GIT_COMMIT",
                    "GIT_COMMIT_SHORT",
                    "GIT_CLEAN",
                    "PKG_VERSION",
                ]),
                vec![
                    Arc::new(StringVector::from(vec![build_info.branch.to_string()])),
                    Arc::new(StringVector::from(vec![build_info.commit.to_string()])),
                    Arc::new(StringVector::from(vec![
                        build_info.commit_short.to_string(),
                    ])),
                    Arc::new(StringVector::from(vec![build_info.clean.to_string()])),
                    Arc::new(StringVector::from(vec![build_info.version.to_string()])),
                ],
            )
        }

        CHARACTER_SETS => (
            vec![
                string_column("CHARACTER_SET_NAME"),
                string_column("DEFAULT_COLLATE_NAME"),
                string_column("DESCRIPTION"),
                bigint_column("MAXLEN"),
            ],
            vec![
                Arc::new(StringVector::from(vec!["utf8"])),
                Arc::new(StringVector::from(vec!["utf8_bin"])),
                Arc::new(StringVector::from(vec!["UTF-8 Unicode"])),
                Arc::new(Int64Vector::from_slice([4])),
            ],
        ),

        COLLATIONS => (
            vec![
                string_column("COLLATION_NAME"),
                string_column("CHARACTER_SET_NAME"),
                bigint_column("ID"),
                string_column("IS_DEFAULT"),
                string_column("IS_COMPILED"),
                bigint_column("SORTLEN"),
            ],
            vec![
                Arc::new(StringVector::from(vec!["utf8_bin"])),
                Arc::new(StringVector::from(vec!["utf8"])),
                Arc::new(Int64Vector::from_slice([1])),
                Arc::new(StringVector::from(vec!["Yes"])),
                Arc::new(StringVector::from(vec!["Yes"])),
                Arc::new(Int64Vector::from_slice([1])),
            ],
        ),

        COLLATION_CHARACTER_SET_APPLICABILITY => (
            vec![
                string_column("COLLATION_NAME"),
                string_column("CHARACTER_SET_NAME"),
            ],
            vec![
                Arc::new(StringVector::from(vec!["utf8_bin"])),
                Arc::new(StringVector::from(vec!["utf8"])),
            ],
        ),

        CHECK_CONSTRAINTS => (
            string_columns(&[
                "CONSTRAINT_CATALOG",
                "CONSTRAINT_SCHEMA",
                "CONSTRAINT_NAME",
                "CHECK_CLAUSE",
            ]),
            // Not support check constraints yet
            vec![],
        ),

        EVENTS => (
            vec![
                string_column("EVENT_CATALOG"),
                string_column("EVENT_SCHEMA"),
                string_column("EVENT_NAME"),
                string_column("DEFINER"),
                string_column("TIME_ZONE"),
                string_column("EVENT_BODY"),
                string_column("EVENT_DEFINITION"),
                string_column("EVENT_TYPE"),
                timestamp_micro_column("EXECUTE_AT"),
                bigint_column("INTERVAL_VALUE"),
                string_column("INTERVAL_FIELD"),
                string_column("SQL_MODE"),
                timestamp_micro_column("STARTS"),
                timestamp_micro_column("ENDS"),
                string_column("STATUS"),
                string_column("ON_COMPLETION"),
                timestamp_micro_column("CREATED"),
                timestamp_micro_column("LAST_ALTERED"),
                timestamp_micro_column("LAST_EXECUTED"),
                string_column("EVENT_COMMENT"),
                bigint_column("ORIGINATOR"),
                string_column("CHARACTER_SET_CLIENT"),
                string_column("COLLATION_CONNECTION"),
                string_column("DATABASE_COLLATION"),
            ],
            vec![],
        ),

        FILES => (
            vec![
                bigint_column("FILE_ID"),
                string_column("FILE_NAME"),
                string_column("FILE_TYPE"),
                string_column("TABLESPACE_NAME"),
                string_column("TABLE_CATALOG"),
                string_column("TABLE_SCHEMA"),
                string_column("TABLE_NAME"),
                string_column("LOGFILE_GROUP_NAME"),
                bigint_column("LOGFILE_GROUP_NUMBER"),
                string_column("ENGINE"),
                string_column("FULLTEXT_KEYS"),
                bigint_column("DELETED_ROWS"),
                bigint_column("UPDATE_COUNT"),
                bigint_column("FREE_EXTENTS"),
                bigint_column("TOTAL_EXTENTS"),
                bigint_column("EXTENT_SIZE"),
                bigint_column("INITIAL_SIZE"),
                bigint_column("MAXIMUM_SIZE"),
                bigint_column("AUTOEXTEND_SIZE"),
                timestamp_micro_column("CREATION_TIME"),
                timestamp_micro_column("LAST_UPDATE_TIME"),
                timestamp_micro_column("LAST_ACCESS_TIME"),
                timestamp_micro_column("RECOVER_TIME"),
                bigint_column("TRANSACTION_COUNTER"),
                string_column("VERSION"),
                string_column("ROW_FORMAT"),
                bigint_column("TABLE_ROWS"),
                bigint_column("AVG_ROW_LENGTH"),
                bigint_column("DATA_LENGTH"),
                bigint_column("MAX_DATA_LENGTH"),
                bigint_column("INDEX_LENGTH"),
                bigint_column("DATA_FREE"),
                timestamp_micro_column("CREATE_TIME"),
                timestamp_micro_column("UPDATE_TIME"),
                timestamp_micro_column("CHECK_TIME"),
                string_column("CHECKSUM"),
                string_column("STATUS"),
                string_column("EXTRA"),
            ],
            vec![],
        ),

        OPTIMIZER_TRACE => (
            vec![
                string_column("QUERY"),
                string_column("TRACE"),
                bigint_column("MISSING_BYTES_BEYOND_MAX_MEM_SIZE"),
                bigint_column("INSUFFICIENT_PRIVILEGES"),
            ],
            vec![],
        ),

        // MySQL(https://dev.mysql.com/doc/refman/8.2/en/information-schema-parameters-table.html)
        // has the spec that is different from
        // PostgreSQL(https://www.postgresql.org/docs/current/infoschema-parameters.html).
        // Follow `MySQL` spec here.
        PARAMETERS => (
            vec![
                string_column("SPECIFIC_CATALOG"),
                string_column("SPECIFIC_SCHEMA"),
                string_column("SPECIFIC_NAME"),
                bigint_column("ORDINAL_POSITION"),
                string_column("PARAMETER_MODE"),
                string_column("PARAMETER_NAME"),
                string_column("DATA_TYPE"),
                bigint_column("CHARACTER_MAXIMUM_LENGTH"),
                bigint_column("CHARACTER_OCTET_LENGTH"),
                bigint_column("NUMERIC_PRECISION"),
                bigint_column("NUMERIC_SCALE"),
                bigint_column("DATETIME_PRECISION"),
                string_column("CHARACTER_SET_NAME"),
                string_column("COLLATION_NAME"),
                string_column("DTD_IDENTIFIER"),
                string_column("ROUTINE_TYPE"),
            ],
            vec![],
        ),

        PROFILING => (
            vec![
                bigint_column("QUERY_ID"),
                bigint_column("SEQ"),
                string_column("STATE"),
                bigint_column("DURATION"),
                bigint_column("CPU_USER"),
                bigint_column("CPU_SYSTEM"),
                bigint_column("CONTEXT_VOLUNTARY"),
                bigint_column("CONTEXT_INVOLUNTARY"),
                bigint_column("BLOCK_OPS_IN"),
                bigint_column("BLOCK_OPS_OUT"),
                bigint_column("MESSAGES_SENT"),
                bigint_column("MESSAGES_RECEIVED"),
                bigint_column("PAGE_FAULTS_MAJOR"),
                bigint_column("PAGE_FAULTS_MINOR"),
                bigint_column("SWAPS"),
                string_column("SOURCE_FUNCTION"),
                string_column("SOURCE_FILE"),
                bigint_column("SOURCE_LINE"),
            ],
            vec![],
        ),

        // TODO: _Must_ reimplement this table when foreign key constraint is supported.
        REFERENTIAL_CONSTRAINTS => (
            vec![
                string_column("CONSTRAINT_CATALOG"),
                string_column("CONSTRAINT_SCHEMA"),
                string_column("CONSTRAINT_NAME"),
                string_column("UNIQUE_CONSTRAINT_CATALOG"),
                string_column("UNIQUE_CONSTRAINT_SCHEMA"),
                string_column("UNIQUE_CONSTRAINT_NAME"),
                string_column("MATCH_OPTION"),
                string_column("UPDATE_RULE"),
                string_column("DELETE_RULE"),
                string_column("TABLE_NAME"),
                string_column("REFERENCED_TABLE_NAME"),
            ],
            vec![],
        ),

        ROUTINES => (
            vec![
                string_column("SPECIFIC_NAME"),
                string_column("ROUTINE_CATALOG"),
                string_column("ROUTINE_SCHEMA"),
                string_column("ROUTINE_NAME"),
                string_column("ROUTINE_TYPE"),
                string_column("DATA_TYPE"),
                bigint_column("CHARACTER_MAXIMUM_LENGTH"),
                bigint_column("CHARACTER_OCTET_LENGTH"),
                bigint_column("NUMERIC_PRECISION"),
                bigint_column("NUMERIC_SCALE"),
                bigint_column("DATETIME_PRECISION"),
                string_column("CHARACTER_SET_NAME"),
                string_column("COLLATION_NAME"),
                string_column("DTD_IDENTIFIER"),
                string_column("ROUTINE_BODY"),
                string_column("ROUTINE_DEFINITION"),
                string_column("EXTERNAL_NAME"),
                string_column("EXTERNAL_LANGUAGE"),
                string_column("PARAMETER_STYLE"),
                string_column("IS_DETERMINISTIC"),
                string_column("SQL_DATA_ACCESS"),
                string_column("SQL_PATH"),
                string_column("SECURITY_TYPE"),
                timestamp_micro_column("CREATED"),
                timestamp_micro_column("LAST_ALTERED"),
                string_column("SQL_MODE"),
                string_column("ROUTINE_COMMENT"),
                string_column("DEFINER"),
                string_column("CHARACTER_SET_CLIENT"),
                string_column("COLLATION_CONNECTION"),
                string_column("DATABASE_COLLATION"),
            ],
            vec![],
        ),

        SCHEMA_PRIVILEGES => (
            vec![
                string_column("GRANTEE"),
                string_column("TABLE_CATALOG"),
                string_column("TABLE_SCHEMA"),
                string_column("PRIVILEGE_TYPE"),
                string_column("IS_GRANTABLE"),
            ],
            vec![],
        ),

        TABLE_PRIVILEGES => (
            vec![
                string_column("GRANTEE"),
                string_column("TABLE_CATALOG"),
                string_column("TABLE_SCHEMA"),
                string_column("TABLE_NAME"),
                string_column("PRIVILEGE_TYPE"),
                string_column("IS_GRANTABLE"),
            ],
            vec![],
        ),

        // TODO: Considering store internal metrics in `global_status` and
        // `session_status` tables.
        GLOBAL_STATUS => (
            vec![
                string_column("VARIABLE_NAME"),
                string_column("VARIABLE_VALUE"),
            ],
            vec![],
        ),

        SESSION_STATUS => (
            vec![
                string_column("VARIABLE_NAME"),
                string_column("VARIABLE_VALUE"),
            ],
            vec![],
        ),

        _ => unreachable!("Unknown table in information_schema: {}", table_name),
    };

    (Arc::new(Schema::new(column_schemas)), columns)
}
