Documentation
use crate::model::reference::Reference;
use apache_avro::schema::{Name, RecordField, RecordFieldOrder};
use apache_avro::Schema;
use serde::Serialize;
use std::collections::BTreeMap;

/// 数据库表中列的元数据字段模型
#[derive(Debug, Clone, Serialize)]
pub struct Column<'a> {
    /// 列名
    name: &'a str,
    /// 列类型名称
    type_name: &'a str,
    /// 标记列是否为null的标志
    not_null: bool,
    /// 默认值
    #[serde(skip_serializing_if = "Option::is_none")]
    default_value: Option<&'a str>,
    /// 索引类型
    #[serde(skip_serializing_if = "Option::is_none")]
    index_type: Option<&'a str>,
    /// 外键列引用
    #[serde(skip_serializing_if = "Option::is_none")]
    reference: Option<Reference<'a>>,
}

impl<'a> Column<'a> {
    /// 创建实例
    pub fn new(name: &'a str, type_name: &'a str, not_null: bool) -> Self {
        Self {
            name,
            type_name,
            not_null,
            default_value: None,
            index_type: None,
            reference: None,
        }
    }

    /// 设置默认值.
    #[inline]
    pub fn set_default_value(&mut self, default_value: &'a str) {
        self.default_value = (!default_value.is_empty()).then_some(default_value);
    }

    /// 设置索引类型.
    #[inline]
    pub fn set_index_type(&mut self, index_type: &'a str) {
        self.index_type = (!index_type.is_empty()).then_some(index_type);
    }

    /// 设置外键引用.
    #[inline]
    pub fn set_reference(&mut self, reference: Reference<'a>) {
        self.reference = Some(reference);
    }

    /// 返回列字段名称.
    #[inline]
    pub fn name(&self) -> &'a str {
        self.name
    }

    /// 返回列字段类型.
    #[inline]
    pub fn type_name(&self) -> &'a str {
        self.type_name
    }

    /// 返回字段值是否允许为null.
    #[inline]
    pub fn is_not_null(&self) -> bool {
        self.not_null
    }

    /// 返回列字段的默认值.
    #[inline]
    pub fn default_value(&self) -> Option<&'a str> {
        self.default_value()
    }

    /// 返回字段的索引类型.
    #[inline]
    pub fn index_type(&self) -> Option<&'a str> {
        self.index_type
    }

    /// 返回列字段的引用 `reference`.
    #[inline]
    pub fn reference(&self) -> Option<&'a str> {
        self.reference()
    }

    /// 如果列字段的默认值为 `auto_increment`,那么就返回 `true`
    #[inline]
    pub fn auto_increment(&self) -> bool {
        self.default_value
            .is_some_and(|value| value == "auto_increment")
    }

    /// 根据列的字段类型,返回 [Apache Avro Scheme](apache_avro::schema::Schema).
    pub fn schema(&self) -> Schema {
        let type_name = self.type_name;
        match type_name {
            "bool" => Schema::Boolean,
            "i32" | "u32" | "i16" | "u16" | "i8" | "u8" => Schema::Int,
            "i64" | "u64" | "isize" | "usize" => Schema::Long,
            "f32" => Schema::Float,
            "f64" => Schema::Double,
            "String" | "Option<String>" => Schema::String,
            "Uuid" | "Option<Uuid>" => Schema::Uuid,
            "DateTime" => Schema::TimeMicros,
            "Vec<u8>" => Schema::Bytes,
            "Vec<Uuid>" => Schema::Array(Box::new(Schema::Uuid)),
            "Vec<String>" => Schema::Array(Box::new(Schema::String)),
            "Map" => Schema::Map(Box::new(Schema::Ref {
                name: Name {
                    name: "json".to_owned(), // to_owned()转移所有权
                    namespace: None,
                },
            })),
            _ => Schema::Ref {
                name: Name {
                    name: type_name.to_owned(),
                    namespace: None,
                },
            },
        }
    }

    /// 根据 Apache Avro Schema 返回 Avro Schema 字段类型 RecordField
    pub fn record_filed(&self) -> RecordField {
        let schema = self.schema();
        // 根据Schema重新定义字段数据的类型
        let default_value = self.default_value().and_then(|s| match schema {
            Schema::Int => s.parse::<i32>().ok().map(|i| i.into()),
            Schema::Long => s.parse::<i64>().ok().map(|i| i.into()),
            _ => Some(s.into()),
        });

        RecordField {
            name: self.name.to_owned(),
            doc: None,
            aliases: None,
            default: default_value,
            schema,
            order: RecordFieldOrder::Ascending,
            position: 0,
            custom_attributes: BTreeMap::new(),
        }
    }
}