use diesel::prelude::*;

#[cfg(any(
    not(hottub_single_provider),
    hottub_provider = "hanime",
    hottub_provider = "hentaihaven",
    hottub_provider = "missav",
    hottub_provider = "perverzija",
))]
pub fn get_video(
    conn: &mut SqliteConnection,
    video_id: String,
) -> Result<Option<String>, diesel::result::Error> {
    use crate::models::DBVideo;
    use crate::schema::videos::dsl::*;
    let result = videos
        .filter(id.eq(video_id))
        .first::<DBVideo>(conn)
        .optional()?;
    match result {
        Some(video) => Ok(Some(video.url)),
        None => Ok(None),
    }
}

#[cfg(any(
    not(hottub_single_provider),
    hottub_provider = "hanime",
    hottub_provider = "hentaihaven",
    hottub_provider = "missav",
    hottub_provider = "perverzija",
))]
pub fn insert_video(
    conn: &mut SqliteConnection,
    new_id: &str,
    new_url: &str,
) -> Result<usize, diesel::result::Error> {
    use crate::models::DBVideo;
    use crate::schema::videos::dsl::*;
    diesel::insert_into(videos)
        .values(DBVideo {
            id: new_id.to_string(),
            url: new_url.to_string(),
        })
        .execute(conn)
}

// Replace any existing rows for `new_id` with a single fresh row. The `videos`
// table is created without a UNIQUE/PRIMARY KEY constraint, so a plain insert
// would append duplicates and `get_video` (which reads the first match) would
// keep returning the stalest copy. Delete-then-insert in a transaction keeps a
// single, up-to-date entry per id so background refreshes actually take effect.
#[cfg(any(
    not(hottub_single_provider),
    hottub_provider = "hanime",
    hottub_provider = "hentaihaven",
    hottub_provider = "missav",
    hottub_provider = "perverzija",
))]
pub fn upsert_video(
    conn: &mut SqliteConnection,
    new_id: &str,
    new_url: &str,
) -> Result<usize, diesel::result::Error> {
    use crate::models::DBVideo;
    use crate::schema::videos::dsl::*;
    conn.transaction(|conn| {
        diesel::delete(videos.filter(id.eq(new_id))).execute(conn)?;
        diesel::insert_into(videos)
            .values(DBVideo {
                id: new_id.to_string(),
                url: new_url.to_string(),
            })
            .execute(conn)
    })
}

#[cfg(any(
    not(hottub_single_provider),
    hottub_provider = "hanime",
    hottub_provider = "hentaihaven",
    hottub_provider = "missav",
    hottub_provider = "perverzija",
))]
pub fn delete_video(
    conn: &mut SqliteConnection,
    video_id: String,
) -> Result<usize, diesel::result::Error> {
    use crate::schema::videos::dsl::*;
    diesel::delete(videos.filter(id.eq(video_id))).execute(conn)
}

pub fn has_table(
    conn: &mut SqliteConnection,
    table_name: &str,
) -> Result<bool, diesel::result::Error> {
    use diesel::sql_query;
    use diesel::sql_types::Text;
    #[derive(QueryableByName)]
    struct TableName {
        #[diesel(sql_type = Text)]
        #[diesel(column_name = name)]
        name: String,
    }

    let query = "SELECT name FROM sqlite_master WHERE type='table' AND name = ?1";
    let rows = sql_query(query)
        .bind::<Text, _>(table_name)
        .load::<TableName>(conn)?;
    let exists = rows.first().map(|r| !r.name.is_empty()).unwrap_or(false);
    Ok(exists)
}

pub fn create_table(
    conn: &mut SqliteConnection,
    create_sql: &str,
) -> Result<(), diesel::result::Error> {
    use diesel::sql_query;
    sql_query(create_sql).execute(conn)?;
    Ok(())
}
