use crate::error::DalError;
use crate::models::instrument::{ExchangeRow, InstrumentRow};
use sqlx::PgPool;

/// Upsert an exchange by name, returning its id.
pub async fn ensure_exchange(pool: &PgPool, name: &str) -> Result<i16, DalError> {
    let row = sqlx::query_as::<_, ExchangeRow>(
        "INSERT INTO exchanges (name)
         VALUES ($1)
         ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
         RETURNING id, name",
    )
    .bind(name)
    .fetch_one(pool)
    .await?;

    Ok(row.id)
}

/// Upsert an instrument, returning its id.
pub async fn ensure_instrument(
    pool: &PgPool,
    exchange_id: i16,
    name_exchange: &str,
    base_asset: &str,
    quote_asset: &str,
    kind: &str,
) -> Result<i32, DalError> {
    let row = sqlx::query_as::<_, InstrumentRow>(
        "INSERT INTO instruments (exchange_id, name_exchange, base_asset, quote_asset, kind)
         VALUES ($1, $2, $3, $4, $5)
         ON CONFLICT (exchange_id, name_exchange) DO UPDATE
             SET base_asset = EXCLUDED.base_asset,
                 quote_asset = EXCLUDED.quote_asset,
                 kind = EXCLUDED.kind
         RETURNING id, exchange_id, name_exchange, base_asset, quote_asset, kind",
    )
    .bind(exchange_id)
    .bind(name_exchange)
    .bind(base_asset)
    .bind(quote_asset)
    .bind(kind)
    .fetch_one(pool)
    .await?;

    Ok(row.id)
}

/// List all exchanges.
pub async fn list_exchanges(pool: &PgPool) -> Result<Vec<ExchangeRow>, DalError> {
    let rows = sqlx::query_as::<_, ExchangeRow>(
        "SELECT id, name FROM exchanges ORDER BY name",
    )
    .fetch_all(pool)
    .await?;

    Ok(rows)
}

/// Find an instrument by exchange name and instrument exchange name.
pub async fn find_by_exchange_and_name(
    pool: &PgPool,
    exchange_name: &str,
    name_exchange: &str,
) -> Result<Option<InstrumentRow>, DalError> {
    let row = sqlx::query_as::<_, InstrumentRow>(
        "SELECT i.id, i.exchange_id, i.name_exchange, i.base_asset, i.quote_asset, i.kind
         FROM instruments i
         JOIN exchanges e ON e.id = i.exchange_id
         WHERE e.name = $1 AND i.name_exchange = $2",
    )
    .bind(exchange_name)
    .bind(name_exchange)
    .fetch_optional(pool)
    .await?;

    Ok(row)
}

/// Get all instruments for a given exchange.
pub async fn list_by_exchange(
    pool: &PgPool,
    exchange_id: i16,
) -> Result<Vec<InstrumentRow>, DalError> {
    let rows = sqlx::query_as::<_, InstrumentRow>(
        "SELECT id, exchange_id, name_exchange, base_asset, quote_asset, kind
         FROM instruments
         WHERE exchange_id = $1
         ORDER BY name_exchange",
    )
    .bind(exchange_id)
    .fetch_all(pool)
    .await?;

    Ok(rows)
}
