use std::ops::DerefMut;

use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};

use super::{logbook::Logbook, user::User};

#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct Rower {
    pub logbook_id: i64,
    pub rower_id: i64,
}

impl Rower {
    pub async fn for_log(db: &SqlitePool, log: &Logbook) -> Vec<User> {
        sqlx::query_as!(
            User,
            "
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id 
FROM user
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
        ",
            log.id
        )
        .fetch_all(db)
        .await
        .unwrap()
    }

    pub async fn create(
        db: &mut Transaction<'_, Sqlite>,
        logbook_id: i64,
        rower_id: i64,
    ) -> Result<(), String> {
        //TODO: Check if rower is allowed to row

        sqlx::query!(
            "INSERT INTO rower(logbook_id, rower_id) VALUES (?,?);",
            logbook_id,
            rower_id
        )
        .execute(db.deref_mut())
        .await
        .map_err(|e| e.to_string())?;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use sqlx::SqlitePool;

    use super::Logbook;
    use crate::model::{rower::Rower, user::User};
    use crate::testdb;

    #[sqlx::test]
    fn test_for_log() {
        let pool = testdb!();

        let logbook = Logbook::find_by_id(&pool, 3).await.unwrap();
        let rowers = Rower::for_log(&pool, &logbook).await;
        let expected = User::find_by_id(&pool, 3).await.unwrap();
        assert_eq!(rowers, vec![expected]);
    }

    #[sqlx::test]
    fn test_for_log_none() {
        let pool = testdb!();

        let logbook = Logbook::find_by_id(&pool, 2).await.unwrap();
        let rowers = Rower::for_log(&pool, &logbook).await;
        assert_eq!(rowers, vec![]);
    }

    #[sqlx::test]
    fn test_create() {
        let pool = testdb!();

        let logbook = Logbook::find_by_id(&pool, 3).await.unwrap();

        let mut tx = pool.begin().await.unwrap();
        Rower::create(&mut tx, logbook.id, 2).await.unwrap();
        tx.commit().await.unwrap();
        let rowers = Rower::for_log(&pool, &logbook).await;
        assert_eq!(
            rowers,
            vec![
                User::find_by_id(&pool, 2).await.unwrap(),
                User::find_by_id(&pool, 3).await.unwrap()
            ]
        );
    }
}
