use crate::impl_from_and_to_type_option;
use crate::services::row::CellDataSerde;

use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyError;
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
use serde::{Deserialize, Serialize};

use strum_macros::EnumIter;

// Date
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
pub struct DateDescription {
    #[pb(index = 1)]
    pub date_format: DateFormat,

    #[pb(index = 2)]
    pub time_format: TimeFormat,
}
impl_from_and_to_type_option!(DateDescription, FieldType::DateTime);

impl DateDescription {
    #[allow(dead_code)]
    fn today_from_timestamp(&self, timestamp: i64) -> String {
        let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
        self.today_from_native(native)
    }

    fn today_from_native(&self, naive: chrono::NaiveDateTime) -> String {
        let utc: chrono::DateTime<chrono::Utc> = chrono::DateTime::from_utc(naive, chrono::Utc);
        let local: chrono::DateTime<chrono::Local> = chrono::DateTime::from(utc);

        let fmt_str = format!("{} {}", self.date_format.format_str(), self.time_format.format_str());
        let output = format!("{}", local.format_with_items(StrftimeItems::new(&fmt_str)));
        output
    }
}

impl CellDataSerde for DateDescription {
    fn deserialize_cell_data(&self, data: String) -> String {
        match data.parse::<i64>() {
            Ok(timestamp) => {
                let native = NaiveDateTime::from_timestamp(timestamp, 0);
                self.today_from_native(native)
            }
            Err(e) => {
                tracing::debug!("DateDescription format {} fail. error: {:?}", data, e);
                String::new()
            }
        }
    }

    fn serialize_cell_data(&self, data: &str) -> Result<String, FlowyError> {
        if let Err(e) = data.parse::<i64>() {
            tracing::error!("Parse {} to i64 failed: {}", data, e);
            return Err(FlowyError::internal().context(e));
        };
        Ok(data.to_owned())
    }
}

#[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum DateFormat {
    Local = 0,
    US = 1,
    ISO = 2,
    Friendly = 3,
}
impl std::default::Default for DateFormat {
    fn default() -> Self {
        DateFormat::Friendly
    }
}

impl std::convert::From<i32> for DateFormat {
    fn from(value: i32) -> Self {
        match value {
            0 => DateFormat::Local,
            1 => DateFormat::US,
            2 => DateFormat::ISO,
            3 => DateFormat::Friendly,
            _ => {
                tracing::error!("Unsupported date format, fallback to friendly");
                DateFormat::Friendly
            }
        }
    }
}

impl DateFormat {
    pub fn value(&self) -> i32 {
        *self as i32
    }
    // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html
    pub fn format_str(&self) -> &'static str {
        match self {
            DateFormat::Local => "%Y/%m/%d",
            DateFormat::US => "%Y/%m/%d",
            DateFormat::ISO => "%Y-%m-%d",
            DateFormat::Friendly => "%b %d,%Y",
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, EnumIter, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum TimeFormat {
    TwelveHour = 0,
    TwentyFourHour = 1,
}

impl std::convert::From<i32> for TimeFormat {
    fn from(value: i32) -> Self {
        match value {
            0 => TimeFormat::TwelveHour,
            1 => TimeFormat::TwentyFourHour,
            _ => {
                tracing::error!("Unsupported time format, fallback to TwentyFourHour");
                TimeFormat::TwentyFourHour
            }
        }
    }
}

impl TimeFormat {
    pub fn value(&self) -> i32 {
        *self as i32
    }

    // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html
    pub fn format_str(&self) -> &'static str {
        match self {
            TimeFormat::TwelveHour => "%r",
            TimeFormat::TwentyFourHour => "%R",
        }
    }
}

impl std::default::Default for TimeFormat {
    fn default() -> Self {
        TimeFormat::TwentyFourHour
    }
}

#[cfg(test)]
mod tests {
    use crate::services::cell::{DateDescription, DateFormat, TimeFormat};
    use crate::services::row::CellDataSerde;
    use strum::IntoEnumIterator;

    #[test]
    fn date_description_date_format_test() {
        let mut description = DateDescription::default();
        let _timestamp = 1647251762;

        for date_format in DateFormat::iter() {
            description.date_format = date_format;
            match date_format {
                DateFormat::Friendly => {
                    assert_eq!(
                        "Mar 14,2022 17:56".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "Mar 14,2022 17:56".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
                DateFormat::US => {
                    assert_eq!(
                        "2022/03/14 17:56".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "2022/03/14 17:56".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
                DateFormat::ISO => {
                    assert_eq!(
                        "2022-03-14 17:56".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "2022-03-14 17:56".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
                DateFormat::Local => {
                    assert_eq!(
                        "2022/03/14 17:56".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "2022/03/14 17:56".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
            }
        }
    }

    #[test]
    fn date_description_time_format_test() {
        let mut description = DateDescription::default();
        for time_format in TimeFormat::iter() {
            description.time_format = time_format;
            match time_format {
                TimeFormat::TwentyFourHour => {
                    assert_eq!(
                        "Mar 14,2022 17:56".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "Mar 14,2022 17:56".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
                TimeFormat::TwelveHour => {
                    assert_eq!(
                        "Mar 14,2022 05:56:02 PM".to_owned(),
                        description.today_from_timestamp(1647251762)
                    );
                    assert_eq!(
                        "Mar 14,2022 05:56:02 PM".to_owned(),
                        description.deserialize_cell_data("1647251762".to_owned())
                    );
                }
            }
        }
    }

    #[test]
    #[should_panic]
    fn date_description_invalid_data_test() {
        let description = DateDescription::default();
        description.serialize_cell_data("he").unwrap();
    }
}
