use chrono::NaiveDateTime;
use rust_decimal::Decimal;
use tokio_postgres::Row;

use petro::entity::Entity;
use petro::entity::EntityColumn;
use petro::entity::EntityId;
use petro::entity::EntityMetadata;
use petro::entity::EntityRef;
use petro::query::QueryBindings;

use super::Customer;

#[derive(Debug, Clone)]
pub struct Order {
    pub id: EntityId<Order>,
    pub order_number: String,
    pub order_date: Option<NaiveDateTime>,
    pub customer: EntityRef<Customer>,
    pub total_amount: Decimal,
}

pub struct OrderColumns {
    pub id: &'static EntityColumn<EntityId<Order>>,
    pub order_number: &'static EntityColumn<String>,
    pub order_date: &'static EntityColumn<Option<NaiveDateTime>>,
    pub customer_id: &'static EntityColumn<EntityId<Customer>>,
    pub total_amount: &'static EntityColumn<Decimal>,
}

impl Order {
    const C: &'static OrderColumns = &OrderColumns {
        id: &EntityColumn::new("id"),
        order_number: &EntityColumn::new("order_number"),
        order_date: &EntityColumn::new("order_date"),
        customer_id: &EntityColumn::new("customer_id"),
        total_amount: &EntityColumn::new("total_amount"),
    };
}

impl EntityMetadata for Order {
    const TABLE_NAME: &'static str = "sales_order";

    type Columns = OrderColumns;
    const C: &'static Self::Columns = Order::C;
    const COLUMN_NAMES: &'static [&'static str] = &[
        "id",
        "order_number",
        "order_date",
        "customer_id",
        "total_amount",
    ];
}

impl Entity for Order {
    const ID_COLUMN_INDEX: usize = 0;

    type EntityIdType = Self;
    fn id(&self) -> &EntityId<Self> {
        return &self.id;
    }

    fn from_db_row(row: &Row, offset: usize) -> Self {
        return Self {
            id: row.get(offset + 0),
            order_number: row.get(offset + 1),
            order_date: row.get(offset + 2),
            customer: row.get(offset + 3),
            total_amount: row.get(offset + 4),
        };
    }

    fn bind_db_params<'a>(&'a self, params: &mut QueryBindings<'a>) {
        params.add(&self.id);
        params.add(&self.order_number);
        params.add(&self.order_date);
        params.add(&self.customer);
        params.add(&self.total_amount);
    }
}
