use crate::ldb::{construct_path, hash, DBConn};
use log::{error, warn};
use lyphedb::{KVList, KeyDirectory, LDBNatsMessage, LypheDBCommand, PropagationStrategy};

pub const INCOMINGSTORE: &str = "incomingstore";
pub const OUTGOINGSTORE: &str = "outgoingstore";

pub async fn a_linksto_b(db: &DBConn, a: &str, b: &str) -> Result<(), ()> {
    let key_sets = vec![
        (
            construct_path(&[OUTGOINGSTORE, &hash(a)])
                .as_bytes()
                .to_vec(),
            a.as_bytes().to_vec(),
        ),
        (
            construct_path(&[OUTGOINGSTORE, &hash(a), &hash(b)])
                .as_bytes()
                .to_vec(),
            b.as_bytes().to_vec(),
        ),
        (
            construct_path(&[INCOMINGSTORE, &hash(b)])
                .as_bytes()
                .to_vec(),
            b.as_bytes().to_vec(),
        ),
        (
            construct_path(&[INCOMINGSTORE, &hash(b), &hash(a)])
                .as_bytes()
                .to_vec(),
            a.as_bytes().to_vec(),
        ),
    ];
    let cmd = LDBNatsMessage::Command(LypheDBCommand::SetKeys(
        KVList { kvs: key_sets },
        PropagationStrategy::OnRead,
    ));

    match db.query(cmd).await {
        LDBNatsMessage::Success => Ok(()),
        LDBNatsMessage::BadRequest => {
            error!("bad request for a_linksto_b");
            Err(())
        }
        LDBNatsMessage::NotFound => {
            error!("not found for a_linksto_b");
            Err(())
        }

        _ => {
            warn!("lyphedb sent weird message as response, treating as error");
            Err(())
        }
    }
}

pub async fn what_links_to_this(db: &DBConn, url: &str) -> Result<Vec<String>, ()> {
    let path = construct_path(&[INCOMINGSTORE, &hash(url)])
        .as_bytes()
        .to_vec();
    let cmd = LDBNatsMessage::Command(LypheDBCommand::GetKeyDirectory(KeyDirectory { key: path }));

    match db.query(cmd).await {
        LDBNatsMessage::Entries(kvs) => Ok(kvs
            .kvs
            .into_iter()
            .map(|v| String::from_utf8_lossy(&v.1).to_string())
            .collect()),
        LDBNatsMessage::Success => {
            warn!("lyphedb responded with \"success\" to what_links_to_this, treating as error");
            Err(())
        }
        LDBNatsMessage::BadRequest => {
            warn!("bad request for what_links_to_this");
            Err(())
        }
        LDBNatsMessage::NotFound => Ok(vec![]),
        _ => {
            warn!("lyphedb sent weird message as response, treating as error");
            Err(())
        }
    }
}

pub async fn what_links_from_this(db: &DBConn, url: &str) -> Result<Vec<String>, ()> {
    let path = construct_path(&[OUTGOINGSTORE, &hash(url)])
        .as_bytes()
        .to_vec();
    let cmd = LDBNatsMessage::Command(LypheDBCommand::GetKeyDirectory(KeyDirectory { key: path }));

    match db.query(cmd).await {
        LDBNatsMessage::Entries(kvs) => Ok(kvs
            .kvs
            .into_iter()
            .map(|v| String::from_utf8_lossy(&v.1).to_string())
            .collect()),
        LDBNatsMessage::Success => {
            warn!("lyphedb responded with \"success\" to what_links_from_this, treating as error");
            Err(())
        }
        LDBNatsMessage::BadRequest => {
            warn!("bad request for what_links_from_this");
            Err(())
        }
        LDBNatsMessage::NotFound => Ok(vec![]),
        _ => {
            warn!("lyphedb sent weird message as response, treating as error");
            Err(())
        }
    }
}
