use sqlx::{prelude::FromRow, Pool, Postgres};

use crate::AppError;

#[derive(FromRow)]
pub struct Account {
    pub account_id: String,
}

#[derive(FromRow)]
pub struct CaughtFish {
    pub id: Option<i32>,
    pub account_id: String,
    pub fish_id: i32,
}

#[derive(FromRow)]
pub struct PinnedFish {
    pub id: Option<i32>,
    pub account_id: String,
    pub fish_id: i32,
}

pub async fn insert_account(id: &str, pool: &Pool<Postgres>) -> Result<(), AppError> {
    sqlx::query("INSERT INTO accounts (account_id) VALUES ($1)")
        .bind(id)
        .execute(pool)
        .await?;
    Ok(())
}

pub async fn get_account(id: &str, pool: &Pool<Postgres>) -> Result<bool, AppError> {
    let results: Vec<Account> = sqlx::query_as("SELECT * FROM accounts WHERE account_id = $1")
        .bind(id)
        .fetch_all(pool)
        .await?;

    if results.len() == 1 {
        Ok(true)
    } else {
        Ok(false)
    }
}

pub async fn insert_caught_fish(
    acc_id: &str,
    fish_id: &i32,
    pool: &Pool<Postgres>,
) -> Result<(), AppError> {
    sqlx::query(
        "
        INSERT INTO caught_fish (account_id, fish_id) VALUES ($1, $2)
        ",
    )
    .bind(acc_id)
    .bind(fish_id)
    .execute(pool)
    .await?;

    Ok(())
}

pub async fn get_caught_fish(acc_id: &str, pool: &Pool<Postgres>) -> Result<Vec<i32>, AppError> {
    let results: Vec<CaughtFish> =
        sqlx::query_as("SELECT * FROM caught_fish WHERE account_id = $1")
            .bind(acc_id)
            .fetch_all(pool)
            .await?;

    Ok(results.iter().map(|cf| cf.fish_id).collect())
}

pub async fn insert_pinned_fish(
    acc_id: &str,
    fish_id: &i32,
    pool: &Pool<Postgres>,
) -> Result<(), AppError> {
    sqlx::query(
        "
        INSERT INTO pinned_fish (account_id, fish_id) VALUES ($1, $2)
        ",
    )
    .bind(acc_id)
    .bind(fish_id)
    .execute(pool)
    .await?;

    Ok(())
}

pub async fn get_pinned_fish(acc_id: &str, pool: &Pool<Postgres>) -> Result<Vec<i32>, AppError> {
    let results: Vec<PinnedFish> =
        sqlx::query_as("SELECT * FROM pinned_fish WHERE account_id = $1")
            .bind(acc_id)
            .fetch_all(pool)
            .await?;

    Ok(results.iter().map(|cf| cf.fish_id).collect())
}

pub async fn delete_pinned_fish(
    acc_id: &str,
    fish_id: &i32,
    pool: &Pool<Postgres>,
) -> Result<(), AppError> {
    sqlx::query("DELETE FROM pinned_fish WHERE account_id = $1 AND fish_id = $2")
        .bind(acc_id)
        .bind(fish_id)
        .fetch_all(pool)
        .await?;

    Ok(())
}
