use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ManagementColumnRequest {
    pub name: String,
    pub data_type: String,
    #[serde(default = "default_nullable")]
    pub nullable: bool,
    #[serde(default)]
    pub default_expression: Option<String>,
}

fn default_nullable() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CreateTableRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    pub table_name: String,
    #[serde(default)]
    pub columns: Vec<ManagementColumnRequest>,
    #[serde(default)]
    pub if_not_exists: bool,
}

fn default_schema_name() -> String {
    "public".to_string()
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TableEditOperation {
    AddColumn {
        column: ManagementColumnRequest,
    },
    RenameColumn {
        from: String,
        to: String,
    },
    SetDefault {
        column_name: String,
        default_expression: String,
    },
    DropDefault {
        column_name: String,
    },
    SetNotNull {
        column_name: String,
    },
    DropNotNull {
        column_name: String,
    },
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct EditTableRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    pub operations: Vec<TableEditOperation>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DropTableRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    #[serde(default)]
    pub cascade: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DropColumnRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    #[serde(default)]
    pub cascade: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CreateIndexRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    pub table_name: String,
    #[serde(default)]
    pub index_name: Option<String>,
    pub columns: Vec<String>,
    #[serde(default)]
    pub unique: bool,
    #[serde(default = "default_index_method")]
    pub method: String,
}

fn default_index_method() -> String {
    "btree".to_string()
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DropIndexRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InstallExtensionRequest {
    pub extension_name: String,
    #[serde(default)]
    pub if_not_exists: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ManagementFunctionsQuery {
    #[serde(default)]
    pub schema: Option<String>,
    #[serde(default)]
    pub name_like: Option<String>,
    #[serde(default)]
    pub include_system: bool,
    #[serde(default)]
    pub include_definition: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpsertFunctionRequest {
    pub ddl: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DropFunctionRequest {
    #[serde(default = "default_schema_name")]
    pub schema_name: String,
    pub function_name: String,
    pub arg_types: Vec<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct ManagementFunctionInfo {
    pub schema_name: String,
    pub function_name: String,
    pub signature: String,
    pub return_type: String,
    pub language: String,
    pub is_set_returning: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub definition: Option<String>,
    #[serde(default)]
    pub metadata: Value,
}

#[derive(Debug, Clone, Serialize)]
pub struct ManagementCapabilitiesResponse {
    pub backend: &'static str,
    pub retention_mutation_supported: bool,
    pub replication_mutation_supported: bool,
    pub supported_operations: Vec<&'static str>,
    pub required_rights: Vec<CapabilityRight>,
    pub allowed_index_methods: Vec<&'static str>,
    pub allowed_column_data_types: Vec<&'static str>,
}

#[derive(Debug, Clone, Serialize)]
pub struct CapabilityRight {
    pub operation: &'static str,
    pub required_right: &'static str,
}
