//! Timestamp newtype — `"%Y%m%d%H%M%S"` format封装。
//!
//! 格式字符串只在此模块出现一次。engine层零格式字面量。

use chrono::{Local, NaiveDateTime};
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;

const FORMAT: &str = "%Y%m%d%H%M%S";

/// 14位时间戳（`YYYYMMDDHHmmss`格式）。
///
/// 封装 `chrono::NaiveDateTime`，对外只暴露格式化后的字符串表示。
/// 格式字符串 `"%Y%m%d%H%M%S"` 只在帽子层出现一次。
#[derive(Clone)]
pub struct Timestamp {
    inner: NaiveDateTime,
    formatted: String,
}

impl Timestamp {
    fn from_naive(dt: NaiveDateTime) -> Self {
        let formatted = dt.format(FORMAT).to_string();
        Self { inner: dt, formatted }
    }

    /// 当前本地时间。
    pub fn now() -> Self {
        Self::from_naive(Local::now().naive_local())
    }

    /// 解析 `"%Y%m%d%H%M%S"` 格式字符串。
    pub fn parse(s: &str) -> Option<Self> {
        let dt = NaiveDateTime::parse_from_str(s, FORMAT).ok()?;
        Some(Self::from_naive(dt))
    }

    /// 格式化后的字符串引用（14位 `YYYYMMDDHHmmss`）。
    pub fn as_str(&self) -> &str {
        &self.formatted
    }

    /// 内部 `NaiveDateTime` 引用。
    pub fn inner(&self) -> &NaiveDateTime {
        &self.inner
    }
}

impl fmt::Display for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.formatted)
    }
}

impl fmt::Debug for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Timestamp({})", self.formatted)
    }
}

impl AsRef<str> for Timestamp {
    fn as_ref(&self) -> &str {
        &self.formatted
    }
}

impl PartialEq for Timestamp {
    fn eq(&self, other: &Self) -> bool {
        self.inner == other.inner
    }
}

impl Eq for Timestamp {}

impl Default for Timestamp {
    fn default() -> Self {
        Self::from_naive(NaiveDateTime::default())
    }
}

impl FromStr for Timestamp {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Timestamp::parse(s).ok_or_else(|| format!("invalid timestamp: {}", s))
    }
}

impl PartialOrd for Timestamp {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Timestamp {
    fn cmp(&self, other: &Self) -> Ordering {
        self.inner.cmp(&other.inner)
    }
}

// ===== Promptable integration =====
// Timestamp renders as inline text (14-digit formatted string) in LLM prompts.

impl crate::llm::Reflectable for Timestamp {}
impl crate::llm::Promptable for Timestamp {
    fn to_markdown_depth(&self, _depth: usize) -> String {
        self.to_string()
    }
    fn to_markdown_item(&self) -> String {
        self.to_string()
    }
}

// ===== Serde integration =====
// Serializes as "%Y%m%d%H%M%S" string, deserializes by parsing back.

impl Serialize for Timestamp {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for Timestamp {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let s = String::deserialize(deserializer)?;
        Timestamp::parse(&s).ok_or_else(|| serde::de::Error::custom(format!("invalid timestamp: {}", s)))
    }
}

// ===== sqlx integration =====
// Timestamp stores as Text in SQLite (14-digit formatted string).
// Symmetric: Encode writes via as_str(), Decode re-parses via parse().

impl sqlx::Type<sqlx::Sqlite> for Timestamp {
    fn type_info() -> sqlx::sqlite::SqliteTypeInfo {
        <String as sqlx::Type<sqlx::Sqlite>>::type_info()
    }

    fn compatible(ty: &sqlx::sqlite::SqliteTypeInfo) -> bool {
        <String as sqlx::Type<sqlx::Sqlite>>::compatible(ty)
    }
}

impl<'q> sqlx::Encode<'q, sqlx::Sqlite> for Timestamp {
    fn encode_by_ref(
        &self,
        args: &mut Vec<sqlx::sqlite::SqliteArgumentValue<'q>>,
    ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
        <String as sqlx::Encode<'q, sqlx::Sqlite>>::encode_by_ref(&self.formatted, args)
    }
}

impl<'r> sqlx::Decode<'r, sqlx::Sqlite> for Timestamp {
    fn decode(
        value: sqlx::sqlite::SqliteValueRef<'r>,
    ) -> Result<Self, sqlx::error::BoxDynError> {
        let s = <String as sqlx::Decode<'r, sqlx::Sqlite>>::decode(value)?;
        Timestamp::parse(&s)
            .ok_or_else(|| format!("invalid timestamp: {}", s).into())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::{Datelike, Timelike};

    #[test]
    fn test_now_format() {
        let ts = Timestamp::now();
        assert_eq!(ts.as_str().len(), 14);
        // All digits
        assert!(ts.as_str().chars().all(|c| c.is_ascii_digit()));
    }

    #[test]
    fn test_parse_valid() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        assert_eq!(ts.as_str(), "20260325121958");
        assert_eq!(ts.to_string(), "20260325121958");
    }

