use crate::chaindef::OutPointHash;
use crate::store::db_decode;
use crate::store::db_encode;
use crate::store::Row;
use anyhow::Context;
use anyhow::Result;
use bitcoincash::hashes::Hash;
use bitcoincash::Txid;

use super::DBRow;

const INPUTINDEX_CF: &str = "input";

#[derive(serde::Serialize, serde::Deserialize)]
pub struct InputIndexKey {
    pub outpointhash: [u8; 32],
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct InputIndexRow {
    key: InputIndexKey,
    txid: [u8; 32],
    index: u32,
}

fn deserialize_value(value: &[u8]) -> Result<([u8; 32], u32)> {
    db_decode(value).context("failed to deserialize InputIndexRow value")
}

impl InputIndexRow {
    pub fn new(outpointhash: [u8; 32], txid: Txid, input_index: u32) -> InputIndexRow {
        InputIndexRow {
            key: InputIndexKey { outpointhash },
            txid: txid.into_inner(),
            index: input_index,
        }
    }

    pub fn filter_by_outpointhash(outpointhash: &OutPointHash) -> Vec<u8> {
        db_encode(&InputIndexKey {
            outpointhash: outpointhash.into_inner(),
        })
        .unwrap()
    }

    pub fn txid(&self) -> Txid {
        Txid::from_inner(self.txid)
    }

    pub fn index(&self) -> u32 {
        self.index
    }

    pub fn parse_values(values: &[u8]) -> Result<([u8; 32], u32)> {
        deserialize_value(values)
    }
}

impl DBRow for InputIndexRow {
    const CF: &'static str = INPUTINDEX_CF;
    fn to_row(&self) -> Row {
        Row {
            key: db_encode(&self.key).unwrap().into_boxed_slice(),
            value: db_encode(&(self.txid, self.index))
                .unwrap()
                .into_boxed_slice(),
        }
    }

    fn from_row(row: &Row) -> InputIndexRow {
        let (txid, index): ([u8; 32], u32) =
            deserialize_value(&row.value).expect("failed to parse InputIndexRow");
        InputIndexRow {
            key: db_decode(&row.key).expect("failed to parse InputIndexRow key"),
            txid,
            index,
        }
    }
}
