use chrono::NaiveDate;
use serde::{Deserialize, Serialize};

mod date_format {
    use chrono::NaiveDate;
    use serde::{self, Deserialize, Deserializer, Serializer};

    const FORMAT: &str = "%Y-%m-%d";

    pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&date.format(FORMAT).to_string())
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
    where
        D: Deserializer<'de>,
    {
        let text = String::deserialize(deserializer)?;
        NaiveDate::parse_from_str(&text, FORMAT).map_err(serde::de::Error::custom)
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum OrderSide {
    Buy,
    Sell,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum OrderStatus {
    Pending,
    Filled,
    PartiallyFilled,
    Canceled,
    Rejected,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderEvent {
    #[serde(with = "date_format")]
    pub date: NaiveDate,
    #[serde(default)]
    pub order_id: Option<u64>,
    pub symbol: String,
    pub side: OrderSide,
    pub requested_quantity: u32,
    pub filled_quantity: u32,
    pub status: OrderStatus,
    pub reason: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FillEvent {
    #[serde(with = "date_format")]
    pub date: NaiveDate,
    #[serde(default)]
    pub order_id: Option<u64>,
    pub symbol: String,
    pub side: OrderSide,
    pub quantity: u32,
    pub price: f64,
    pub gross_amount: f64,
    pub commission: f64,
    pub stamp_tax: f64,
    pub net_cash_flow: f64,
    pub reason: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionEvent {
    #[serde(with = "date_format")]
    pub date: NaiveDate,
    pub symbol: String,
    pub delta_quantity: i32,
    pub quantity_after: u32,
    pub average_cost: f64,
    pub realized_pnl_delta: f64,
    pub reason: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountEvent {
    #[serde(with = "date_format")]
    pub date: NaiveDate,
    pub cash_before: f64,
    pub cash_after: f64,
    pub total_equity: f64,
    pub note: String,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum ProcessEventKind {
    PreBeforeTrading,
    BeforeTrading,
    PostBeforeTrading,
    PreOpenAuction,
    OpenAuction,
    PostOpenAuction,
    PreScheduled,
    PostScheduled,
    PreOnDay,
    OnDay,
    PostOnDay,
    PreAfterTrading,
    AfterTrading,
    PostAfterTrading,
    PreSettlement,
    Settlement,
    PostSettlement,
    OrderPendingNew,
    OrderCreationPass,
    OrderCreationReject,
    OrderPendingCancel,
    OrderCancellationPass,
    OrderCancellationReject,
    OrderUnsolicitedUpdate,
    Trade,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessEvent {
    #[serde(with = "date_format")]
    pub date: NaiveDate,
    pub kind: ProcessEventKind,
    #[serde(default)]
    pub order_id: Option<u64>,
    #[serde(default)]
    pub symbol: Option<String>,
    #[serde(default)]
    pub side: Option<OrderSide>,
    pub detail: String,
}