    #[test]
    fn test_parse_invalid() {
        assert!(Timestamp::parse("not-a-date").is_none());
        assert!(Timestamp::parse("2026").is_none());
        assert!(Timestamp::parse("").is_none());
    }

    #[test]
    fn test_parse_invalid_date() {
        // Month 13 doesn't exist
        assert!(Timestamp::parse("20261325121958").is_none());
    }

    #[test]
    fn test_display() {
        let ts = Timestamp::parse("20260101000000").unwrap();
        assert_eq!(format!("{}", ts), "20260101000000");
    }

    #[test]
    fn test_debug() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        assert_eq!(format!("{:?}", ts), "Timestamp(20260325121958)");
    }

    #[test]
    fn test_as_ref_str() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        let s: &str = ts.as_ref();
        assert_eq!(s, "20260325121958");
    }

    #[test]
    fn test_clone() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        let ts2 = ts.clone();
        assert_eq!(ts, ts2);
    }

    #[test]
    fn test_eq() {
        let a = Timestamp::parse("20260325121958").unwrap();
        let b = Timestamp::parse("20260325121958").unwrap();
        let c = Timestamp::parse("20260325122000").unwrap();
        assert_eq!(a, b);
        assert_ne!(a, c);
    }

    #[test]
    fn test_ord() {
        let earlier = Timestamp::parse("20260325120000").unwrap();
        let later = Timestamp::parse("20260325130000").unwrap();
        assert!(earlier < later);
        assert!(later > earlier);
    }

    #[test]
    fn test_inner() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        let dt = ts.inner();
        assert_eq!(dt.year(), 2026);
        assert_eq!(dt.month(), 3);
        assert_eq!(dt.day(), 25);
        assert_eq!(dt.hour(), 12);
        assert_eq!(dt.minute(), 19);
        assert_eq!(dt.second(), 58);
    }

    #[test]
    fn test_roundtrip() {
        let original = "20260325121958";
        let ts = Timestamp::parse(original).unwrap();
        assert_eq!(ts.as_str(), original);

        // Parse back from display
        let ts2 = Timestamp::parse(&ts.to_string()).unwrap();
        assert_eq!(ts, ts2);
    }

    #[test]
    fn test_serde_serialize() {
        let ts = Timestamp::parse("20260325121958").unwrap();
        let json = serde_json::to_string(&ts).unwrap();
        assert_eq!(json, "\"20260325121958\"");
    }

    #[test]
    fn test_serde_deserialize() {
        let json = "\"20260325121958\"";
        let ts: Timestamp = serde_json::from_str(json).unwrap();
        assert_eq!(ts.as_str(), "20260325121958");
    }

    #[test]
    fn test_serde_roundtrip() {
        let ts = Timestamp::parse("20260101000000").unwrap();
        let json = serde_json::to_string(&ts).unwrap();
        let ts2: Timestamp = serde_json::from_str(&json).unwrap();
        assert_eq!(ts, ts2);
    }

    #[test]
    fn test_serde_invalid() {
        let result: Result<Timestamp, _> = serde_json::from_str("\"not-a-date\"");
        assert!(result.is_err());
    }

    #[test]
    fn test_to_markdown() {
        use crate::llm::Promptable;
        let ts = Timestamp::parse("20260325121958").unwrap();
        assert_eq!(ts.to_markdown_depth(2), "20260325121958");
        assert_eq!(ts.to_markdown_item(), "20260325121958");
        assert_eq!(ts.to_markdown(), "20260325121958");
    }

    #[test]
    fn test_serde_in_struct() {
        #[derive(Serialize, Deserialize, PartialEq, Debug)]
        struct Record {
            name: String,
            created_at: Timestamp,
        }
        let rec = Record {
            name: "test".to_string(),
            created_at: Timestamp::parse("20260325121958").unwrap(),
        };
        let json = serde_json::to_string(&rec).unwrap();
        assert!(json.contains("\"created_at\":\"20260325121958\""));
        let rec2: Record = serde_json::from_str(&json).unwrap();
        assert_eq!(rec, rec2);
    }
}
