use std::fmt::{Display, Formatter};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use sqlx::any::{AnyTypeInfo, AnyValue};
use sqlx::encode::IsNull;
use sqlx::error::BoxDynError;
use sqlx::postgres::any::{AnyTypeInfoKind, AnyValueKind};
use sqlx::{Any, Database, Decode, Encode, Type};

#[derive(Clone, Debug)]
pub struct IdentityType {
    inner: AnyValue,
}

impl IdentityType {
    pub fn new(value: AnyValue) -> Self {
        IdentityType { inner: value }
    }
}

impl From<AnyValue> for IdentityType {
    fn from(value: AnyValue) -> Self {
        IdentityType {
            inner: value,
        }
    }
}

impl From<IdentityType> for i16 {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::SmallInt(value) = val.inner.kind {
            return value;
        }
        panic!("IdentityType is not i16")
    }
}

impl From<IdentityType> for i64 {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::BigInt(value) = val.inner.kind {
            return value;
        }
        panic!("IdentityType is not i64")
    }
}

impl From<IdentityType> for i32 {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::Integer(value) = val.inner.kind {
            return value;
        }
        panic!("IdentityType is not i32")
    }
}

impl From<IdentityType> for f32 {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::Real(value) = val.inner.kind {
            return value;
        }
        panic!("IdentityType is not f32")
    }
}

impl From<IdentityType> for f64 {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::Double(value) = val.inner.kind {
            return value;
        }
        panic!("IdentityType is not f64")
    }
}

impl From<IdentityType> for String {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::Text(value) = val.inner.kind {
            return value.to_string();
        }
        panic!("IdentityType is not String")
    }
}

impl From<IdentityType> for Vec<u8> {
    fn from(val: IdentityType) -> Self {
        if let AnyValueKind::Blob(value) = val.inner.kind {
            return value.to_vec();
        }
        panic!("IdentityType is not u8")
    }
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Storable<T>(pub T);

impl <'r, T> Decode<'r, Any> for Storable<T> where T: DeserializeOwned + Serialize {
    fn decode(value: <Any as Database>::ValueRef<'r>) -> std::result::Result<Self, BoxDynError> {
        let value: &str = <&str as Decode<Any>>::decode(value)?;
        Ok(serde_json::from_str(value)?)
    }
}

impl <'r, T> Encode<'r, Any> for Storable<T> where T: DeserializeOwned + Serialize {
    fn encode_by_ref(&self, buf: &mut <Any as Database>::ArgumentBuffer<'r>) -> std::result::Result<IsNull, BoxDynError> {
        let value = serde_json::to_string(&self.0).unwrap();
        buf.0.push(AnyValueKind::Text(std::borrow::Cow::from(value)));
        Ok(IsNull::No)
    }
}

impl <T> Type<Any> for Storable<T> {
    fn type_info() -> <Any as Database>::TypeInfo {
        AnyTypeInfo {
            kind: AnyTypeInfoKind::Text
        }
    }
}

pub enum TriggerType {
    AfterInsert, // activated after data is inserted into the table. 
    AfterUpdate, // activated after data in the table is modified. 
    AfterDelete, // activated after data is deleted/removed from the table. 
    BeforeInsert, // activated before data is inserted into the table. 
    BeforeUpdate, // activated before data in the table is modified. 
    BeforeDelete, // activated before data is deleted/removed from the table. 
}

impl Display for TriggerType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            TriggerType::AfterInsert => {
                "AFTER INSERT"
            },
            TriggerType::AfterUpdate => {
                "AFTER UPDATE"
            },
            TriggerType::AfterDelete => {
                "AFTER DELETE"
            },
            TriggerType::BeforeInsert => {
                "BEFORE INSERT"
            },
            TriggerType::BeforeUpdate => {
                "BEFORE UPDATE"
            },
            TriggerType::BeforeDelete => {
                "BEFORE DELETE"
            },
        })
    }
}