use log::{error, info};
use serde::{Deserialize, Serialize};
use sqlx::{Arguments, AssertSqlSafe, Decode, Encode, FromRow, PgPool, Row};
use sqlx::postgres::{PgArguments, PgRow};
use crate::models::label::Label;

#[derive(Debug, Deserialize, Serialize, Clone, FromRow, Decode, Encode)]
pub struct Supplier {
    pub id: i64,
    pub topm_id: String,
    pub name: String,
    pub bio_info: Option<String>,
}

fn from_postgres_row_small(row: PgRow) -> Supplier {
    Supplier {
        id: row.get(0),
        topm_id: row.get(1),
        name: row.get(2),
        bio_info: row.get(3),
    }
}

pub async fn load_suppliers(pool: PgPool) -> Option<Vec<Supplier>> {
    let statement = format!(
        "SELECT * FROM {}",
        "supplier",
    );

    let res = sqlx::query(
        AssertSqlSafe(statement.as_str()))
        .fetch_all(&pool).await;

    let mut suppliers: Vec<Supplier> = Vec::new();

    match res {
        Ok(records) => {
            for record in records {
                suppliers.push(
                    from_postgres_row_small(record)
                );
            }

            Some(suppliers)
        }
        Err(err) => {
            error!("Error while loading all suppliers");
            None
        }
    }
}

pub async fn load_supplier(id: i64, pool: &PgPool) -> Option<Supplier> {
    let statement = format!(
        "SELECT * FROM {} WHERE $1 = id",
        "supplier",
    );

    let mut args = PgArguments::default();
    args.add(id);

    let res = sqlx::query_with(
        AssertSqlSafe(statement.as_str()), args)
        .fetch_one(pool).await;

    match res {
        Ok(record) => {
            Some(from_postgres_row_small(record))
        }
        Err(err) => {
            error!("Error while loading supplier: {}", id);
            None
        }
    }
}

pub async fn insert_supplier(supplier: &Supplier, pool: PgPool) -> Option<Supplier> {
    let mut args = PgArguments::default();
    args.add(&supplier.topm_id);
    args.add(&supplier.name);
    args.add(&supplier.bio_info);

    let statement = format!(
        "INSERT INTO {} (topm_id, name, bio_info) VALUES ($1, $2, $3) RETURNING *",
        "supplier",
    );

    let res = sqlx::query_with(
        AssertSqlSafe(statement.as_str()),
        args)
        .fetch_one(&pool).await;

    info!("{:#?}", res);

    match res {
        Ok(record) => {
            let supplier = from_postgres_row_small(record);

            Some(supplier)
        }
        Err(err) => {
            error!("Error while loading newly created supplier: {}", supplier.name);
            None
        }
    }
}

pub async fn update_supplier(supplier_id: i64, supplier: &Supplier, pool: PgPool) -> Option<Supplier> {
    let mut args = PgArguments::default();
    args.add(&supplier.id);
    args.add(&supplier.topm_id);
    args.add(&supplier.name);
    args.add(&supplier.bio_info);

    let statement = format!(
        "UPDATE {} SET topm_id = $2, name = $3, bio_info = $4 WHERE id = $1 RETURNING *",
        "supplier",
    );

    let res = sqlx::query_with(
        AssertSqlSafe(statement.as_str()),
        args)
        .fetch_one(&pool).await;

    info!("{:#?}", res);

    match res {
        Ok(record) => {
            let supplier = from_postgres_row_small(record);
            Some(supplier)
        }
        Err(err) => {
            error!("Error while updating supplier: {}", supplier_id);
            None
        }
    }
}

pub async fn delete_supplier(supplier_id: i64, pool: PgPool) -> Option<()> {
    let result = sqlx::query(
        AssertSqlSafe(format!("DELETE FROM {} WHERE id = {}",
                              "supplier",
                              supplier_id
        ).as_str())
    ).execute(&pool).await;

    match result {
        Ok(_) => {
            Some(())
        }
        Err(err) => {
            println!("Error deleting supplier: {:#?}", err);

            None
        }
    }
}


